Lightweight Python toolkit: validators, colors, decorators, and FlexVar
Project description
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
pip install twilkit
Supports Python 3.10+
Quick Start
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)
API
Validators
Reusable data descriptors that enforce constraints on attributes when you set them. Import them via the grouped namespace or directly:
from twilkit import validators
# or:
from twilkit import StartWith, EndsWith, MoreThan, LessThan, InBetween
StartWith(*prefixes: str)
Validate that a string starts with any of the provided prefixes.
class Person:
title = validators.StartWith("Mr ", "Ms ", "Dr ")
p = Person()
p.title = "Dr Alice" # OK
# p.title = "Alice" # raises ValidationError
EndsWith(*suffixes: str)
Validate that a string ends with any of the provided suffixes.
class Account:
email = validators.EndsWith("@example.com", "@corp.local")
a = Account()
a.email = "dev@corp.local" # OK
# a.email = "dev@gmail.com" # raises ValidationError
MoreThan(value: int | float)
Validate that a number is strictly greater than value.
class Metrics:
height_cm = validators.MoreThan(0)
m = Metrics()
m.height_cm = 172 # OK
# m.height_cm = 0 # raises ValidationError
LessThan(value: int | float)
Validate that a number is strictly less than value.
class Bio:
age = validators.LessThan(150)
b = Bio()
b.age = 42 # OK
# b.age = 200 # raises ValidationError
InBetween(minv: int | float, maxv: int | float)
Validate that minv <= value <= maxv.
class Exam:
score = validators.InBetween(0, 100)
e = Exam()
e.score = 88 # OK
# e.score = -5 # raises ValidationError
All validators raise
twilkit.ValidationErrorwith 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
-
Colorsenum → raw escape codes -
Cprintclass → underlying helper
from twilkit import color, Colors
print(color("Success").green)
print(color("Warning").yellow)
print(f"{Colors.RED.value}Error{Colors.RESET.value}")
Decorators
@catch_exceptions
Catch any exception, print a colored error (<func> <error>), return None.
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
@log_function
Log function start, arguments, return values, and exceptions to <func_name>.log.
from twilkit import log_function
@log_function
def compute_total(prices):
return sum(prices)
compute_total([10, 20, 30]) # Logs to compute_total.log
FlexVar
A small, chainable dict-like container with a pretty string output.
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, _)→KeyErrorif attribute exists.update(name, _)/.remove(name)→KeyErrorif missing.__getattr__(name)→AttributeErrorif missing.__getitem__/.__delitem__→KeyErrorif missing
Mini Project: User Registry CLI
Combining validators, colors, decorators, and FlexVar.
# 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
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.
Extra Tools (PyTxt, Return, Copy helpers)
New in 1.1.0 – Utility helpers under
twilkit.extra_toolsand re-exported at the top level.
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=Trueblocks writes and raisesPermissionErrorwhen setting.text.- Assigning
.file = <path>auto-creates aTextFileusing basename/dirname. - Deleting
del p.filepulls content back into memory and removes the file on disk.
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().
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 theif __name__ == '__main__':block in the copy.
A convenience printing wrapper is also available:
from twilkit import copy_me_print
copy_me_print(path="backups", new_name="final.py", keep_main=False)
These helpers rely on ADVfile_manager for safe file operations. Install with extras:
pip install twilkit[extra_tools].
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file twilkit-1.1.1.tar.gz.
File metadata
- Download URL: twilkit-1.1.1.tar.gz
- Upload date:
- Size: 20.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54b411121dac46859ba35ef667e885645644476b01fe799bae25882fb188caf5
|
|
| MD5 |
5860be8704b21d149f6eaa7afcf68baa
|
|
| BLAKE2b-256 |
438b0982f789e7e6adb169c1e9b890f3c82ce6481a3d09d1f66cb051863b78a1
|
File details
Details for the file twilkit-1.1.1-py3-none-any.whl.
File metadata
- Download URL: twilkit-1.1.1-py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91381e57604f48ddd830e3195a77a6f0007d764b7aa40439d081105c7b09e3cb
|
|
| MD5 |
e49a5ac437ffa0fcd3bdb7399c55e05f
|
|
| BLAKE2b-256 |
e75abd03dd4eb1d3b18ed0d1a5ec626204712271c0d5581e798f95359bf987f8
|