Домашни > The Old Man from Scene #24 > Решения > Решението на Гергана Панделиева

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

4 точки общо

23 успешни теста
2 неуспешни теста
Код (list[int] | tuple[int] ми вгорчи деня)
Скрий всички коментари

  1import importlib
  2import re
  3from itertools import product
  4
  5class BridgeKeeperProxy:
  6    def __init__(self, module):
  7        self._module = module
  8
  9    def __getattr__(self, name):
 10        try:
 11            obj = getattr(self._module, name)
 12        except AttributeError:
 13            raise AttributeError(f"The module has no attribute named'{name}'")
 14
 15        if self._ask_question_1(obj) and self._ask_question_2(obj) and self._ask_question_3(obj):
 16            return obj
 17
 18        raise AttributeError(f"The object '{name}' did not pass the Bridge Guardian's questions!")
 19
 20    def _generate_dummy_arg(self, type_str):
 21        options = [t.strip() for t in type_str.split('|')]
 22
 23        for opt in options:
 24            val = self._generate_single_type(opt)
 25            if val is not None:
 26                return val
 27        return None 
 28
 29    def _extract_params(self, doc):
 30        if not doc:
 31            return []
 32
 33        lines = doc.splitlines()
 34        start = None
 35
 36        for i, line in enumerate(lines):
 37            if line.strip() == "Parameters":
 38                start = i + 2
 39                break
 40
 41        if start is None:
 42            return []
 43
 44        params = []
 45        for line in lines[start:]:
 46            if not line.strip():
 47                break
 48            if ':' in line:
 49                _, val = line.split(':', 1)
 50                params.append(val.strip())
 51
 52        return params
 53
 54    def _generate_single_type(self, type_str):
 55        if type_str == 'int': return 1
 56        if type_str == 'float': return 1.5
 57        if type_str == 'str': return "str"
 58        if type_str == 'bool': return True
 59
 60        dict_match = re.search(r'dict\[(.+?),\s*(.+?)\]', type_str)
 61        if dict_match:
 62            k = self._generate_dummy_arg(dict_match.group(1))
 63            v = self._generate_dummy_arg(dict_match.group(2))
 64            return {k: v}
 65
 66        coll_match = re.search(r'(list|tuple|set)\[(.+?)\]', type_str)
 67        if coll_match:
 68            item = self._generate_dummy_arg(coll_match.group(2))
 69            if coll_match.group(1) == 'list': return [item]
 70            if coll_match.group(1) == 'tuple': return (item,)
 71            if coll_match.group(1) == 'set': return {item}
 72
 73        return None
 74
 75    def _ask_question_1(self, obj):
 76        name = getattr(obj, '__name__', '')
 77        return isinstance(name, str) and len(name) > 0 and name[0].isupper()
 78
 79    def _ask_question_2(self, obj):
 80        if not callable(obj):
 81            return False
 82            
 83        doc = getattr(obj, '__doc__', '') or ''
 84        types = self._extract_params(doc)
 85        all_options = []
 86        for t in types:
 87            variants = [opt.strip() for opt in t.split('|')]
 88            generated = [self._generate_single_type(v) for v in variants]
 89            all_options.append(generated)
 90
 91        for combo in product(*all_options):
 92            try:
 93                obj(*combo)
 94                return True
 95            except Exception:
 96                continue
 97
 98        return False
 99
100    def _ask_question_3(self, obj):
101        for attr in dir(obj):
102            if (attr.startswith('__') and attr.endswith('__')) or (re.search(r'[aeiouAEIOU]{4,}', attr)):
103                continue
104                
105            latin_letters = [l for l in attr if l in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ']
106            if latin_letters and latin_letters[-1].isupper():
107                return True
108        return False
109
110
111class BridgeKeeper:
112    def __init__(self, module_name):
113        self.module_name = module_name
114        self.proxy = None
115
116    def __enter__(self):
117        module = importlib.import_module(self.module_name)
118        self.proxy = BridgeKeeperProxy(module)
119        return self.proxy
120
121    def __exit__(self, exc_type, exc_val, exc_tb):
122        pass

............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)

Дискусия
Виктор Бечев
27.04.2026 18:11

Генерално добре решение. Тук таме има още някакви дребни неща, но съм достатъчно 50:50 дали всъщност не са окей, че да не ги спомена.
История

