Digesting function arguments into clear, reliable contracts.#
Install it#
conda install -c uibcdf argdigest
Use it#
ArgDigest helps you normalize and validate input arguments at your API boundary, without forcing a single internal architecture.
from argdigest import arg_digest
@arg_digest(
digestion_source="mylib._private.argdigest.argument",
digestion_style="package",
strictness="warn",
)
def get(molecular_system, selection="all", syntax="MolSysMT"):
return molecular_system, selection, syntax
What happens here:
ArgDigest resolves digesters for each argument and applies them before your logic runs.
If an argument cannot be digested, behavior follows your
strictnesspolicy.Optional diagnostics can be emitted through SMonitor.
Two axes#
That example covers one half of the problem: given an argument name, is its value valid and in canonical form? A library also needs the other half: may this function receive this argument at all, and does it have what it needs?
Axis |
You declare |
|---|---|
The function argument contract |
a |
The argument value contract |
one digester per argument name |
Without the first, a mistyped keyword is silently discarded, the call runs with the default, and you get back a plausible wrong answer. ArgDigest refuses it instead:
UnknownArgumentError: 'mylib.basic.get.get' does not accept the argument 'n_atomss'.
Did you mean 'n_atoms'?
A closed signature needs no declaration: it is held to its own parameters, because
ArgDigest must never end up more permissive than Python, which already raises
TypeError for an unexpected keyword. A function taking **kwargs opened its door on
purpose, so it declares the domain those keywords come from:
# mylib/_private/argdigest/domain/attribute.py
from argdigest import Domain
from mylib.attribute import attributes, is_attribute
domain = Domain(name='attribute', contains=is_attribute,
members=lambda: tuple(attributes))
# mylib/_private/argdigest/function/get.py
from argdigest import FunctionContract
contract = FunctionContract(caller='mylib.basic.get.get', admits='attribute')
Pointing the domain at your library’s own catalogue, rather than copying names into a
list, is what keeps the two from drifting apart. describe_contract then renders the
whole thing as data, which is how the real domain of a **kwargs function becomes
readable — something inspect.signature cannot show.
Note
unknown_argument defaults to error from version 0.10.0. Set
UNKNOWN_ARGUMENT = "warn" or "ignore" in your configuration module during a
migration.