Mini Library Walkthrough#
This walkthrough shows a complete, small integration using package-style digesters
and library-level defaults in _argdigest.py, covering both axes: what each function
accepts, and what each argument’s value must be.
Project layout#
mylib/
__init__.py
_argdigest.py
basic.py
_private/
digestion/
argument/
__init__.py
selection.py
syntax.py
1. Define library defaults#
# mylib/_argdigest.py
DIGESTION_SOURCE = "mylib._private.argdigest.argument"
DIGESTION_STYLE = "package"
STRICTNESS = "warn"
SKIP_PARAM = "skip_digestion"
2. Define digesters#
# mylib/_private/argdigest/argument/selection.py
def digest_selection(selection, syntax="MolSysMT", caller=None):
if selection is None:
return "all"
if isinstance(selection, str):
return selection
if syntax == "MolSysMT" and isinstance(selection, int):
return [selection]
raise ValueError(f"Invalid selection in {caller}: {selection!r}")
# mylib/_private/argdigest/argument/syntax.py
def digest_syntax(syntax, caller=None):
if syntax is None:
return "MolSysMT"
if isinstance(syntax, str):
return syntax
raise ValueError(f"Invalid syntax in {caller}: {syntax!r}")
3. Decorate API functions#
# mylib/basic.py
from argdigest import arg_digest
@arg_digest(config="mylib._argdigest")
def get(molecular_system, selection=None, syntax=None, skip_digestion=False):
return molecular_system, selection, syntax
4. Declare what each function accepts#
get above has a closed signature, so it is already held to its own parameters: calling
it with selectionn="all" raises UnknownArgumentError suggesting selection, and you
declared nothing to get that.
A function taking **kwargs is the case that needs declaring, because ArgDigest cannot
guess what it meant:
# mylib/_private/argdigest/domain/attribute.py
from argdigest import Domain
ATTRIBUTES = ("n_atoms", "n_bonds", "coordinates")
domain = Domain(name="attribute", contains=lambda k: k in ATTRIBUTES,
members=lambda: ATTRIBUTES)
# mylib/_private/argdigest/function/get_attributes.py
from argdigest import FunctionContract
contract = FunctionContract(
caller="mylib.basic.get_attributes",
admits="attribute",
requires_any_of="attribute", # asking for nothing would be meaningless
)
Then add to _argdigest.py:
FUNCTION_SOURCE = "mylib._private.argdigest.function"
DOMAIN_SOURCE = "mylib._private.argdigest.domain"
UNKNOWN_ARGUMENT = "error"
In a real library the domain should point at whatever already defines those names, rather than repeating them, so the two cannot drift apart.
5. Add one pipeline rule (optional)#
from argdigest import register_pipeline
@register_pipeline(kind="selection", name="selection.non_empty")
def selection_non_empty(value, ctx):
if value == "":
raise ValueError("selection cannot be empty")
return value
Then attach it in @arg_digest(map={...}) where needed.
Expected result#
selectionandsyntaxare digested before function logic executes.The integration remains localized in digestion modules, not spread across API code.
Additional rules can be layered via pipelines without changing digesters.
Next#
Continue with Configuration.