f1import importlibf1import importlib
2import re2import re
3from itertools import product3from itertools import product
44
5class BridgeKeeperProxy:5class BridgeKeeperProxy:
6    def __init__(self, module):6    def __init__(self, module):
7        self._module = module7        self._module = module
88
9    def __getattr__(self, name):9    def __getattr__(self, name):
10        try:10        try:
11            obj = getattr(self._module, name)11            obj = getattr(self._module, name)
12        except AttributeError:12        except AttributeError:
n13            raise AttributeError(f"Модулът няма атрибут с име '{name}'")n13            raise AttributeError(f"The module has no attribute named'{name}'")
1414
15        if self._ask_question_1(obj) and self._ask_question_2(obj) and self._ask_question_3(obj):15        if self._ask_question_1(obj) and self._ask_question_2(obj) and self._ask_question_3(obj):
16            return obj16            return obj
1717
t18        raise AttributeError(f"Обектът '{name}' не премина въпросите на Пазителя на моста!")t18        raise AttributeError(f"The object '{name}' did not pass the Bridge Guardian's questions!")
1919
20    def _generate_dummy_arg(self, type_str):20    def _generate_dummy_arg(self, type_str):
21        options = [t.strip() for t in type_str.split('|')]21        options = [t.strip() for t in type_str.split('|')]
2222
23        for opt in options:23        for opt in options:
24            val = self._generate_single_type(opt)24            val = self._generate_single_type(opt)
25            if val is not None:25            if val is not None:
26                return val26                return val
27        return None 27        return None 
2828
29    def _extract_params(self, doc):29    def _extract_params(self, doc):
30        if not doc:30        if not doc:
31            return []31            return []
3232
33        lines = doc.splitlines()33        lines = doc.splitlines()
34        start = None34        start = None
3535
36        for i, line in enumerate(lines):36        for i, line in enumerate(lines):
37            if line.strip() == "Parameters":37            if line.strip() == "Parameters":
38                start = i + 238                start = i + 2
39                break39                break
4040
41        if start is None:41        if start is None:
42            return []42            return []
4343
44        params = []44        params = []
45        for line in lines[start:]:45        for line in lines[start:]:
46            if not line.strip():46            if not line.strip():
47                break47                break
48            if ':' in line:48            if ':' in line:
49                _, val = line.split(':', 1)49                _, val = line.split(':', 1)
50                params.append(val.strip())50                params.append(val.strip())
5151
52        return params52        return params
5353
54    def _generate_single_type(self, type_str):54    def _generate_single_type(self, type_str):
55        if type_str == 'int': return 155        if type_str == 'int': return 1
56        if type_str == 'float': return 1.556        if type_str == 'float': return 1.5
57        if type_str == 'str': return "str"57        if type_str == 'str': return "str"
58        if type_str == 'bool': return True58        if type_str == 'bool': return True
5959
60        dict_match = re.search(r'dict\[(.+?),\s*(.+?)\]', type_str)60        dict_match = re.search(r'dict\[(.+?),\s*(.+?)\]', type_str)
61        if dict_match:61        if dict_match:
62            k = self._generate_dummy_arg(dict_match.group(1))62            k = self._generate_dummy_arg(dict_match.group(1))
63            v = self._generate_dummy_arg(dict_match.group(2))63            v = self._generate_dummy_arg(dict_match.group(2))
64            return {k: v}64            return {k: v}
6565
66        coll_match = re.search(r'(list|tuple|set)\[(.+?)\]', type_str)66        coll_match = re.search(r'(list|tuple|set)\[(.+?)\]', type_str)
67        if coll_match:67        if coll_match:
68            item = self._generate_dummy_arg(coll_match.group(2))68            item = self._generate_dummy_arg(coll_match.group(2))
69            if coll_match.group(1) == 'list': return [item]69            if coll_match.group(1) == 'list': return [item]
70            if coll_match.group(1) == 'tuple': return (item,)70            if coll_match.group(1) == 'tuple': return (item,)
71            if coll_match.group(1) == 'set': return {item}71            if coll_match.group(1) == 'set': return {item}
7272
73        return None73        return None
7474
75    def _ask_question_1(self, obj):75    def _ask_question_1(self, obj):
76        name = getattr(obj, '__name__', '')76        name = getattr(obj, '__name__', '')
77        return isinstance(name, str) and len(name) > 0 and name[0].isupper()77        return isinstance(name, str) and len(name) > 0 and name[0].isupper()
7878
79    def _ask_question_2(self, obj):79    def _ask_question_2(self, obj):
80        if not callable(obj):80        if not callable(obj):
81            return False81            return False
82            82            
83        doc = getattr(obj, '__doc__', '') or ''83        doc = getattr(obj, '__doc__', '') or ''
84        types = self._extract_params(doc)84        types = self._extract_params(doc)
85        all_options = []85        all_options = []
86        for t in types:86        for t in types:
87            variants = [opt.strip() for opt in t.split('|')]87            variants = [opt.strip() for opt in t.split('|')]
88            generated = [self._generate_single_type(v) for v in variants]88            generated = [self._generate_single_type(v) for v in variants]
89            all_options.append(generated)89            all_options.append(generated)
9090
91        for combo in product(*all_options):91        for combo in product(*all_options):
92            try:92            try:
93                obj(*combo)93                obj(*combo)
94                return True94                return True
95            except Exception:95            except Exception:
96                continue96                continue
9797
98        return False98        return False
9999
100    def _ask_question_3(self, obj):100    def _ask_question_3(self, obj):
101        for attr in dir(obj):101        for attr in dir(obj):
102            if (attr.startswith('__') and attr.endswith('__')) or (re.search(r'[aeiouAEIOU]{4,}', attr)):102            if (attr.startswith('__') and attr.endswith('__')) or (re.search(r'[aeiouAEIOU]{4,}', attr)):
103                continue103                continue
104                104                
105            latin_letters = [l for l in attr if l in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ']105            latin_letters = [l for l in attr if l in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ']
106            if latin_letters and latin_letters[-1].isupper():106            if latin_letters and latin_letters[-1].isupper():
107                return True107                return True
108        return False108        return False
109109
110110
111class BridgeKeeper:111class BridgeKeeper:
112    def __init__(self, module_name):112    def __init__(self, module_name):
113        self.module_name = module_name113        self.module_name = module_name
114        self.proxy = None114        self.proxy = None
115115
116    def __enter__(self):116    def __enter__(self):
117        module = importlib.import_module(self.module_name)117        module = importlib.import_module(self.module_name)
118        self.proxy = BridgeKeeperProxy(module)118        self.proxy = BridgeKeeperProxy(module)
119        return self.proxy119        return self.proxy
120120
121    def __exit__(self, exc_type, exc_val, exc_tb):121    def __exit__(self, exc_type, exc_val, exc_tb):
122        pass122        pass
123123
124124
125125
126126
127127
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op