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.0.tar.gz (3.5 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.0-py3-none-any.whl (3.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: argbox-0.2.0.tar.gz
  • Upload date:
  • Size: 3.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","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.0.tar.gz
Algorithm Hash digest
SHA256 5a1f7c5947a8520bbbce7e5a2bcc8502d86a2c017dabbb6bdcf33ee6b7e00ba7
MD5 59e0ea621ae45cd0520cdd3ae2c7739e
BLAKE2b-256 d871a04f58cbf8908e888a550ae8d92ea7cd2238179f6ee81a44c1250fa34614

See more details on using hashes here.

File details

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

File metadata

  • Download URL: argbox-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 3.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a9bc5ed669917d15911b638a31725fd5afd4d4966bac60d894648d4dbe2f516
MD5 86a16924d2146d07f48813d7b07f8859
BLAKE2b-256 fbcdc1da0f9ee1b68af37650ecdfc5c8e5d15826f2713b7b81a5a5cd4ba4926d

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