Skip to main content

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:

def decorator(func):
    def wrapper(*args, **kwargs):
        ctx = Context(func, args, kwargs)
        arg_0 = ctx.get_arg(position=0)
        arg_y = ctx.get_arg(name="y")
        print(f"Parameter at position 0: {arg_0}, Parameter with name 'y': {arg_y}")
        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 at position 0: 1, Parameter with name 'y': 2
>>> func(1, y=2)
Parameter at position 0: 1, Parameter with name 'y': 2
>>> func(x=1, y=2)
Parameter at position 0: 1, Parameter with name 'y': 2
>>> func(y=2, x=1)
Parameter at position 0: 1, Parameter with name '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

argbox-0.2.3.tar.gz (5.1 kB view details)

Uploaded Source

Built Distribution

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

argbox-0.2.3-py3-none-any.whl (5.7 kB view details)

Uploaded Python 3

File details

Details for the file argbox-0.2.3.tar.gz.

File metadata

  • Download URL: argbox-0.2.3.tar.gz
  • Upload date:
  • Size: 5.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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

Hashes for argbox-0.2.3.tar.gz
Algorithm Hash digest
SHA256 c14db34a9da9b5aeb786d1660733c3a9adb8ec5caea1df93ee252176fe1d2a1c
MD5 569769838c786db0eba75031c1717847
BLAKE2b-256 590ffa2024ac9721c445a09a70ea4ea27c8a25fdd96a1d0d52c16b9726c19e66

See more details on using hashes here.

File details

Details for the file argbox-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: argbox-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 5.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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

Hashes for argbox-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 565961df36e9f6bc54878a8b198fb7e0effa012d39685c9479f2ab40c09a0208
MD5 e7cce0e46844a1c8798a2d29c38f8b0a
BLAKE2b-256 4935567d71bea8375c1147f39cf58d34a6fe050ce452af42b280bb4f90d0bafc

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