1import re
2from itertools import product
3
4def dummy_var(attr_type: str):
5 if attr_type == "int":
6 return 1
7 if attr_type == "float":
8 return 1.
9 if attr_type == "str":
10 return "1"
11 if attr_type == "bool":
12 return True
13 if attr_type.startswith("list"):
14 return [dummy_var(attr_type[5:-1])]
15 if attr_type.startswith("set"):
16 return {dummy_var(attr_type[4:-1])}
17 if attr_type.startswith("dict"):
18 t1, t2 = attr_type[5:-1].split(",")
19 return {dummy_var(t1.strip()) : dummy_var(t2.strip())}
20 if attr_type.startswith("tuple"):
21 return (dummy_var(attr_type[6:-1]), dummy_var(attr_type[6:-1]))
22
23
24class FilteredModule:
25 def __init__(self, module):
26 self._module = module
27
28 def __getattr__(self, name):
29 attr = getattr(self._module, name)
30
31 attr_name = getattr(attr, "__name__", None)
32 if not attr_name or not attr_name[0].isupper():
33 raise AttributeError
34
35 if not callable(attr):
36 raise AttributeError
37
38 docstr = attr.__doc__ if attr.__doc__ else ""
39 matched = re.search(r"Parameters\n----------(.*?)(?:$|\n\n)", docstr, re.DOTALL)
40 params = matched.group(1) if matched else ""
41 param_types = re.findall(r"^[^:]+\s*:\s*([^\n]+)",params, re.MULTILINE)
42 param_types_combinations = [c for c in product(*[[x.strip() for x in t.split("|")] for t in param_types])]
43 param_values = [tuple(dummy_var(t) for t in combo) for combo in param_types_combinations]
44
45 for args in param_values:
46 try:
47 attr(*args)
48 except Exception:
49 raise AttributeError
50
51 for attr_name in dir(attr):
52
53 if attr_name.startswith("__") and attr_name.endswith("__"):
54 continue
55
56 if re.search(r"[aeiou]{4,}", attr_name.lower()):
57 continue
58
59 last_letter = re.search(r"[a-zA-Z][^a-zA-Z]*$", attr_name)
60 if not last_letter or not last_letter.group()[0].isupper():
61 continue
62
63 return attr
64
65 raise AttributeError
66
67
68class BridgeKeeper:
69 def __init__(self, module_name):
70 self._module_name = module_name
71
72 def __enter__(self):
73 self._module = __import__(self._module_name)
74 return FilteredModule(self._module)
75
76 def __exit__(self, ex_type, exc, tb):
77 pass
............F............
======================================================================
FAIL: test_rejects_callable_when_parameter_names_do_not_match (test.TestBridgeKeeper.test_rejects_callable_when_parameter_names_do_not_match)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 59, in test_rejects_callable_when_parameter_names_do_not_match
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised
----------------------------------------------------------------------
Ran 25 tests in 0.002s
FAILED (failures=1)
Виктор Бечев
30.04.2026 13:34Отвъд дребните забележки и един изпуснат интервал - добре написано решение.
|
30.04.2026 13:27
30.04.2026 13:28