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]) and partial(func, /, *args, **keywords).
Everything else: cache, lru_cache, 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 'lru_cache') and raise AttributeError at runtime.
reduce()cannot call a host function. The reduction function runs through the same synchronous path asmap()’s, which cannot suspend the VM. An external function raisesNotImplementedError: reduce(): external function 'f' is not yet supported in this context; one that touches the filesystem raises the same error naming the OS function it maps to, e.g.reduce(): OS function 'Path.iterdir' is not yet supported in this contextforos.listdir(). This covers apartialthat wraps one. Calling apartialanywhere else is unaffected, since that goes through the ordinary call path.- 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.type(...).__name__is the dotted name.type(functools.partial(f)).__name__is'functools.partial', where CPython reports the bare'partial'. This is Monty’s general treatment of types whose CPythontp_nameis dotted (re.Patternanditertools.countbehave the same way);str(type(p))matches CPython’s"<class 'functools.partial'>", as do error messages naming the type.partialobjects carry no dunder attributes.p.__call__andp.__doc__both raiseAttributeError, where CPython has a method-wrapper and the type’s docstring respectively.partial[int]is not subscriptable at runtime. CPython returns atypes.GenericAlias; Monty raisesTypeError: 'type' object is not subscriptable, as it does forlist[int]— there are no runtime generic aliases at all (see typing.md). The type checker accepts the expression, so this is one of the few divergences the stubs cannot reject up front. Annotations are unaffected, Monty stringizing them rather than evaluating them.- 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.
partial is a descriptor, as it is in CPython 3.14: one stored as a class attribute binds the instance as the next
argument after those it already carries.