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.1.tar.gz (3.6 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.1-py3-none-any.whl (3.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: argbox-0.2.1.tar.gz
  • Upload date:
  • Size: 3.6 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.1.tar.gz
Algorithm Hash digest
SHA256 b7df0de90ae10600c29886dd8d7dec6c352054b1aeb287ff7d13a28b5f3c443e
MD5 8beb864fdad5943bf0c1f4f26252aaac
BLAKE2b-256 00dbc73152e9c3ab5620962ac5d2e4fcb33e50d9c63964181138b1a5564b270e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: argbox-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 3.9 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d89bcdc93dac4aa9df85f32a5fe9aaec0ddd6d23d7d6b9dfff993c84147def29
MD5 8e9acc0366a59ae2f9ed08f188c122cf
BLAKE2b-256 ff94f9e11cd6b924a396f85767252dfd36900a9aba1b7201ea808b21525eaede

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