An extensive drop-in argument validator.
Project description
typekeeper
⚠️⚠️⚠️️ 3b1 is a beta release with a host of new not-fully-tested new features.
Overview
typekeeper is a lightweight Python library that injects comprehensive runtime validation into your functions with a single decorator. It automatically:
- Verifies that arguments conform to their type annotations (including nested generics).
- Warns when default values don’t match annotations or use mutable defaults.
- Enforces numeric ranges and sequence-length constraints via an easy spec string.
- Captures both the function definition and call site in all warnings for precise diagnostics.
Simply decorate your function with @validate_args(...) to gain these safeguards during development, without changing your function signature.
Features
- Type validation: Recursively checks simple and generic types (
List[int],Dict[str, float],Optional[...],Union[...],Callable, etc.). - Default‐value checks: Detects mismatches between annotated types and default values, plus the classic mutable‐default gotcha.
- Range & length specs: Specify numeric or length constraints in one string (e.g. "x=1-5; tags=2-4"). Use
:to traverse nested dictionaries and sequences (escape literal colons with\:). The wildcard*selects all items, and a trailing:implies*. Paths only follow keys or indexes, not object attributes. Later entries override earlier ones. - Context‐aware warnings: All warnings include file paths and line numbers of both the decorator and the call site.
- Global control: Enable or disable checks with
set_arg_checks(), stop after the first error viaset_stop_on_error(), or temporarily disable withinsuspended_arg_checks(). - Recursive _validate() Invocation: Recursively calls _validate() on variable if defined. Gives metadata of location of object in recursion path which fails _validate().
Installation
pip install typekeeper
Configuration
Global Toggle
Argument checks are controlled by an internal flag; direct access is not required. Use the provided APIs:
set_arg_checks(enabled: bool): Turn all checks on or off globally.set_stop_on_error(stop: bool): Raise aValueErrorwhen any validation fails.suspended_arg_checks(): Context manager to disable checks within awithblock.
API Reference
validate_args
def validate_args(*, lengths: Optional[str] = None, ignore_defaults: bool = False) -> Callable
- lengths: Optional specification string for constraining numeric values or sequence lengths. It uses a semicolon-separated list of parameter specifications in the form:
name=token1,token2,...
where eachtokenis either:- A single number
N, enforcing an exact value (for numeric arguments) or exact length (for sequences). - A range
min-max, enforcing inclusive bounds (both endpoints inclusive). Nested fields can be referenced with:separators, e.g.users:tables:headers=100. For example:
lengths="x=1,3-5; y=2"
enforces:xmust be either exactly1or between3and5.ymust be exactly2.
If omitted, no range or length constraints are applied.
- A single number
- ignore_defaults: Skip warnings for mutable default arguments if set to
True.
set_arg_checks
def set_arg_checks(enabled: bool) -> None
Globally enable or disable all argument validation.
set_stop_on_error
def set_stop_on_error(stop: bool) -> None
Raise a ValueError whenever a validation warning would be emitted.
suspended_arg_checks
@contextmanager
def suspended_arg_checks() -> None
Temporarily suspend argument checks within a with block.
Examples
Full scripts for these examples live in the examples/ directory.
# Example 1: Basic type‐and‐default validation
from typekeeper import validate_args
@validate_args()
def greet(name: str, times: int = 1):
return " ".join([name] * times)
# Valid call:
greet("Alice") # → "Alice"
# Invalid call (wrong type):
greet(42)
# UserWarning: Arg 'name' to '<function greet at 0x7a8a6f3163e0>' mismatches <class 'str'>; expected <class 'str'>, got <class 'int'>
# Example 2: Numeric‐range and length‐range constraints
from typekeeper import validate_args
@validate_args(lengths="x=1-3; data=2-4")
def process(x: int, data: list[int]):
return data * x
# Valid:
process(2, [10, 20, 30])
# Invalid x:
process(0, [1, 2])
# UserWarning: Value for 'x'=0 not in ranges [(1.0, 3.0)]
# Invalid data length:
process(2, [1])
# UserWarning: Length of 'data'=1 not in ranges [(2.0, 4.0)]
# Example 2b: Nested path constraints
from typekeeper import validate_args
@validate_args(lengths="users:tables:headers=100")
def check(users: list[dict]):
return users
check([{"tables": [{"headers": 100}]}])
check([{"tables": [{"headers": 5}]}])
# UserWarning: Value for 'users:tables:headers'=5 not in ranges [(100.0, 100.0)]
# Using a trailing colon applies the spec to every element under that key:
@validate_args(lengths="a:=1") # same as "a:*=1"
def foo(a: dict[str, int]):
return a
foo({"x": 1, "y": 1})
foo({"x": 1, "y": 2})
# UserWarning: Value for 'a:'=2 not in ranges [(1.0, 1.0)]
# Example 2c: Overriding wildcards
from typekeeper import validate_args
@validate_args(lengths="var::=2; var::a=1")
def merge(var: dict[str, dict[str, int]]):
return var
merge({"x": {"a": 1}, "y": {"b": 2}})
merge({"x": {"a": 2}, "y": {"b": 2}})
# UserWarning: Value for 'var::a'=2 not in ranges [(1.0, 1.0)]
# Example 3: Mutable‐default detection
from typekeeper import validate_args
@validate_args(ignore_defaults=False)
def append_item(items: list[int] = []):
items.append(1)
return items
# Decoration‐time warning:
# UserWarning: Mutable default for 'items' in '<function append_item at fn>' : []
# Example 4: Custom _validate() on nested items
from typekeeper import validate_args
class Thing:
def __init__(self, v: int):
self.v = v
def _validate(self) -> bool:
return self.v >= 0
@validate_args()
def handle(things: list[Thing]):
return [t.v for t in things]
# Call with one bad element:
handle([Thing(1), Thing(-5), Thing(3)])
# UserWarning: Recursed value of parameter 'things' at depth [1] failed _validate in '<function handle at ...>'
Contributing
Contributions are welcome! To contribute:
- Fork the repository.
- Create a feature branch.
- Submit a pull request.
License
MIT License
Author
Parth Mittal
Email: parth@privatepanda.co
Project details
Release history Release notifications | RSS feed
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 typekeeper-0.0.3b1.tar.gz.
File metadata
- Download URL: typekeeper-0.0.3b1.tar.gz
- Upload date:
- Size: 10.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d4f471971aacd1670d27761d6700752e1a59b45f3bb500eddf84ade27ec266d
|
|
| MD5 |
3091483e2ca2ea193e7cf46a002c0253
|
|
| BLAKE2b-256 |
78e3f7bafdbfecb71bb5c080df32d67c4183b1263ad58b78f91364611e310866
|
File details
Details for the file typekeeper-0.0.3b1-py3-none-any.whl.
File metadata
- Download URL: typekeeper-0.0.3b1-py3-none-any.whl
- Upload date:
- Size: 8.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20ae91da068c22f2ed1d2ad5ef23c0216fc12c2cd302c0d12fd11d4cca462677
|
|
| MD5 |
ee602958d297dd0cee9e16878ecd68f5
|
|
| BLAKE2b-256 |
04db7625626c3b63ccfbd35a3c0c59d4a386a6cbc755338625cd421c3d9db0b1
|