1import importlib
2
3class BridgeKeeper:
4 def __init__(self, module_name):
5 self.module_name = module_name
6
7 def __enter__(self):
8 module = importlib.import_module(self.module_name)
9 return self.FilteredModule(module)
10
11 def __exit__(self, exc_type, exc_val, exc_tb):
12 return False
13
14 class FilteredModule:
15 def __init__(self, original_module):
16 self._original = original_module
17
18 def __getattr__(self, name):
19 obj = getattr(self._original, name)
20
21 if self._passes_all_questions(obj):
22 return obj
23 raise AttributeError(f"BridgeKeeper rejects '{name}'")
24
25 def _passes_all_questions(self, obj):
26 if not self._check_name(obj):
27 return False
28 if not self._check_quest(obj):
29 return False
30 if not self._check_random_question(obj):
31 return False
32 return True
33
34 def _check_name(self, obj):
35 if not hasattr(obj, "__name__"):
36 return False
37 name = obj.__name__
38 if not name:
39 return False
40 return name[0].isupper()
41
42 def _check_quest(self, obj):
43 if not callable(obj):
44 return False
45
46 params = self._get_param_types(obj.__doc__)
47
48 if not params:
49 try:
50 obj()
51 return True
52 except:
53 return False
54
55 test_args = []
56 for p in params:
57 val = self._make_value(p)
58 if val is None and p != "None":
59 return False
60 test_args.append(val)
61
62 try:
63 obj(*test_args)
64 return True
65 except:
66 return False
67
68 def _check_random_question(self, obj):
69 vowels = set('aeiouAEIOU')
70
71 for attr_name in dir(obj):
72 if attr_name.startswith('__') and attr_name.endswith('__'):
73 continue
74
75 seq_vowels = 0
76 bad = False
77 for ch in attr_name:
78 if ch in vowels:
79 seq_vowels += 1
80 if seq_vowels > 3:
81 bad = True
82 break
83 else:
84 seq_vowels = 0
85
86 if bad:
87 continue
88
89 last_alpha = None
90 for ch in reversed(attr_name):
91 if ch.isalpha():
92 last_alpha = ch
93 break
94
95 if last_alpha and last_alpha.isupper():
96 return True
97
98 return False
99
100 def _get_param_types(self, doc):
101 if not doc:
102 return []
103
104 lines = doc.split('\n')
105 in_params = False
106 types = []
107
108 for i in range(len(lines)):
109 line = lines[i].strip()
110
111 if line == "Parameters" and i+1 < len(lines) and lines[i+1].strip() == "----------":
112 in_params = True
113 continue
114
115 if in_params:
116 if line == "":
117 break
118
119 if " : " in line:
120 parts = line.split(" : ", 1)
121 if len(parts) == 2:
122 type_text = parts[1].strip()
123 types.append(type_text)
124
125 return types
126
127 def _make_value(self, type_string):
128 type_string = type_string.strip()
129
130 if "|" in type_string:
131 type_string = type_string.split("|")[0].strip()
132
133 if type_string == "int":
134 return 0
135 if type_string == "float":
136 return 0.0
137 if type_string == "str":
138 return ""
139 if type_string == "bool":
140 return True
141 if type_string == "None":
142 return None
143
144 if type_string.startswith("list["):
145 return []
146 if type_string.startswith("tuple["):
147 return ()
148 if type_string.startswith("set["):
149 return set()
150 if type_string.startswith("dict["):
151 return {}
152
153 return None
............F.F..F...F.FF
======================================================================
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_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_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.003s
FAILED (failures=6)
Виктор Бечев
30.04.2026 13:24Ех, ако бяхме учили регулярни изрази, колко по-просто щеше да е на места. 😛
|
30.04.2026 13:05
30.04.2026 13:07
30.04.2026 13:09
30.04.2026 13:27
30.04.2026 13:23