Skip to content

Commit a8d0449

Browse files
Commit api design ramblings
1 parent e306432 commit a8d0449

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

2025-11-12/Group-1/decorator2.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from typing import TypedDict
2+
from collections import defaultdict
3+
from collections.abc import Callable
4+
import json
5+
6+
class ExperimentResult(TypedDict):
7+
matches: int
8+
failures: int
9+
10+
class Experiment[**P, T]:
11+
12+
_experiments: dict[str, list[Callable]] = defaultdict(list)
13+
_results: dict[str, dict[str, ExperimentResult]] = defaultdict(dict)
14+
15+
def __init__(self, *, control: str|Callable[P, T]) -> None:
16+
self._control = control if isinstance(control, str) else control.__name__
17+
18+
def __call__(self, function: Callable[P, T]) -> Callable[P, T]:
19+
"""Add all the experimental only functions to the experiments list"""
20+
if self._control == function.__name__:
21+
Experiment._experiments[self._control].append(function)
22+
Experiment._results[self._control][function.__name__] = ExperimentResult(matches=0, failures=0)
23+
24+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
25+
"""When we call the real function, run the experiments too"""
26+
real_result = function(*args, **kwargs)
27+
28+
for experimental_function in Experiment._experiments[self._control]:
29+
experimental_result = experimental_function(*args, **kwargs)
30+
if experimental_result == real_result:
31+
Experiment._results[self._control][experimental_function.__name__]["matches"] += 1
32+
else:
33+
Experiment._results[self._control][experimental_function.__name__]["failures"] += 1
34+
35+
return real_result
36+
return wrapper
37+
38+
class Raysperiment[**P, T]:
39+
def __init__(self, control:Callable[P, T]) -> None:
40+
self._functions = []
41+
self._control = control
42+
self._results: dict[str, ExperimentResult] = defaultdict(lambda:ExperimentResult(matches=0, failures=0))
43+
44+
def register(self, function:Callable[P, T]) -> Callable[P, T]:
45+
self._functions.append(function)
46+
return function
47+
48+
def handle_result(self, function_name: str, good: T, unknown: T) -> None:
49+
if good == unknown:
50+
self._results[function_name]["matches"] += 1
51+
else:
52+
self._results[function_name]["failures"] += 1
53+
54+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
55+
real_result = self._control(*args, **kwargs)
56+
for func in self._functions:
57+
unknown = func(*args, **kwargs)
58+
self.handle_result(func.__name__, real_result, unknown)
59+
return real_result
60+
61+
legit_function = Raysperiment(control=lambda a, b, c:sum([a, b, c]))
62+
63+
@legit_function.register
64+
def bad_refactor(a: int, b: int, c: int) -> int:
65+
print("a + b - c")
66+
return a + b - c
67+
68+
@legit_function.register
69+
def good_refactor(a: int, b: int, c: int) -> int:
70+
print("a + b + c")
71+
return a + b + c
72+
73+
print(legit_function(1, 2, 3))
74+
print(json.dumps(legit_function._results, indent=2))

0 commit comments

Comments
 (0)