Registering Specs to Methods

Methods can be made to remember their arg specs. This can be quite handy when dynamically generating front-end code, for instance.

Registering with decorators

Methods decorated with the arg_spec decorator by default remember their specs. This allows us to retrieve them whenever we like, directly from the method itself. Take the following method:

amodule.py:

from canonical_args import arg_spec

@arg_spec(
        {
                "args": [
                        {
                                "name": "arg1",
                                "type": int,
                                "values": None
                        },
                        {
                                "name": "arg2",
                                "type": float,
                                "values": None
                        }
                ],
                "kwargs": {
                        "kwarg1": {
                                "type": str,
                                "values": None
                        }
                }
        }
)
def some_function(*args, **kwargs):
        return args, kwargs

We can now import this module from the python environment, and call up its Spec like so.

>>> import amodule
>>> amodule.some_function.get_spec()
{"args": [{"name": "arg1", "type": int, "values": None},
                  {"name": "arg2", "type": float, "values": None}],
 "kwargs": {"kwarg1": {"type": str, "values": None}}}

By doing such, we can even dynamically generate a front-end for in HTML or any other format for that matter, with proper type and value checking in Javascript.

Registering without decorators

Although decorators are quite useful, what if we do not control the source code containing the method to be decorated? Quite simply, we can use the canonical_args module to ensure a method remembers its spec:

from canonical_args import function, checkspec
# this is a third-party method we don't control
from thirdparty_module import amethod

function.register_spec(
        amethod,
        {"args": [{"name": "arg1", "type": int, "values": None}]})

def passthrough(*args, **kwargs):
        check_args(amethod.get_spec(), *args, **kwargs)
        return amethod(*args, **kwargs)

Thusly, we create a passthrough method, which automatically checks the arguments for us.

Note

It is possible to force a method call to always call check_args upon execution by overriding its __call__ instance method. When working with code you do not control, however, this could cause some seriously whacky side-effects. So probably just don’t do that.