Skip to main content

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 via set_stop_on_error(), or temporarily disable within suspended_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 a ValueError when any validation fails.
  • suspended_arg_checks(): Context manager to disable checks within a with block.

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 each token is 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:
    • x must be either exactly 1 or between 3 and 5.
    • y must be exactly 2.
      If omitted, no range or length constraints are applied.
  • 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:

  1. Fork the repository.
  2. Create a feature branch.
  3. Submit a pull request.

License

MIT License

Author

Parth Mittal
Email: parth@privatepanda.co

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

typekeeper-0.0.3b1.tar.gz (10.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typekeeper-0.0.3b1-py3-none-any.whl (8.6 kB view details)

Uploaded Python 3

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

Hashes for typekeeper-0.0.3b1.tar.gz
Algorithm Hash digest
SHA256 8d4f471971aacd1670d27761d6700752e1a59b45f3bb500eddf84ade27ec266d
MD5 3091483e2ca2ea193e7cf46a002c0253
BLAKE2b-256 78e3f7bafdbfecb71bb5c080df32d67c4183b1263ad58b78f91364611e310866

See more details on using hashes here.

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

Hashes for typekeeper-0.0.3b1-py3-none-any.whl
Algorithm Hash digest
SHA256 20ae91da068c22f2ed1d2ad5ef23c0216fc12c2cd302c0d12fd11d4cca462677
MD5 ee602958d297dd0cee9e16878ecd68f5
BLAKE2b-256 04db7625626c3b63ccfbd35a3c0c59d4a386a6cbc755338625cd421c3d9db0b1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page