monty-macros
Procedural macros used by the monty crate.
Not a public crate — consumers get the macros re-exported
(monty::args::FromArgs / monty::args::ToArgs) and should not depend on
monty-macros directly.
#[derive(FromArgs)]
Generates the from_args body for a Rust-implemented Python function. The
macro is deliberately thin: it emits a static ParamSpec describing the
signature and one call to the runtime binder
(crates/monty/src/args/bind_native.rs), which owns all positional/kwarg
dispatch, arity checks, duplicate detection, and reference-count cleanup.
The generated code then converts each raw slot via FromValue in
declaration order and applies defaults.
use FromArgs;
use Value;
let DatetimeArgs =
from_args?;
Fields must appear in Python signature order:
[pos_only…] [pos_or_keyword…] [varargs] [kw_only…] [varkwargs], with
required fields before optional ones in each region. Field types must
implement FromValue (impls live in monty::args::from_value). Coercion
failures are structured (FromValueFail): wrong-type failures get their
wording from the extraction site (bad_arg/bad_arg_named, or the impl's
type_error), while value-level failures (ValueError, OverflowError)
surface unchanged. str arguments the function only reads should use
StrArg, which validates without copying the text and lends &str via
as_str(vm).
style — pick the CPython parser family
The style attribute names the argument parser the target function uses in
CPython. It selects both the error wording and the error ordering
(binding errors vs conversion errors — see ErrorFamily in
crates/monty/src/args/bind_native.rs for the exact contracts). Pick it by
looking at how the function is implemented in CPython:
| CPython implementation | style |
tell-tale error wording |
|---|---|---|
pure-Python def (re module, json.dumps, Path.mkdir) |
def |
f() takes from 1 to 2 positional arguments but 3 were given |
Argument Clinic — most modern builtins/methods (look for clinic blocks / *.c.h includes in the CPython source) |
clinic (the default — omit it) |
replace() takes at least 2 positional arguments (1 given) |
PyArg_ParseTupleAndKeywords, no :name in the format string |
c |
function missing required argument 'day' (pos 3) |
PyArg_ParseTupleAndKeywords with :name |
c_named |
timezone() missing required argument 'offset' (pos 1) |
PyArg_UnpackTuple (positional-only, fixed min..max) |
unpack |
name expected at most 2 arguments, got 3 (expected N … when min == max) |
Style-derived behaviour that used to be separate flags: the C
"… positional arguments …" overflow pivot turns on automatically for
style = c and style = c_named structs with kw_only fields (CPython's
vgetargskeywords / _PyArg_UnpackKeywords behave the same way; the named
variant additionally says takes exactly N positional argument(s) when every
positional param is required, e.g. os.stat), and unpack collapses to the
exact-arity expected N argument(s) wording when no positional field has a
default.
Since CPython's def binding never type-checks, style = def structs
should declare fields as raw Value (or StrArg-in-body) and coerce in the
function body, so type errors carry the message the CPython function body
would produce. bad_arg is rejected under style = def for the same
reason. varargs is allowed: a *args signature never overflows, but a
missing required parameter still wants the def wording.
style = unpack models PyArg_UnpackTuple's fixed positional min..max
range, so the derive rejects anything outside that shape: every positional
field must be pos_only, and varargs, varkwargs, and at_most_total
are all incompatible.
Modifiers
at_most_total— pre-count positionals + kwargs against all named parameters, including keyword-only parameters, before dispatch ({name}() takes at most N arguments (M given)). This is a per-function empirical fact, not derivable from the fields or the style. Litmus test: call the CPython function with valid positionals plus one bogus kwarg — if it reportstakes at most N arguments (M given), set the flag; if it reportsunexpected keyword argument, don't. Only meaningful for the C-parser families (clinic/c/c_named) on signatures with a fixed maximum — rejected understyle = def/style = unpackand withvarargs/varkwargs.vectorcall— kwarg-free calls check positional arity first with_PyArg_CheckPositionalwording ({name} expected at most N arguments, got M), modelingtp_vectorcallfast paths (int,str) that only fall back to the clinic parser when keywords are present. Requires the defaultclinicstyle plusat_most_total(which supplies the parenthesised wording for the kwargs path).bad_arg/bad_arg_named— reportFromValuewrong-type failures in CPython's_PyArg_BadArgumentwording ({name}() argument {pos|'arg'} must be {expected}, not {got}).kwarg_error_name = "..."— override the function name in the unknown-kwarg error only (json.dumpsreportsJSONEncoder.__init__). Understyle = unpackit instead names the function in the blankettakes no keyword argumentsrejection (unicodedata.name()).kwargs_not_supported_yet— reject every kwarg with aNotImplementedError; a Monty TODO marker.
Field-level attributes: pos_only, kw_only (must carry a default — the
runtime binder's fast paths skip the missing-keyword check, so required
keyword-only params are rejected at derive time), varargs (must be
Vec<Value> — elements are handed over unconverted), varkwargs,
default[ = expr], static_string = "..." (override the StaticStrings
variant used for kwarg matching; neither applies to varargs/varkwargs
fields). All are documented inline in
src/from_args.rs, which also carries #[cfg(test)]
unit tests for every attribute-validation error.
#[derive(ToArgs)]
Projects a struct into the CallArgs a host callback takes, reusing the #[from_args(...)] field attributes.
varargs fields contribute one positional argument per element.
Not a standalone crate
Generated code emits crate::... paths: FromArgs compiles inside monty, and ToArgs inside monty-types.
Cross-crate use would need proc-macro-crate and paths to the appropriate crate.
Monty crates
monty— the core interpreter: Python parser, bytecode VM, and sandbox.monty-types— the shared boundary data types (values, exceptions, OS calls, resource limits) hosts use without linking the interpreter.monty-fs— host-side filesystem mounts: maps virtual sandbox paths to real host directories.monty-runtime— themontybinary: REPL, file runner, and subprocess worker mode.monty-pool— an elastic pool of crash-isolatedmontyworker subprocesses.monty-proto— the protobuf wire protocol spoken between pool parents and workers.monty-type-checking— type checking of sandboxed code, powered by ty.monty-typeshed— the trimmed typeshed stubs describing the stdlib subset Monty implements.monty-macros— the proc macros behindmonty's argument parsing. this crate