1import importlib
2import inspect
3import re
4
5class BridgeKeeper:
6 def __init__(self, module_name):
7 self.module_name = module_name
8
9 def __enter__(self):
10 module = importlib.import_module(self.module_name)
11 return ModuleProxy(module)
12
13 def __exit__(self, exc_type, exc_val, exc_tb):
14 return False
15
16class ModuleProxy:
17 def __init__(self, module):
18 self.module = module
19
20 def __getattr__(self, name):
21 if not hasattr(self.module, name):
22 raise AttributeError(name)
23
24 obj = getattr(self.module, name)
25
26 # Question 1
27 if not NameValidator.is_valid(obj):
28 raise AttributeError(name)
29
30 #Question 2
31 if callable(obj) and not CallableValidator.is_valid(obj):
32 raise AttributeError(name)
33
34 # Question 3
35 if not self._has_valid_attribute(obj):
36 raise AttributeError(name)
37
38 return obj
39
40 def _has_valid_attribute(self, obj):
41 for attr in dir(obj):
42 if AttributeValidator.is_valid(attr):
43 return True
44
45 return False
46
47
48class NameValidator:
49 @staticmethod
50 def is_valid(obj):
51 if not hasattr(obj, "__name__"):
52 return False
53 name = obj.__name__
54 return bool(name) and name[0].isupper()
55
56
57class AttributeValidator:
58 @staticmethod
59 def is_valid(name):
60 return (AttributeValidator._not_dunder(name) and
61 AttributeValidator._no_vowel_overflow(name) and
62 AttributeValidator._last_letter_is_upper(name))
63
64 @staticmethod
65 def _not_dunder(name):
66 return re.fullmatch(r"__.*__", name) is None
67
68 @staticmethod
69 def _no_vowel_overflow(name):
70 return re.search(r"[aeiouAEIOU]{4,}", name) is None
71
72 @staticmethod
73 def _last_letter_is_upper(name):
74 return re.fullmatch(r".*[A-Z][^a-zA-Z]*", name) is not None
75
76
77class CallableValidator:
78 @staticmethod
79 def is_valid(func):
80 try:
81 sig = inspect.signature(func)
82 except Exception:
83 return False
84
85 doc = inspect.getdoc(func)
86 expected = CallableValidator._parse_doc(doc)
87
88 if expected is None:
89 return len(sig.parameters) == 0
90
91 if list(sig.parameters.keys()) != [p[0] for p in expected]:
92 return False
93
94 try:
95 args = [CallableValidator._dummy_value(t) for _, t in expected]
96 sig.bind(*args)
97 except Exception:
98 return False
99
100 return True
101
102 @staticmethod
103 def _parse_doc(doc):
104 if not doc:
105 return None
106
107 match = re.search(r"Parameters\s*-+\s*(.*?)(\n\s*\n|$)", doc, re.DOTALL)
108
109 if not match:
110 return None
111
112 block = match.group(1)
113
114 params = []
115 for line in block.splitlines():
116 m = re.match(r"\s*(\w+)\s*:\s*(.+)", line)
117 if m:
118 name = m.group(1)
119 type_str = m.group(2).strip()
120 params.append((name, type_str))
121
122 return params
123
124 @staticmethod
125 def _dummy_value(type_str):
126 type_str = type_str.strip()
127
128 # basic types
129 if type_str == "int":
130 return 1
131 if type_str == "float":
132 return 1.0
133 if type_str == "str":
134 return "a"
135 if type_str == "bool":
136 return True
137
138 # union A | B
139 if "|" in type_str:
140 return CallableValidator._dummy_value(type_str.split("|")[0])
141
142 # list[T]
143 if type_str.startswith("list["):
144 inner = type_str[5:-1]
145 return [CallableValidator._dummy_value(inner)]
146
147 # tuple[T]
148 if type_str.startswith("tuple["):
149 inner = type_str[6:-1]
150 return (CallableValidator._dummy_value(inner),)
151
152 # set[T]
153 if type_str.startswith("set["):
154 inner = type_str[4:-1]
155 return {CallableValidator._dummy_value(inner)}
156
157 # dict[K,V]
158 if type_str.startswith("dict["):
159 inner = type_str[5:-1]
160 k_t, v_t = map(str.strip, inner.split(","))
161 return {
162 CallableValidator._dummy_value(k_t):
163 CallableValidator._dummy_value(v_t)
164 }
165
166 return None
..............F..F.F.F.FF
======================================================================
FAIL: test_rejects_dict_when_callable_does_not_work_with_documented_key_and_value_types (test.TestBridgeKeeper.test_rejects_dict_when_callable_does_not_work_with_documented_key_and_value_types)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 90, in test_rejects_dict_when_callable_does_not_work_with_documented_key_and_value_types
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised
======================================================================
FAIL: test_rejects_list_when_callable_does_not_work_with_documented_element_type (test.TestBridgeKeeper.test_rejects_list_when_callable_does_not_work_with_documented_element_type)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 102, in test_rejects_list_when_callable_does_not_work_with_documented_element_type
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised
======================================================================
FAIL: test_rejects_non_callable_object (test.TestBridgeKeeper.test_rejects_non_callable_object)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 27, in test_rejects_non_callable_object
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised
======================================================================
FAIL: test_rejects_set_when_callable_does_not_work_with_documented_element_type (test.TestBridgeKeeper.test_rejects_set_when_callable_does_not_work_with_documented_element_type)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 112, in test_rejects_set_when_callable_does_not_work_with_documented_element_type
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised
======================================================================
FAIL: test_rejects_tuple_when_callable_does_not_work_with_documented_element_type (test.TestBridgeKeeper.test_rejects_tuple_when_callable_does_not_work_with_documented_element_type)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 107, in test_rejects_tuple_when_callable_does_not_work_with_documented_element_type
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised
======================================================================
FAIL: test_rejects_union_when_callable_does_not_support_all_union_branches (test.TestBridgeKeeper.test_rejects_union_when_callable_does_not_support_all_union_branches)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 72, in test_rejects_union_when_callable_does_not_support_all_union_branches
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised
----------------------------------------------------------------------
Ran 25 tests in 0.004s
FAILED (failures=6)
27.04.2026 17:59
27.04.2026 18:01