Interact with arguments passed to a function by their parameter name or position
Project description
Argbox
When defining Python decorators we sometimes want to interact with the inputted arguments by their respective parameter name or position in the function's signature. This is where argbox comes in handy:
import argbox
def decorator(func):
def wrapper(*args, **kwargs):
ctx = argbox.Context(func, args, kwargs) # (1)!
param_0_arg = ctx.get_arg(position=0) # (2)!
param_y_arg = ctx.get_arg(name="y") # (3)!
print(f"Parameter 0={param_0_arg} y={param_y_arg}")
return func(*ctx.args, **ctx.kwargs)
return wrapper
@decorator
def func(x, y):
pass
This decorator will work no matter if arguments are inputted as positional or keyword arguments; and no matter the ordering of the keyword arguments:
>>> func(1, 2)
Parameter 0=1 y=2
>>> func(1, y=2)
Parameter 0=1 y=2
>>> func(x=1, y=2)
Parameter 0=1 y=2
>>> func(y=2, x=1)
Parameter 0=1 y=2
Example 1
Say we want a decorator that validates the types of the inputted arguments before running the wrapped function. Without argbox we can make a simple version, but which will only work when arguments are passed as positional arguments:
def check_types(*types):
def wrapper(func):
def wrapped(*args, **kwargs):
if len(args) < len(types):
raise ValueError(f"Expected at least {len(types)} positional arguments, got {len(args)}")
for i, type_ in enumerate(types):
if not isinstance(args[i], type_):
raise TypeError(f"Argument {i} is not of type {type_}")
return func(*args, **kwargs)
return wrapped
return wrapper
@check_types(str, int)
def add(x, y):
return x + str(y)
>>> add("hello", 1)
hello1
>>> add("hello", y=1)
# raise ValueError
>>> add(x="hello", y=1)
# raises ValueError
>>> add(y=1, x="hello")
# raises ValueError
With argbox we can make a version that works with both positional and keyword arguments; also no matter the ordering of the keyword arguments:
from argbox import Context
def check_types(*types):
def wrapper(func):
def wrapped(*args, **kwargs):
ctx = Context(func, args, kwargs)
for i, type_ in enumerate(types):
# get argument at parameter position i
arg = ctx.get_arg(position=i)
if not isinstance(arg, type_):
raise TypeError(f"Argument {i} is not of type {type_}")
return func(*args, **kwargs)
return wrapped
return wrapper
@check_types(int, str)
def add(x, y):
return str(x) + y
>>> add("hello", 1)
hello1
>>> add("hello", y=1)
hello1
>>> add(x="hello", y=1)
hello1
>>> add(y=1, x="hello")
hello1
Example 2
Say we want a decorator that validates if the specified backend exists before running the wrapped function. Without argbox we can make a simple version, but which will only work when the backend parameter is passed as a keyword argument:
def check_backend_exists(func):
def wrapped(*args, **kwargs):
ctx = Context(func, args, kwargs)
if not "backend" in kwargs:
raise ValueError("Parameter `backend` must be specified as a keyword argument")
backend = kwargs["backend"]
if backend == "numpy":
if importlib.util.find_spec("numpy") is None:
raise ImportError("Backend 'numpy' is not installed")
return func(*ctx.args, **ctx.kwargs)
return wrapped
@check_backend_exists
def random_array(size: int, backend="python"):
if backend == "numpy":
import numpy as np
return np.random.rand(size)
else:
return [random.random() for _ in range(size)]
>>> random_array(3, backend="numpy")
array([0.23532788, 0.880924 , 0.77030806])
>>> random_array(3, "numpy")
# raises ValueError
With argbox we can make a version, which works with backend both as a positional and keyword argument:
def check_backend_exists(func):
def wrapped(*args, **kwargs):
ctx = Context(func, args, kwargs)
backend = ctx.get_arg(name="backend")
if backend == "numpy":
if importlib.util.find_spec("numpy") is None:
raise ImportError("Backend 'numpy' is not installed")
return func(*ctx.args, **ctx.kwargs)
return wrapped
@check_backend_exists
def random_array(size: int, backend="python"):
if backend == "numpy":
import numpy as np
return np.random.rand(size)
else:
return [random.random() for _ in range(size)]
>>> random_array(3, backend="numpy")
array([0.49507765, 0.99207232, 0.26754601])
>>> random_array(3, "numpy")
array([0.99186762, 0.96713066, 0.58288705])
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 argbox-0.3.1.tar.gz.
File metadata
- Download URL: argbox-0.3.1.tar.gz
- Upload date:
- Size: 7.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c9066edb43f69bd5608958d359ee80e845f0259debc9bc739fdcbffcf5345f7
|
|
| MD5 |
e84b008d553cc720209cd3a24395ee25
|
|
| BLAKE2b-256 |
f8385030f6de23b6305a4450571ac15ecf0ed61b8eff176fc4e1a1394fcf8519
|
File details
Details for the file argbox-0.3.1-py3-none-any.whl.
File metadata
- Download URL: argbox-0.3.1-py3-none-any.whl
- Upload date:
- Size: 7.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e132fcc7d80f330627e1152d33963bbe0e98043981791b402997f627674a3d9
|
|
| MD5 |
57d2b6bdc123ae405a6e47413cefea95
|
|
| BLAKE2b-256 |
8a1e34de6dfe66c42c9b1301260e02defa5a526289f933681e605ef96b136633
|