functools module
Monty implements a small subset of functools.
The implemented callables match CPython 3.14 for arguments, values, repr() and error messages, apart from the notes
below.
reduce(function, iterable, /[, initial]), partial(func, /, *args, **keywords),
lru_cache(maxsize=128, typed=False), and cache(user_function).
Everything else: cached_property, wraps, update_wrapper, partialmethod, total_ordering,
singledispatch, singledispatchmethod, cmp_to_key, get_cache_token, WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES.
functools.Placeholder, added in 3.14 to skip a positional slot when binding (partial(f, Placeholder, 2)), is also
absent, so every bound positional fills a leading slot.
These names are absent from the module namespace rather than stubbed, so they are rejected at type-check time
(Module 'functools' has no member 'wraps') and raise AttributeError at runtime.
-
Native iterator adaptors used by
reduce()retain their synchronous callback restrictions. -
Calling a
partialcharges the native re-entry budget. A partial stored as a class attribute binds as a bound method whose__func__is another partial, so a chain of them nests on the interpreter’s own call stack without pushing a Python frame. Monty bounds that chain at the fixed native re-entry depth (see resource_limits.md), raisingRecursionError: maximum recursion depth exceededbeyond roughly a dozen levels; CPython runs such a chain into the thousands before its own C stack gives out. Ordinary use is unaffected — nestedpartial(partial(f, 1), 2)is flattened at construction, and a partial passed tomap()orsorted(key=)costs one level. -
partialobjects have no__dict__. CPython allows arbitrary attributes on one (p.x = 1), which Monty rejects withAttributeError: 'functools.partial' object has no attribute 'x' and no __dict__ for setting new attributes. Assigning tofunc,argsorkeywordsfails in both, but CPython words itAttributeError: readonly attribute. -
argsandkeywordsare rebuilt on each access.p.args is p.argsisFalse, where CPython returns the same objects every time. Mutating the dict fromp.keywordstherefore has no effect on what the partial passes on; CPython returns the live dict, so mutating it changes later calls. -
partialobjects carry no dunder attributes.p.__call__andp.__doc__both raiseAttributeError, where CPython has a method-wrapper and the type’s docstring respectively. -
A
partialcrossing the host boundary marshals as itsrepr. Python and JavaScript hosts receiveMontyObject::repr("functools.partial(...)")rather than a callable, since neither side can call back into a value that only exists inside the sandbox.
- Cached functions have no
__dict__and do not copy the wrapped function’s metadata (__name__,__qualname__,__module__,__doc__, or annotations).__wrapped__is readable but not writable. cache_info(),cache_clear(), andcache_parameters()work as calls, but retrieving those methods as values raisesAttributeError, as with other native methods. Explicit__call__and__get__attribute access is also unsupported.lru_cache(maxsize=...)returns a configured wrapper rather than CPython’s decorating closure, so its type andrepr()differ. The module does not expose CPython’s private_lru_cache_wrapperconstructor or_CacheInfoclass.- Integer
maxsizevalues outside the signed 64-bit range are rejected withTypeError. CPython accepts arbitrarily negative sizes as zero and raisesOverflowErrorwhen decorating with an oversized positive size.cache_parameters()reports normalized integer/boolean settings rather than the original argument objects. - Eviction removes an entry by position without running user hashing or equality methods. CPython can compare colliding keys during eviction, so comparison side effects can differ. The number of hash and equality calls during lookup and insertion can also differ.
- An unhashable argument is diagnosed by hashing arguments again after hashing the complete key fails.
A user
__hash__can therefore run twice on a failing call. For nested unhashable values, the error can name the enclosing tuple instead of its unhashable element. - Applying a caching decorator to a method with
@syntax is rejected along with other method decorators. Assigningmethod = functools.cache(function)in the class body is supported. - A cached function crossing the host boundary is represented by its
repr, not a host-callable function.