1import re
2
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 = __import__(self.module_name)
11 return FilteredModule(self.module)
12
13 def __exit__(self, exc_type, exc_value, traceback):
14 return False
15
16
17class FilteredModule:
18 def __init__(self, module):
19 self.module = module
20
21 def __getattr__(self, name):
22 obj = getattr(self.module, name)
23 if self.answers_first_question(obj) and self.answers_second_question(obj) and self.answers_third_question(obj):
24 return obj
25 raise AttributeError(name)
26
27 def answers_first_question(self, obj):
28 name = getattr(obj, "__name__", None)
29 return isinstance(name, str) and name and name[0].isupper()
30
31 def answers_second_question(self, obj):
32 if not callable(obj):
33 return False
34 parameters = self.get_parameters(obj)
35 parameter_names = [name for name, _ in parameters]
36
37 if parameter_names != self.get_real_parameter_names(obj):
38 return False
39 possible_arguments = [self.get_values(type_name) for _, type_name in parameters]
40
41 for arguments in self.make_combinations(possible_arguments):
42 try:
43 obj(*arguments)
44 except Exception:
45 return False
46 return True
47
48 def answers_third_question(self, obj):
49 for name in dir(obj):
50 if self.is_valid_answer(name):
51 return True
52 return False
53
54 def get_parameters(self, obj):
55 docstring = getattr(obj, "__doc__", None)
56
57 if not docstring:
58 return []
59
60 match = re.search(
61 r"Parameters\s*\n----------\s*\n(.*?)(?:\n\s*\n|\Z)",
62 docstring,
63 re.DOTALL
64 )
65
66 if not match:
67 return []
68 parameters = []
69 for line in match.group(1).splitlines():
70 line = line.strip()
71 if not line:
72 continue
73 name, type_name = line.split(":", 1)
74 parameters.append((name.strip(), type_name.strip()))
75 return parameters
76
77 def get_real_parameter_names(self, obj):
78 if hasattr(obj, "__code__"):
79 code = obj.__code__
80 return list(code.co_varnames[:code.co_argcount])
81
82 if hasattr(obj, "__call__") and hasattr(obj.__call__, "__code__"):
83 code = obj.__call__.__code__
84 names = list(code.co_varnames[:code.co_argcount])
85 if names and names[0] == "self":
86 names = names[1:]
87 return names
88 return []
89
90 def get_values(self, type_name):
91 parts = self.split_union(type_name)
92 values = []
93 for part in parts:
94 values.append(self.get_value(part))
95 return values
96
97 def get_value(self, type_name):
98 type_name = type_name.strip()
99
100 if type_name == "int":
101 return 1
102 if type_name == "float":
103 return 1.0
104 if type_name == "str":
105 return "a"
106 if type_name == "bool":
107 return True
108
109 list_match = re.fullmatch(r"list\[(.+)\]", type_name)
110 if list_match:
111 return [self.get_values(list_match.group(1))[0]]
112
113 tuple_match = re.fullmatch(r"tuple\[(.+)\]", type_name)
114 if tuple_match:
115 return (self.get_values(tuple_match.group(1))[0],)
116
117 set_match = re.fullmatch(r"set\[(.+)\]", type_name)
118 if set_match:
119 return {self.get_values(set_match.group(1))[0]}
120
121 dict_match = re.fullmatch(r"dict\[(.+),\s*(.+)\]", type_name)
122 if dict_match:
123 key_type = dict_match.group(1)
124 value_type = dict_match.group(2)
125 key = self.get_values(key_type)[0]
126 value = self.get_values(value_type)[0]
127 return {key: value}
128
129 return None
130
131 def split_union(self, type_name):
132 parts = []
133 current = ""
134 depth = 0
135
136 for symbol in type_name:
137 if symbol == "[":
138 depth += 1
139 elif symbol == "]":
140 depth -= 1
141
142 if symbol == "|" and depth == 0:
143 parts.append(current.strip())
144 current = ""
145 else:
146 current += symbol
147
148 parts.append(current.strip())
149 return parts
150
151 def make_combinations(self, values):
152 if not values:
153 return [[]]
154
155 result = []
156 for value in values[0]:
157 for combination in self.make_combinations(values[1:]):
158 result.append([value] + combination)
159 return result
160
161 def is_valid_answer(self, name):
162 if name.startswith("__") and name.endswith("__"):
163 return False
164 if re.search(r"[aeiouAEIOU]{4,}", name):
165 return False
166 match = re.search(r"[a-zA-Z](?=[^a-zA-Z]*$)", name)
167 return match is not None and match.group(0).isupper()
.........................
----------------------------------------------------------------------
Ran 25 tests in 0.002s
OK
30.04.2026 14:15
30.04.2026 14:17
30.04.2026 14:17
30.04.2026 14:18