1import importlib
2import re
3
4class BridgeKeeper:
5 def __init__(self, module_name):
6 self.module_name = module_name
7 self.module = None
8
9 def __enter__(self):
10 self.module = importlib.import_module(self.module_name)
11 return _FilteredModule(self.module)
12
13 def __exit__(self, exc_type, exc_value, traceback):
14 return False
15
16class _FilteredModule:
17 def __init__(self, module):
18 self._module = module
19
20 def __getattr__(self, name):
21 obj = getattr(self._module, name)
22
23 if _answers_all_questions(obj):
24 return obj
25 raise AttributeError(f"'{name}' is not allowed by the BridgeKeeper")
26
27def _answers_all_questions(obj):
28 return (
29 _has_valid_name(obj)
30 and _has_valid_quest(obj)
31 and _has_valid_secret_attribute(obj)
32 )
33
34def _has_valid_name(obj):
35 name = getattr(obj, "__name__", None)
36 return isinstance(name, str) and len(name) > 0 and name[0].isupper()
37
38def _has_valid_quest(obj):
39 if not callable(obj):
40 return False
41
42 params = _extract_parameters(obj)
43
44 try:
45 args = [_sample_value(param_type) for _, param_type in params]
46 obj(*args)
47 return True
48 except Exception:
49 return False
50
51def _extract_parameters(obj):
52 doc = getattr(obj, "__doc__", None)
53
54 if not doc:
55 return []
56
57 match = re.search(
58 r"Parameters\s*\n----------\s*\n(.*?)(?:\n\s*\n|$)",
59 doc,
60 re.DOTALL,
61 )
62
63 if not match:
64 return []
65
66 block = match.group(1)
67 params = []
68
69 for line in block.splitlines():
70 line = line.strip()
71
72 if not line or ":" not in line:
73 continue
74
75 name, type_expr = line.split(":", 1)
76 params.append((name.strip(), type_expr.strip()))
77
78 return params
79
80def _sample_value(type_expr):
81 type_expr = type_expr.strip()
82
83 if "|" in type_expr:
84 first_type = type_expr.split("|", 1)[0].strip()
85 return _sample_value(first_type)
86
87 collection_match = re.fullmatch(r"(list|tuple|set)\[(.+)\]", type_expr)
88
89 if collection_match:
90 collection_type = collection_match.group(1)
91 inner_type = collection_match.group(2).strip()
92 value = _sample_value(inner_type)
93
94 if collection_type == "list":
95 return [value]
96 if collection_type == "tuple":
97 return (value,)
98 if collection_type == "set":
99 return {value}
100
101 dict_match = re.fullmatch(r"dict\[(.+),\s*(.+)\]", type_expr)
102
103 if dict_match:
104 key_type = dict_match.group(1).strip()
105 value_type = dict_match.group(2).strip()
106
107 key = _sample_value(key_type)
108 value = _sample_value(value_type)
109 return {key: value}
110
111 return _sample_base_value(type_expr)
112
113def _sample_base_value(type_name):
114 samples = {
115 "int": 1,
116 "float": 1.5,
117 "str": "bridge",
118 "bool": True,
119 "complex": 1 + 2j,
120 "bytes": b"bridge",
121 "None": None,
122 "NoneType": None,
123 }
124 return samples.get(type_name, object())
125
126def _has_valid_secret_attribute(obj):
127 for attr_name in dir(obj):
128 if _is_valid_secret_attribute(attr_name):
129 return True
130
131 return False
132
133def _is_valid_secret_attribute(name):
134 if _is_dunder(name):
135 return False
136
137 if _has_more_than_three_consecutive_vowels(name):
138 return False
139
140 last_latin_letter = _last_latin_letter(name)
141 return last_latin_letter is not None and last_latin_letter.isupper()
142
143def _is_dunder(name):
144 return len(name) >= 4 and name.startswith("__") and name.endswith("__")
145
146def _has_more_than_three_consecutive_vowels(name):
147 return re.search(r"[aeiouAEIOU]{4,}", name) is not None
148
149def _last_latin_letter(name):
150 for ch in reversed(name):
151 if ("a" <= ch <= "z") or ("A" <= ch <= "Z"):
152 return ch
153 return None
............F...........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
======================================================================
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.002s
FAILED (failures=2)
01.05.2026 15:40
01.05.2026 15:47
01.05.2026 15:48