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.- 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.