Домашни > The Old Man from Scene #24 > Решения > Решението на Коста Илев

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

4 точки общо

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

 1import importlib
 2import re
 3
 4def passes_name(obj):
 5    obj_name = getattr(obj, '__name__', None)
 6    return bool(obj_name and obj_name[0].isupper())
 7
 8def parse_params(doc):
 9    if not doc:
10        return [] 
11    match = re.search(r'Parameters\n----------\n(.*?)(\n\n|$)', doc, re.DOTALL)
12    if not match:
13        return []
14    params = []
15    for line in match.group(1).splitlines():
16        line = line.strip()
17        if not line:
18            break #continue? не съм сигурен дали може да има празни редове между параметрите
19        m = re.match(r'^(\w+)\s:\s(.+)$', line)
20        if m:
21            params.append((m.group(1), m.group(2).strip()))
22    return params
23
24def generate_sample_value(type_str):
25    type_str = type_str.strip()
26    if '|' in type_str:
27        return generate_sample_value(type_str.split('|')[0].strip())
28    dict_match = re.match(r'^dict\[(.+),\s(.+)\]$', type_str)
29    if dict_match:
30        key = generate_sample_value(dict_match.group(1).strip())
31        val = generate_sample_value(dict_match.group(2).strip())
32        return {key: val}
33    coll_match = re.match(r'^(list|tuple|set)\[(.+)\]$', type_str)
34    if coll_match:
35        elem = generate_sample_value(coll_match.group(2).strip())
36        return {'list': [elem], 'tuple': (elem,), 'set': {elem}}[coll_match.group(1)]
37    return {'int': 0, 'float': 0.0, 'str': '', 'bool': True, 'complex': 0j}.get(type_str, None)
38
39def passes_quest(obj):
40    if not callable(obj):
41        return False
42    params = parse_params(getattr(obj, '__doc__', None))
43    sample_args = [generate_sample_value(ts) for _, ts in params]
44    try:
45        obj(*sample_args)
46        return True
47    except TypeError:
48        return False
49    except Exception:
50        return True 
51
52def is_valid_answer_attr(attr_name):
53    if attr_name.startswith('__') and attr_name.endswith('__'):
54        return False
55    letters_only = re.sub(r'[^a-zA-Z]', '', attr_name)
56    if not letters_only:
57        return False
58    if not letters_only[-1].isupper():
59        return False
60    if re.search(r'[aeiouAEIOU]{4,}', attr_name):
61        return False
62    return True
63
64def passes_colour(obj):
65    return any(is_valid_answer_attr(attr) for attr in dir(obj))
66
67class FilteredModule:
68    def __init__(self, allowed):
69        object.__setattr__(self, '_allowed', allowed)
70
71    def __getattr__(self, name):
72        allowed = object.__getattribute__(self, '_allowed')
73        if name in allowed:
74            return allowed[name]
75        raise AttributeError(f"'{name}' is not accessible through BridgeKeeper")
76
77
78class BridgeKeeper:
79    def __init__(self, module_name):
80        self.module_name = module_name
81
82    def __enter__(self):
83        module = importlib.import_module(self.module_name)
84        allowed = {}
85        for name, obj in vars(module).items():
86            if passes_name(obj) and passes_quest(obj) and passes_colour(obj):
87                allowed[name] = obj
88        return FilteredModule(allowed)
89
90    def __exit__(self, exc_type, exc_val, exc_tb):
91        return False
92  

............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.010s

FAILED (failures=2)

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