Домашни > The Old Man from Scene #24 > Решения > Решението на Ирина Влайкова

Резултати
4 точки от тестове
0 точки от учител

4 точки общо

23 успешни теста
2 неуспешни теста
Код

  1import importlib
  2
  3class BridgeKeeper:
  4    def __init__(self, module_name):
  5        self.module_name = module_name
  6        self.module = None
  7
  8    def __enter__(self):
  9        self.module = importlib.import_module(self.module_name)
 10        return FilteredModule(self.module)
 11
 12    def __exit__(self, exc_type, exc_val, exc_tb):
 13        pass
 14
 15
 16class FilteredModule:
 17    def __init__(self, module):
 18        self._module = module
 19
 20    def __getattr__(self, attr_name):
 21        obj = getattr(self._module, attr_name)
 22
 23        if is_allowed(obj):
 24            return obj
 25
 26        raise AttributeError(f"{attr_name} not allowed")
 27
 28def is_allowed(obj):
 29    return ( answers_name_question(obj)
 30             and answers_quest_question(obj)
 31             and answers_random_question(obj))
 32
 33def answers_name_question(obj):
 34    if not hasattr(obj, "__name__"):
 35        return False
 36
 37    name = obj.__name__
 38
 39    if not name:
 40        return False
 41
 42    return name[0].isupper()
 43
 44def answers_quest_question(obj):
 45    if not callable(obj):
 46        return False
 47
 48    try:
 49        all_args = build_args_from_docstring(obj.__doc__)
 50
 51        for args in all_args:
 52            try:
 53                obj(*args)
 54                return True
 55            except Exception:
 56                continue
 57
 58        return False
 59    except Exception:
 60        return False
 61
 62def build_args_from_docstring(docstring):
 63    block = extract_parameters_block(docstring)
 64
 65    if not block:
 66        return [[]]
 67
 68    options = []
 69
 70    for line in block.splitlines():
 71        line = line.strip()
 72
 73        if not line:
 74            continue
 75
 76        if ":" not in line:
 77            continue
 78
 79        name, type_expr = line.split(":", 1)
 80        options.append(example_values(type_expr.strip()))
 81
 82    result = [[]]
 83
 84    for values in options:
 85        new_result = []
 86
 87        for current_args in result:
 88            for value in values:
 89                new_result.append(current_args + [value])
 90
 91        result = new_result
 92
 93    return result
 94
 95def extract_parameters_block(docstring):
 96    if not docstring:
 97        return ""
 98
 99    lines = docstring.splitlines()
100
101    start = None
102
103    for i in range(len(lines) - 1):
104        current_line = lines[i].strip()
105        next_line = lines[i + 1].strip()
106
107        if current_line == "Parameters" and is_dash_line(next_line):
108            start = i + 2
109            break
110
111    if start is None: return ""
112
113    block_lines = []
114
115    for i in range (start, len(lines)):
116        line = lines[i]
117
118        if line.strip() == "": break
119
120        block_lines.append(line)
121
122    return "\n".join(block_lines)
123
124def is_dash_line(line):
125    if not line: return False
126
127    for char in line:
128        if char != "-":
129            return False
130
131    return True
132
133def example_value(type_expr):
134    type_expr = type_expr.strip()
135
136    if type_expr.startswith("list[") and type_expr.endswith("]"):
137        inner_type = type_expr[5:-1]
138        return [example_value(inner_type)]
139
140    if type_expr.startswith("tuple[") and type_expr.endswith("]"):
141        inner_type = type_expr[6:-1]
142        return (example_value(inner_type),)
143
144    if type_expr.startswith("set[") and type_expr.endswith("]"):
145        inner_type = type_expr[4:-1]
146        return {example_value(inner_type)}
147
148    if type_expr.startswith("dict[") and type_expr.endswith("]"):
149        inner = type_expr[5:-1]
150        key_type, value_type = split_dict_types(inner)
151
152        return {
153            example_value(key_type): example_value(value_type)
154        }
155
156    return example_basic_value(type_expr)
157
158def example_values(type_expr):
159    type_expr = type_expr.strip()
160
161    if "|" in type_expr:
162        values = []
163
164        for part in type_expr.split("|"):
165            values.extend(example_values(part.strip()))
166
167        return values
168
169    return [example_value(type_expr)]
170
171def split_dict_types(inner):
172    key_type, value_type = inner.split(",", 1)
173    return key_type.strip(), value_type.strip()
174
175def example_basic_value(type_name):
176    values = {
177        "int": 1,
178        "float": 1.0,
179        "str": "a",
180        "bool": True,
181        "complex": 1 + 1j,
182        "bytes": b"a",
183    }
184
185    return values[type_name]
186
187def answers_random_question(obj):
188    for attr_name in dir(obj):
189        if is_valid_secret_attribute(attr_name):
190            return True
191
192    return False
193
194def is_valid_secret_attribute(name):
195    if is_dunder(name):
196        return False
197
198    if has_more_than_3_consecutive_vowels(name):
199        return False
200
201    last_latin_letter = get_last_latin_letter(name)
202
203    if last_latin_letter is None:
204        return False
205
206    return last_latin_letter.isupper()
207
208
209def is_dunder(name):
210    return name.startswith("__") and name.endswith("__")
211
212def has_more_than_3_consecutive_vowels(name):
213    vowels = "aeiouAEIOU"
214    counter = 0
215
216    for char in name:
217        if char in vowels:
218            counter += 1
219
220            if counter > 3:
221                return True
222
223        else:
224            counter = 0
225
226    return False
227
228def get_last_latin_letter(name):
229    for char in reversed(name):
230        if  ("a" <= char <= "z") or ("A" <= char <= "Z"):
231            return char
232
233    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.003s

FAILED (failures=2)

Дискусия
История
Това решение има само една версия.