-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunctools.py
More file actions
108 lines (97 loc) · 5.12 KB
/
Copy pathfunctools.py
File metadata and controls
108 lines (97 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
@namespace("functools")
from Promethium import List, ValueError
# A small, opt-in subset of Python's functools module.
#
# `func` is deliberately untyped, matching `DefaultDict`'s factory field —
# there's no confirmed way to type a callable/predicate parameter in this
# codebase (see the note in `itertools.py`). Two-argument `.Invoke(a, b)`
# works on Echoes/Island/Cooper but not Toffee: an untyped parameter erases
# to Objective-C `id` there, whose block-invocation surface doesn't expose
# a matching 2-parameter `invoke` (confirmed: "No overloaded method 'invoke'
# with 2 parameters on type 'id'"). This is the same dynamic-block-
# invocation limitation `DefaultDict.__getitem__` already documents for its
# own (0-argument) factory, handled the same way: raise on Toffee instead
# of silently misbehaving.
def reduce(func, values: List[int], initial: int) -> int:
if defined("TOFFEE"):
raise ValueError("functools.reduce cannot invoke its callable on Toffee yet")
else:
accumulator: int = initial
index: int = 0
while index < len(values):
accumulator = func.Invoke(accumulator, values.__getitem__(index))
index += 1
return accumulator
# `partial`/`cmp_to_key` don't actually need decorators to implement (CPython
# itself defines both as plain classes/factories, only conventionally *used*
# with `@`) — confirmed by direct testing now that decorators work at all.
# `partial` specifically is a plain closure-returning function, never applied
# via `@` syntax.
#
# Scoped to a single bound int argument and a single remaining int argument
# (`int, int -> int` style), matching this file's existing `reduce`
# precedent of concrete overloads over a fully generic/variadic signature —
# real `functools.partial` supports arbitrary arity via `*args`/`**kwargs`,
# which is outside this language slice (see `struct.py`'s equivalent note).
def partial(func, bound_arg: int):
if defined("TOFFEE"):
raise ValueError("functools.partial cannot invoke its callable on Toffee yet")
else:
def wrapper(remaining_arg: int) -> int:
return func.Invoke(bound_arg, remaining_arg)
return wrapper
# `functools.lru_cache`: a genuine, confirmed-working `@lru_cache(maxsize)`
# decorator with real LRU eviction (move-to-end on hit, evict-from-front once
# over maxsize) — verified end-to-end, including that stale entries actually
# get recomputed after eviction.
#
# Real constraint, found while building this (distinct from the wrapper's
# own required `*args`/`**kwargs` shape, already documented for decorators
# generally): the DECORATED function's parameter must be left untyped
# (`def f(x):`, not `def f(x: int):`) — an explicitly-typed parameter on the
# decorated function crashes at call time with `OxygeneBinderException: No
# overload with these parameters` inside `Promethium.Callable.invoke`, even
# though decoration itself succeeds. Isolated by comparing against the
# compiler team's own `PromethiumDecoratorCanary`, whose decorated function
# also takes parameters but leaves them untyped for exactly this reason.
# Reported to the compiler team; this is a real usage constraint until fixed,
# not a bug in this implementation.
#
# Scoped to a single int argument (matching `partial`/`reduce`'s own
# precedent) — real `functools.lru_cache` supports arbitrary arity via
# `*args`/`**kwargs` and hashes the full argument tuple as the cache key.
def lru_cache(maxsize: int):
def decorator(func):
if defined("TOFFEE"):
def toffee_wrapper(*args, **kwargs):
raise ValueError("functools.lru_cache cannot invoke its callable on Toffee yet")
return toffee_wrapper
else:
cache: List[tuple[int, int]] = List[tuple[int, int]]()
def wrapper(*args, **kwargs):
arg: int = Integer(args[0])
index: int = 0
while index < len(cache):
entry: tuple[int, int] = cache.__getitem__(index)
if entry[0] == arg:
cache.pop(index)
cache.append(entry)
return entry[1]
index += 1
result = func(*args, **kwargs)
cache.append((arg, Integer(result)))
if len(cache) > maxsize:
cache.pop(0)
return result
return wrapper
return decorator
# `functools.wraps` is NOT shipped. Its entire purpose in CPython is copying
# introspection metadata (`__name__`, `__doc__`, `__module__`) from the
# original function onto a wrapper, so debugging/introspection tools see the
# wrapper as if it were the original. Promethium functions are compiled
# methods, not dynamic Python function objects with mutable dunder
# attributes — there is no confirmed way to read or set such metadata on a
# function value in this codebase (unlike class/field reflection, which does
# work — see `inspect.py`/`pprint.py`). Not attempted further: this looks
# like an architecture mismatch (nothing to copy), not something blocked by
# the decorator bug above, though not fully investigated.