| Name | twilkit JSON |
| Version |
2.0.0
JSON |
| download |
| home_page | None |
| Summary | Lightweight Python toolkit: validators, colors, decorators, and FlexVar |
| upload_time | 2025-09-14 05:49:25 |
| maintainer | None |
| docs_url | None |
| author | Avi Twil |
| requires_python | >=3.10 |
| license | MIT License
Copyright (c) 2025 Avi Twil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
|
| keywords |
validation
decorators
colors
ansi
toolkit
utilities
|
| VCS |
 |
| bugtrack_url |
|
| requirements |
No requirements were recorded.
|
| Travis-CI |
No Travis.
|
| coveralls test coverage |
No coveralls.
|
# twilkit
A lightweight toolkit for everyday Python work:
* **Validators** – clean, reusable descriptors for attribute validation
* **Colors** – simple ANSI color formatting for terminal output
* **Decorators** – exception handling and per-function logging
* **FlexVar** – a flexible, chainable dict-like container with a pretty `__str__`
---
## Table of Contents
* [Installation](#installation)
* [Quick Start](#quick-start)
* [API](#api)
* [Validators](#validators)
* [Start with](#startwithprefixes-str)
* [End with](#endswithsuffixes-str)
* [In between](#inbetweenminv-int--float-maxv-int--float)
* [Less than](#lessthanvalue-int--float)
* [More than](#morethanvalue-int--float)
* [Colors](#colors)
* [Decorators](#decorators)
* [Catch Execptions](#catch_exceptions)
* [Log Function](#log_function)
* [FlexVar](#flexvar)
* [Extra Tools new features from version 1.1.3](#extra-tools-pytxt-return-copy-helpers)
* [Return](#return)
* [PyTxt](#pytxt)
* [Copy helper function](#copy-helpers)
* [Version 2.0 is here](#-version-200-)
* [ADVfile_manager](#advfile_manager)
* [pyproj_inspector](#pyproj_inspector)
* [Mini Project: “User Registry” CLI](#mini-project-user-registry-cli)
* [Contributing](#contributing)
* [License](#license)
---
## Installation
```bash
pip install twilkit
```
[back to menu](#table-of-contents)
---
> Supports Python 3.10+
---
## Quick Start
```python
from twilkit import validators, color, FlexVar, catch_exceptions, log_function
class User:
name = validators.StartWith("Mr ", "Ms ", "Dr ")
email = validators.EndsWith("@example.com", "@corp.local")
age = validators.InBetween(0, 130)
@catch_exceptions
@log_function
def create_profile(name, email, age):
u = User()
u.name = name
u.email = email
u.age = age
profile = FlexVar("Profile").add("name", name).add("email", email).add("age", age)
print(color("User created").green)
print(profile)
return profile
create_profile("Dr Jane Doe", "jane@example.com", 34)
```
[back to menu](#table-of-contents)
---
## API
### Validators
Reusable data descriptors that enforce constraints on attributes when you set them.
Import them via the grouped namespace or directly:
```python
from twilkit import validators
# or:
from twilkit import StartWith, EndsWith, MoreThan, LessThan, InBetween
```
[back to menu](#table-of-contents)
---
#### `StartWith(*prefixes: str)`
Validate that a string starts with any of the provided prefixes.
```python
class Person:
title = validators.StartWith("Mr ", "Ms ", "Dr ")
p = Person()
p.title = "Dr Alice" # OK
# p.title = "Alice" # raises ValidationError
```
[back to menu](#table-of-contents)
---
#### `EndsWith(*suffixes: str)`
Validate that a string ends with any of the provided suffixes.
```python
class Account:
email = validators.EndsWith("@example.com", "@corp.local")
a = Account()
a.email = "dev@corp.local" # OK
# a.email = "dev@gmail.com" # raises ValidationError
```
[back to menu](#table-of-contents)
---
#### `MoreThan(value: int | float)`
Validate that a number is strictly greater than `value`.
```python
class Metrics:
height_cm = validators.MoreThan(0)
m = Metrics()
m.height_cm = 172 # OK
# m.height_cm = 0 # raises ValidationError
```
[back to menu](#table-of-contents)
---
#### `LessThan(value: int | float)`
Validate that a number is strictly less than `value`.
```python
class Bio:
age = validators.LessThan(150)
b = Bio()
b.age = 42 # OK
# b.age = 200 # raises ValidationError
```
[back to menu](#table-of-contents)
---
#### `InBetween(minv: int | float, maxv: int | float)`
Validate that `minv <= value <= maxv`.
```python
class Exam:
score = validators.InBetween(0, 100)
e = Exam()
e.score = 88 # OK
# e.score = -5 # raises ValidationError
```
[back to menu](#table-of-contents)
---
> All validators raise `twilkit.ValidationError` with a clear, colored message on failure.
---
### Colors
Minimal ANSI color helpers for terminals.
* `color(value)` → wraps the value and provides properties:
* `.red`, `.light_green`, `.green`, `.yellow`, `.blue`, `.light_blue`, `.magenta`, `.cyan`, `.black`, `.purple`, `.orange`
* `Colors` enum → raw escape codes
* `Cprint` class → underlying helper
```python
from twilkit import color, Colors
print(color("Success").green)
print(color("Warning").yellow)
print(f"{Colors.RED.value}Error{Colors.RESET.value}")
```
[back to menu](#table-of-contents)
---
### Decorators
#### `@catch_exceptions`
Catch any exception, print a colored error (`<func> <error>`), return `None`.
```python
from twilkit import catch_exceptions, color
@catch_exceptions
def risky_div(a, b):
return a / b
print(color("Result:").blue, risky_div(6, 3)) # 2.0
risky_div(1, 0) # Prints colored error, returns None
```
[back to menu](#table-of-contents)
---
#### `@log_function`
Log function start, arguments, return values, and exceptions to `<func_name>.log`.
```python
from twilkit import log_function
@log_function
def compute_total(prices):
return sum(prices)
compute_total([10, 20, 30]) # Logs to compute_total.log
```
[back to menu](#table-of-contents)
---
### FlexVar
A small, chainable dict-like container with a pretty string output.
```python
from twilkit import FlexVar
cfg = (
FlexVar("Server Config")
.add("host", "localhost")
.add("port", 8080)
.update("port", 9090)
)
print(cfg["host"]) # "localhost"
print(cfg.port) # "9090"
print(cfg) # Pretty formatted block
"host" in cfg # True
del cfg["port"] # Remove key
for key, val in cfg:
print(key, val)
```
Error behavior:
* `.add(name, _)` → `KeyError` if attribute exists
* `.update(name, _)` / `.remove(name)` → `KeyError` if missing
* `.__getattr__(name)` → `AttributeError` if missing
* `.__getitem__` / `.__delitem__` → `KeyError` if missing
[back to menu](#table-of-contents)
---
## Mini Project: User Registry CLI
Combining validators, colors, decorators, and FlexVar.
```python
# file: user_registry.py
from twilkit import validators, color, log_function, catch_exceptions, FlexVar
class User:
name = validators.StartWith("Mr ", "Ms ", "Dr ")
email = validators.EndsWith("@example.com", "@corp.local")
age = validators.InBetween(0, 130)
def __init__(self, name: str, email: str, age: int):
self.name = name
self.email = email
self.age = age
class Registry:
def __init__(self):
self._db = []
@log_function
@catch_exceptions
def add_user(self, name: str, email: str, age: int):
user = User(name, email, age)
entry = (
FlexVar("User")
.add("name", user.name)
.add("email", user.email)
.add("age", user.age)
)
self._db.append(entry)
print(color("User added").green)
print(entry)
return entry
@log_function
def list_users(self):
if not self._db:
print(color("No users found").yellow)
return []
print(color(f"Total users: {len(self._db)}").cyan)
for i, entry in enumerate(self._db, start=1):
print(color(f"[{i}]").purple)
print(entry)
return list(self._db)
@log_function
@catch_exceptions
def update_email(self, index: int, new_email: str):
entry = self._db[index]
tmp = User(entry.name, new_email, entry.age) # re-validation
entry["email"] = tmp.email
print(color("Email updated").light_green)
print(entry)
return entry
if __name__ == "__main__":
reg = Registry()
reg.add_user("Dr Alice", "alice@example.com", 34)
reg.add_user("Ms Eve", "eve@gmail.com", 29) # invalid -> ValidationError
reg.list_users()
reg.update_email(0, "alice@corp.local")
reg.list_users()
```
This demonstrates:
* **Validation**: descriptors enforce constraints
* **Colors**: feedback messages
* **Logging**: each method logs to its own file
* **FlexVar**: flexible, human-readable data storage
[back to menu](#table-of-contents)
---
## Contributing
* Issues and PRs are welcome.
* Keep scope small, API tidy, docs clear.
* Include tests for new features .
---
## License
This project is licensed under the terms of the [MIT License](LICENSE).
[back to menu](#table-of-contents)
---
## Extra Tools (PyTxt, Return, Copy helpers)
> **New in 1.1.3** – Utility helpers under `twilkit.extra_tools` and re-exported at the top level.
[back to menu](#table-of-contents)
---
### PyTxt
A lightweight text/file wrapper that lets you work with an in-memory buffer or a bound file (via `ADVfile_manager.TextFile`).
from twilkit import PyTxt
p = PyTxt("hello")
p.text # 'hello'
p.file = "backups/example.txt" # binds to a file (created if missing)
p.text = "new content" # writes to disk via ADVfile_manager
Key notes:
- `read_only=True` blocks writes and raises `PermissionError` when setting `.text`.
- Assigning `.file = <path>` auto-creates a `TextFile` using basename/dirname.
- Deleting `del p.file` pulls content back into memory and removes the file on disk.
[back to menu](#table-of-contents)
---
### Return
A tiny “result” object for returning a payload + success/error state.
from twilkit import Return
ok = Return(True, file="out.txt", size=123)
if ok:
print(ok["file"], ok.get("size"))
err = Return.fail("not found", query="*.txt")
if not err:
print("Error:", err.error)
Conveniences: `.ok` (alias for `success`), `.dict()`, `.unwrap(key, default)`, and `.raise_for_error()`.
[back to menu](#table-of-contents)
---
### Copy helpers
Create a counted copy of a Python module with a header and optional removal of the `__main__` block.
from twilkit import copy_this_module
res = copy_this_module("backups", new_name="final.py", include_main=False)
print("Created:", res["file_name"], "at", res["file_path"])
Parameters:
- `new_copy_file_path`: target folder.
- `copy_num`: start index for numbering; if the target exists, numbering auto-increments.
- `new_name`: optional output file name (suffix optional; source suffix is inherited if missing).
- `include_main`: keep or remove the `if __name__ == '__main__':` block in the copy.
> These helpers rely on **ADVfile_manager** for safe file operations. Install with extras: `pip install twilkit[extra_tools]`.
[back to menu](#table-of-contents)
---
# Version 2.0.0
### pyproj_inspector
**Repo:** [https://github.com/avitwil/pyproj_inspector](https://github.com/avitwil/pyproj_inspector)
`pyproj_inspector` ingests either a **single Python file** or a **project directory**, parses all `.py` files, and builds a structured view of your codebase:
- Built-in (stdlib) imports
- External imports (PyPI distributions mapped from import names)
- Internal modules (top-level packages/modules contained in your project)
- A map of `relative_path -> source_code` for every file
- An optional entry script when a single file is analyzed
It also ships with utilities to:
- Materialize the analyzed project into a temporary or target directory
- Create binaries via **PyInstaller** or **Nuitka**
- Generate a ready-to-edit **`pyproject.toml`** for packaging to PyPI
- Build a Debian `.deb` package (when `dpkg-deb` is available)
>Utility Analyze a Python script or project, classify imports, reconstruct sources, and quickly package into distributables.
```python
from twilkit.pyproj_inspector import PythonProject
proj = PythonProject("path/to/app.py")
print(proj.result.builtins) # {'os', 'json', ...}
print(proj.result.external_imports) # {'requests': {'requests'}, ...}
print(proj.result.internal_modules) # {'app'}
print(proj.result.entry_relpath) # 'app.py'
```
[pyproj_inspector](./pyproj_inspector_README.md)
[back to menu](#table-of-contents)
---
### ADVfile_manager
**Repo:** [https://github.com/avitwil/ADVfile\_manager](https://github.com/avitwil/ADVfile_manager)
Unified file abstractions for Python with **safe writes, caching, backups, context managers, and exit-time cleanup** — all under a consistent API for **Text**, **JSON**, **CSV**, **YAML**, **INI**, **TOML**, **XML**, and **Excel** files. Includes **unified search** across formats and **async variants** of all classes.
* `TextFile` – read/write/append; line tools: `lines()`, `read_line()`.
* `JsonFile` – dict/list roots, `append()`, `get_item()`, `items()`.
* `CsvFile` – `DictReader`/`DictWriter`, `read_row()`, `rows()`, column order control.
* `YamlFile` – like `JsonFile` (requires `PyYAML`).
* `IniFile` – INI via `configparser`, dict-like write/append, search by section/key/value.
* `TomlFile` – TOML read/write/append (requires `tomli`/`tomli-w` or `tomllib`).
* `XmlFile` – XML via `xml.etree.ElementTree`, append elements, search by tag/attrs/text.
* `ExcelFile` – Excel via `openpyxl`, header-based rows, multi-sheet support.
The base class `File` adds **backups**, **restore**, **retention helpers**, **human-readable sizes**, **cache control**, a **context manager** that can auto-backup & restore on error, and **exit-time cleanup** for ephemeral backups. Each class implements a **unified `search()`** signature tailored to the format. Async wrappers (`ATextFile`, `AJsonFile`, …) expose `aread/awrite/aappend/asearch` and async context management.
```python
from twilkit.ADVfile_manager import File, TextFile, JsonFile, CsvFile, YamlFile,IniFile, TomlFile, XmlFile, ExcelFile
# Text
txt = TextFile("notes.txt", "data")
txt.write("first line")
txt.append("second line")
print(txt.read_line(2)) # "second line"
for i, line in txt.lines():
print(i, line)
# JSON (dict root)
j = JsonFile("config.json", "data")
j.write({"users": [{"id": 1}]})
j.append({"active": True})
print(j.get_item("active")) # True
# CSV
c = CsvFile("table.csv", "data")
c.write([{"name":"Avi","age":30},{"name":"Dana","age":25}], fieldnames=["name","age"])
c.append({"name":"Noa","age":21})
print(c.read_row(2)) # {"name":"Dana","age":"25"}
for idx, row in c.rows():
print(idx, row)
# YAML
y = YamlFile("config.yaml", "data")
y.write({"app":{"name":"demo"}, "features":["a"]})
y.append({"features":["b"]})
print(y.get_item("app"))
# INI
ini = IniFile("settings.ini", "data")
ini.write({"server": {"host": "127.0.0.1", "port": 8000}})
ini.append({"server": {"debug": "true"}, "auth": {"enabled": "yes"}})
cfg = ini.read()
print(cfg["server"]["host"])
# TOML
toml = TomlFile("config.toml", "data")
toml.write({"app": {"name": "demo"}, "features": {"b": True}})
print(toml.read()["app"]["name"])
toml.append({"features": {"c": 123}})
# XML
xmlf = XmlFile("books.xml", "data")
root = ET.Element("books")
root.append(ET.Element("book", attrib={"id": "1"}))
xmlf.write(root)
# Append another
xmlf.append(ET.Element("book", attrib={"id": "2"}))
print(ET.tostring(xmlf.read(), encoding="unicode"))
# EXEL
xl = ExcelFile("report.xlsx", "data", default_sheet="Sheet1")
xl.write([{"name":"Avi","score":95},{"name":"Dana","score":88}])
xl.append({"name":"Noa","score":92})
rows = xl.read()
print(rows[0]["name"])
```
[README](./ADVfile_manager_README.md)
[Usage Guide](./ADVfile_manager_USAGE.md)
[back to menu](#table-of-contents)
---
Raw data
{
"_id": null,
"home_page": null,
"name": "twilkit",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "validation, decorators, colors, ansi, toolkit, utilities",
"author": "Avi Twil",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/a7/38/7f20dbaafa4cacc99df33296bb816a64eee909345c0bbe68c95695cbb21d/twilkit-2.0.0.tar.gz",
"platform": null,
"description": "\r\n# twilkit\r\n\r\nA lightweight toolkit for everyday Python work:\r\n\r\n* **Validators** \u2013 clean, reusable descriptors for attribute validation\r\n* **Colors** \u2013 simple ANSI color formatting for terminal output\r\n* **Decorators** \u2013 exception handling and per-function logging\r\n* **FlexVar** \u2013 a flexible, chainable dict-like container with a pretty `__str__`\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n* [Installation](#installation)\r\n* [Quick Start](#quick-start)\r\n* [API](#api)\r\n * [Validators](#validators)\r\n * [Start with](#startwithprefixes-str)\r\n * [End with](#endswithsuffixes-str)\r\n * [In between](#inbetweenminv-int--float-maxv-int--float)\r\n * [Less than](#lessthanvalue-int--float)\r\n * [More than](#morethanvalue-int--float)\r\n * [Colors](#colors)\r\n * [Decorators](#decorators)\r\n * [Catch Execptions](#catch_exceptions)\r\n * [Log Function](#log_function)\r\n * [FlexVar](#flexvar)\r\n * [Extra Tools new features from version 1.1.3](#extra-tools-pytxt-return-copy-helpers)\r\n * [Return](#return)\r\n * [PyTxt](#pytxt)\r\n * [Copy helper function](#copy-helpers)\r\n* [Version 2.0 is here](#-version-200-)\r\n * [ADVfile_manager](#advfile_manager)\r\n * [pyproj_inspector](#pyproj_inspector)\r\n* [Mini Project: \u201cUser Registry\u201d CLI](#mini-project-user-registry-cli)\r\n* [Contributing](#contributing)\r\n* [License](#license)\r\n\r\n---\r\n\r\n## Installation\r\n\r\n```bash\r\npip install twilkit\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n> Supports Python 3.10+\r\n\r\n---\r\n\r\n## Quick Start\r\n\r\n```python\r\nfrom twilkit import validators, color, FlexVar, catch_exceptions, log_function\r\n\r\nclass User:\r\n name = validators.StartWith(\"Mr \", \"Ms \", \"Dr \")\r\n email = validators.EndsWith(\"@example.com\", \"@corp.local\")\r\n age = validators.InBetween(0, 130)\r\n\r\n@catch_exceptions\r\n@log_function\r\ndef create_profile(name, email, age):\r\n u = User()\r\n u.name = name\r\n u.email = email\r\n u.age = age\r\n profile = FlexVar(\"Profile\").add(\"name\", name).add(\"email\", email).add(\"age\", age)\r\n print(color(\"User created\").green)\r\n print(profile)\r\n return profile\r\n\r\ncreate_profile(\"Dr Jane Doe\", \"jane@example.com\", 34)\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n## API\r\n\r\n### Validators\r\n\r\nReusable data descriptors that enforce constraints on attributes when you set them.\r\nImport them via the grouped namespace or directly:\r\n\r\n```python\r\nfrom twilkit import validators\r\n# or:\r\nfrom twilkit import StartWith, EndsWith, MoreThan, LessThan, InBetween\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n#### `StartWith(*prefixes: str)`\r\n\r\nValidate that a string starts with any of the provided prefixes.\r\n\r\n```python\r\nclass Person:\r\n title = validators.StartWith(\"Mr \", \"Ms \", \"Dr \")\r\n\r\np = Person()\r\np.title = \"Dr Alice\" # OK\r\n# p.title = \"Alice\" # raises ValidationError\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n#### `EndsWith(*suffixes: str)`\r\n\r\nValidate that a string ends with any of the provided suffixes.\r\n\r\n```python\r\nclass Account:\r\n email = validators.EndsWith(\"@example.com\", \"@corp.local\")\r\n\r\na = Account()\r\na.email = \"dev@corp.local\" # OK\r\n# a.email = \"dev@gmail.com\" # raises ValidationError\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n#### `MoreThan(value: int | float)`\r\n\r\nValidate that a number is strictly greater than `value`.\r\n\r\n```python\r\nclass Metrics:\r\n height_cm = validators.MoreThan(0)\r\n\r\nm = Metrics()\r\nm.height_cm = 172 # OK\r\n# m.height_cm = 0 # raises ValidationError\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n#### `LessThan(value: int | float)`\r\n\r\nValidate that a number is strictly less than `value`.\r\n\r\n```python\r\nclass Bio:\r\n age = validators.LessThan(150)\r\n\r\nb = Bio()\r\nb.age = 42 # OK\r\n# b.age = 200 # raises ValidationError\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n#### `InBetween(minv: int | float, maxv: int | float)`\r\n\r\nValidate that `minv <= value <= maxv`.\r\n\r\n```python\r\nclass Exam:\r\n score = validators.InBetween(0, 100)\r\n\r\ne = Exam()\r\ne.score = 88 # OK\r\n# e.score = -5 # raises ValidationError\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n> All validators raise `twilkit.ValidationError` with a clear, colored message on failure.\r\n\r\n---\r\n\r\n### Colors\r\n\r\nMinimal ANSI color helpers for terminals.\r\n\r\n* `color(value)` \u2192 wraps the value and provides properties:\r\n\r\n * `.red`, `.light_green`, `.green`, `.yellow`, `.blue`, `.light_blue`, `.magenta`, `.cyan`, `.black`, `.purple`, `.orange`\r\n* `Colors` enum \u2192 raw escape codes\r\n* `Cprint` class \u2192 underlying helper\r\n\r\n```python\r\nfrom twilkit import color, Colors\r\n\r\nprint(color(\"Success\").green)\r\nprint(color(\"Warning\").yellow)\r\nprint(f\"{Colors.RED.value}Error{Colors.RESET.value}\")\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n\r\n\r\n### Decorators\r\n\r\n#### `@catch_exceptions`\r\n\r\nCatch any exception, print a colored error (`<func> <error>`), return `None`.\r\n\r\n```python\r\nfrom twilkit import catch_exceptions, color\r\n\r\n@catch_exceptions\r\ndef risky_div(a, b):\r\n return a / b\r\n\r\nprint(color(\"Result:\").blue, risky_div(6, 3)) # 2.0\r\nrisky_div(1, 0) # Prints colored error, returns None\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n#### `@log_function`\r\n\r\nLog function start, arguments, return values, and exceptions to `<func_name>.log`.\r\n\r\n```python\r\nfrom twilkit import log_function\r\n\r\n@log_function\r\ndef compute_total(prices):\r\n return sum(prices)\r\n\r\ncompute_total([10, 20, 30]) # Logs to compute_total.log\r\n```\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n\r\n\r\n### FlexVar\r\n\r\nA small, chainable dict-like container with a pretty string output.\r\n\r\n```python\r\nfrom twilkit import FlexVar\r\n\r\ncfg = (\r\n FlexVar(\"Server Config\")\r\n .add(\"host\", \"localhost\")\r\n .add(\"port\", 8080)\r\n .update(\"port\", 9090)\r\n)\r\n\r\nprint(cfg[\"host\"]) # \"localhost\"\r\nprint(cfg.port) # \"9090\"\r\nprint(cfg) # Pretty formatted block\r\n\r\n\"host\" in cfg # True\r\ndel cfg[\"port\"] # Remove key\r\nfor key, val in cfg:\r\n print(key, val)\r\n```\r\n\r\nError behavior:\r\n\r\n* `.add(name, _)` \u2192 `KeyError` if attribute exists\r\n* `.update(name, _)` / `.remove(name)` \u2192 `KeyError` if missing\r\n* `.__getattr__(name)` \u2192 `AttributeError` if missing\r\n* `.__getitem__` / `.__delitem__` \u2192 `KeyError` if missing\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n\r\n## Mini Project: User Registry CLI\r\n\r\nCombining validators, colors, decorators, and FlexVar.\r\n\r\n```python\r\n# file: user_registry.py\r\nfrom twilkit import validators, color, log_function, catch_exceptions, FlexVar\r\n\r\nclass User:\r\n name = validators.StartWith(\"Mr \", \"Ms \", \"Dr \")\r\n email = validators.EndsWith(\"@example.com\", \"@corp.local\")\r\n age = validators.InBetween(0, 130)\r\n\r\n def __init__(self, name: str, email: str, age: int):\r\n self.name = name\r\n self.email = email\r\n self.age = age\r\n\r\nclass Registry:\r\n def __init__(self):\r\n self._db = []\r\n\r\n @log_function\r\n @catch_exceptions\r\n def add_user(self, name: str, email: str, age: int):\r\n user = User(name, email, age)\r\n entry = (\r\n FlexVar(\"User\")\r\n .add(\"name\", user.name)\r\n .add(\"email\", user.email)\r\n .add(\"age\", user.age)\r\n )\r\n self._db.append(entry)\r\n print(color(\"User added\").green)\r\n print(entry)\r\n return entry\r\n\r\n @log_function\r\n def list_users(self):\r\n if not self._db:\r\n print(color(\"No users found\").yellow)\r\n return []\r\n print(color(f\"Total users: {len(self._db)}\").cyan)\r\n for i, entry in enumerate(self._db, start=1):\r\n print(color(f\"[{i}]\").purple)\r\n print(entry)\r\n return list(self._db)\r\n\r\n @log_function\r\n @catch_exceptions\r\n def update_email(self, index: int, new_email: str):\r\n entry = self._db[index]\r\n tmp = User(entry.name, new_email, entry.age) # re-validation\r\n entry[\"email\"] = tmp.email\r\n print(color(\"Email updated\").light_green)\r\n print(entry)\r\n return entry\r\n\r\nif __name__ == \"__main__\":\r\n reg = Registry()\r\n reg.add_user(\"Dr Alice\", \"alice@example.com\", 34)\r\n reg.add_user(\"Ms Eve\", \"eve@gmail.com\", 29) # invalid -> ValidationError\r\n reg.list_users()\r\n reg.update_email(0, \"alice@corp.local\")\r\n reg.list_users()\r\n```\r\n\r\nThis demonstrates:\r\n\r\n* **Validation**: descriptors enforce constraints\r\n* **Colors**: feedback messages\r\n* **Logging**: each method logs to its own file\r\n* **FlexVar**: flexible, human-readable data storage\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n## Contributing\r\n\r\n* Issues and PRs are welcome.\r\n* Keep scope small, API tidy, docs clear.\r\n* Include tests for new features .\r\n\r\n---\r\n\r\n## License\r\n\r\nThis project is licensed under the terms of the [MIT License](LICENSE).\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n## Extra Tools (PyTxt, Return, Copy helpers)\r\n\r\n> **New in 1.1.3** \u2013 Utility helpers under `twilkit.extra_tools` and re-exported at the top level.\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n### PyTxt\r\nA lightweight text/file wrapper that lets you work with an in-memory buffer or a bound file (via `ADVfile_manager.TextFile`).\r\n\r\n from twilkit import PyTxt\r\n p = PyTxt(\"hello\")\r\n p.text # 'hello'\r\n p.file = \"backups/example.txt\" # binds to a file (created if missing)\r\n p.text = \"new content\" # writes to disk via ADVfile_manager\r\n\r\nKey notes:\r\n- `read_only=True` blocks writes and raises `PermissionError` when setting `.text`.\r\n- Assigning `.file = <path>` auto-creates a `TextFile` using basename/dirname.\r\n- Deleting `del p.file` pulls content back into memory and removes the file on disk.\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n### Return\r\nA tiny \u201cresult\u201d object for returning a payload + success/error state.\r\n\r\n from twilkit import Return\r\n ok = Return(True, file=\"out.txt\", size=123)\r\n if ok:\r\n print(ok[\"file\"], ok.get(\"size\"))\r\n\r\n err = Return.fail(\"not found\", query=\"*.txt\")\r\n if not err:\r\n print(\"Error:\", err.error)\r\n\r\nConveniences: `.ok` (alias for `success`), `.dict()`, `.unwrap(key, default)`, and `.raise_for_error()`.\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n### Copy helpers\r\nCreate a counted copy of a Python module with a header and optional removal of the `__main__` block.\r\n\r\n from twilkit import copy_this_module\r\n res = copy_this_module(\"backups\", new_name=\"final.py\", include_main=False)\r\n print(\"Created:\", res[\"file_name\"], \"at\", res[\"file_path\"])\r\n\r\n\r\n\r\nParameters:\r\n- `new_copy_file_path`: target folder.\r\n- `copy_num`: start index for numbering; if the target exists, numbering auto-increments.\r\n- `new_name`: optional output file name (suffix optional; source suffix is inherited if missing).\r\n- `include_main`: keep or remove the `if __name__ == '__main__':` block in the copy.\r\n\r\n\r\n\r\n> These helpers rely on **ADVfile_manager** for safe file operations. Install with extras: `pip install twilkit[extra_tools]`.\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n# Version 2.0.0 \r\n\r\n\r\n### pyproj_inspector\r\n**Repo:** [https://github.com/avitwil/pyproj_inspector](https://github.com/avitwil/pyproj_inspector)\r\n\r\n`pyproj_inspector` ingests either a **single Python file** or a **project directory**, parses all `.py` files, and builds a structured view of your codebase:\r\n\r\n- Built-in (stdlib) imports\r\n- External imports (PyPI distributions mapped from import names)\r\n- Internal modules (top-level packages/modules contained in your project)\r\n- A map of `relative_path -> source_code` for every file\r\n- An optional entry script when a single file is analyzed\r\n\r\nIt also ships with utilities to:\r\n\r\n- Materialize the analyzed project into a temporary or target directory\r\n- Create binaries via **PyInstaller** or **Nuitka**\r\n- Generate a ready-to-edit **`pyproject.toml`** for packaging to PyPI\r\n- Build a Debian `.deb` package (when `dpkg-deb` is available)\r\n\r\n>Utility Analyze a Python script or project, classify imports, reconstruct sources, and quickly package into distributables.\r\n```python\r\nfrom twilkit.pyproj_inspector import PythonProject\r\n\r\nproj = PythonProject(\"path/to/app.py\")\r\nprint(proj.result.builtins) # {'os', 'json', ...}\r\nprint(proj.result.external_imports) # {'requests': {'requests'}, ...}\r\nprint(proj.result.internal_modules) # {'app'}\r\nprint(proj.result.entry_relpath) # 'app.py'\r\n\r\n```\r\n[pyproj_inspector](./pyproj_inspector_README.md)\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n\r\n### ADVfile_manager\r\n**Repo:** [https://github.com/avitwil/ADVfile\\_manager](https://github.com/avitwil/ADVfile_manager)\r\n\r\nUnified file abstractions for Python with **safe writes, caching, backups, context managers, and exit-time cleanup** \u2014 all under a consistent API for **Text**, **JSON**, **CSV**, **YAML**, **INI**, **TOML**, **XML**, and **Excel** files. Includes **unified search** across formats and **async variants** of all classes.\r\n\r\n* `TextFile` \u2013 read/write/append; line tools: `lines()`, `read_line()`.\r\n* `JsonFile` \u2013 dict/list roots, `append()`, `get_item()`, `items()`.\r\n* `CsvFile` \u2013 `DictReader`/`DictWriter`, `read_row()`, `rows()`, column order control.\r\n* `YamlFile` \u2013 like `JsonFile` (requires `PyYAML`).\r\n* `IniFile` \u2013 INI via `configparser`, dict-like write/append, search by section/key/value.\r\n* `TomlFile` \u2013 TOML read/write/append (requires `tomli`/`tomli-w` or `tomllib`).\r\n* `XmlFile` \u2013 XML via `xml.etree.ElementTree`, append elements, search by tag/attrs/text.\r\n* `ExcelFile` \u2013 Excel via `openpyxl`, header-based rows, multi-sheet support.\r\n\r\nThe base class `File` adds **backups**, **restore**, **retention helpers**, **human-readable sizes**, **cache control**, a **context manager** that can auto-backup & restore on error, and **exit-time cleanup** for ephemeral backups. Each class implements a **unified `search()`** signature tailored to the format. Async wrappers (`ATextFile`, `AJsonFile`, \u2026) expose `aread/awrite/aappend/asearch` and async context management.\r\n\r\n\r\n```python\r\nfrom twilkit.ADVfile_manager import File, TextFile, JsonFile, CsvFile, YamlFile,IniFile, TomlFile, XmlFile, ExcelFile\r\n# Text\r\ntxt = TextFile(\"notes.txt\", \"data\")\r\ntxt.write(\"first line\")\r\ntxt.append(\"second line\")\r\nprint(txt.read_line(2)) # \"second line\"\r\nfor i, line in txt.lines():\r\n print(i, line)\r\n\r\n# JSON (dict root)\r\nj = JsonFile(\"config.json\", \"data\")\r\nj.write({\"users\": [{\"id\": 1}]})\r\nj.append({\"active\": True})\r\nprint(j.get_item(\"active\")) # True\r\n\r\n# CSV\r\nc = CsvFile(\"table.csv\", \"data\")\r\nc.write([{\"name\":\"Avi\",\"age\":30},{\"name\":\"Dana\",\"age\":25}], fieldnames=[\"name\",\"age\"])\r\nc.append({\"name\":\"Noa\",\"age\":21})\r\nprint(c.read_row(2)) # {\"name\":\"Dana\",\"age\":\"25\"}\r\nfor idx, row in c.rows():\r\n print(idx, row)\r\n\r\n# YAML\r\ny = YamlFile(\"config.yaml\", \"data\")\r\ny.write({\"app\":{\"name\":\"demo\"}, \"features\":[\"a\"]})\r\ny.append({\"features\":[\"b\"]})\r\nprint(y.get_item(\"app\"))\r\n\r\n# INI\r\nini = IniFile(\"settings.ini\", \"data\")\r\nini.write({\"server\": {\"host\": \"127.0.0.1\", \"port\": 8000}})\r\nini.append({\"server\": {\"debug\": \"true\"}, \"auth\": {\"enabled\": \"yes\"}})\r\ncfg = ini.read()\r\nprint(cfg[\"server\"][\"host\"])\r\n\r\n# TOML\r\ntoml = TomlFile(\"config.toml\", \"data\")\r\ntoml.write({\"app\": {\"name\": \"demo\"}, \"features\": {\"b\": True}})\r\nprint(toml.read()[\"app\"][\"name\"])\r\ntoml.append({\"features\": {\"c\": 123}})\r\n\r\n# XML\r\n\r\nxmlf = XmlFile(\"books.xml\", \"data\")\r\nroot = ET.Element(\"books\")\r\nroot.append(ET.Element(\"book\", attrib={\"id\": \"1\"}))\r\nxmlf.write(root)\r\n\r\n# Append another\r\nxmlf.append(ET.Element(\"book\", attrib={\"id\": \"2\"}))\r\nprint(ET.tostring(xmlf.read(), encoding=\"unicode\"))\r\n\r\n# EXEL\r\n\r\nxl = ExcelFile(\"report.xlsx\", \"data\", default_sheet=\"Sheet1\")\r\nxl.write([{\"name\":\"Avi\",\"score\":95},{\"name\":\"Dana\",\"score\":88}])\r\nxl.append({\"name\":\"Noa\",\"score\":92})\r\nrows = xl.read()\r\nprint(rows[0][\"name\"])\r\n\r\n\r\n\r\n```\r\n[README](./ADVfile_manager_README.md)\r\n\r\n[Usage Guide](./ADVfile_manager_USAGE.md)\r\n\r\n[back to menu](#table-of-contents)\r\n\r\n\r\n---\r\n\r\n\r\n",
"bugtrack_url": null,
"license": "MIT License\r\n \r\n Copyright (c) 2025 Avi Twil\r\n \r\n Permission is hereby granted, free of charge, to any person obtaining a copy\r\n of this software and associated documentation files (the \"Software\"), to deal\r\n in the Software without restriction, including without limitation the rights\r\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n copies of the Software, and to permit persons to whom the Software is\r\n furnished to do so, subject to the following conditions:\r\n \r\n The above copyright notice and this permission notice shall be included in all\r\n copies or substantial portions of the Software.\r\n \r\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n SOFTWARE.\r\n ",
"summary": "Lightweight Python toolkit: validators, colors, decorators, and FlexVar",
"version": "2.0.0",
"project_urls": {
"Homepage": "https://github.com/avitwil/twilkit",
"Issues": "https://github.com/avitwil/twilkit/issues",
"Repository": "https://github.com/avitwil/twilkit"
},
"split_keywords": [
"validation",
" decorators",
" colors",
" ansi",
" toolkit",
" utilities"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "9856b5b9055054d0e41bde82c9162343af4d02ca94e9e46b678460edee3836fd",
"md5": "ea635d33e85297c0ff81e0b01d7f8e77",
"sha256": "87a28b29ae202252eb713ed75755796eb5bd8d35477321f78542a76232101ae0"
},
"downloads": -1,
"filename": "twilkit-2.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "ea635d33e85297c0ff81e0b01d7f8e77",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 46769,
"upload_time": "2025-09-14T05:49:24",
"upload_time_iso_8601": "2025-09-14T05:49:24.722630Z",
"url": "https://files.pythonhosted.org/packages/98/56/b5b9055054d0e41bde82c9162343af4d02ca94e9e46b678460edee3836fd/twilkit-2.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "a7387f20dbaafa4cacc99df33296bb816a64eee909345c0bbe68c95695cbb21d",
"md5": "8fde7b53a5aba35384236c22ca7cc7ca",
"sha256": "47e90058fd614f3213959a1dbda7a2067f868f33c4154029e119082c168968c6"
},
"downloads": -1,
"filename": "twilkit-2.0.0.tar.gz",
"has_sig": false,
"md5_digest": "8fde7b53a5aba35384236c22ca7cc7ca",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 48885,
"upload_time": "2025-09-14T05:49:25",
"upload_time_iso_8601": "2025-09-14T05:49:25.919775Z",
"url": "https://files.pythonhosted.org/packages/a7/38/7f20dbaafa4cacc99df33296bb816a64eee909345c0bbe68c95695cbb21d/twilkit-2.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-14 05:49:25",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "avitwil",
"github_project": "twilkit",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "twilkit"
}