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

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

4 точки общо

25 успешни теста
0 неуспешни теста
Код
Скрий всички коментари

  1import importlib
  2import inspect
  3import re
  4from itertools import product
  5
  6
  7class BridgeKeeper:
  8    def __init__(self, module_name):
  9        self.module_name = module_name
 10        self.module = None
 11        self.allowed_objects = {}
 12
 13    def __enter__(self):
 14        self.module = importlib.import_module(self.module_name)
 15
 16        for name in dir(self.module):
 17            obj = getattr(self.module, name)
 18
 19            if self._answers_all_questions(obj):
 20                self.allowed_objects[name] = obj
 21
 22        return _FilteredModule(self.allowed_objects)
 23
 24    def __exit__(self, exc_type, exc_value, traceback):
 25        return False
 26
 27    def _answers_all_questions(self, obj):
 28        return (
 29            self._has_good_name(obj)
 30            and self._has_good_quest(obj)
 31            and self._has_secret_answer(obj)
 32        )
 33
 34    def _has_good_name(self, obj):
 35        if not hasattr(obj, "__name__"):
 36            return False
 37
 38        name = obj.__name__
 39
 40        if not isinstance(name, str) or name == "":
 41            return False
 42
 43        return name[0].isupper()
 44
 45    def _has_good_quest(self, obj):
 46        if not callable(obj):
 47            return False
 48
 49        parameters = self._get_parameters_from_docstring(obj)
 50
 51        try:
 52            signature = inspect.signature(obj)
 53            real_params = [
 54                param.name
 55                for param in signature.parameters.values()
 56                if param.kind in (
 57                    param.POSITIONAL_ONLY,
 58                    param.POSITIONAL_OR_KEYWORD,
 59                    param.KEYWORD_ONLY,
 60                )
 61            ]
 62
 63            doc_param_names = [name for name, _ in parameters]
 64
 65            if real_params != doc_param_names:
 66                return False
 67        except (TypeError, ValueError):
 68            pass
 69
 70        possible_arguments = []
 71
 72        for _, type_text in parameters:
 73            values = self._values_for_type(type_text)
 74            possible_arguments.append(values)
 75
 76        try:
 77            for args in product(*possible_arguments):
 78                obj(*args)
 79        except Exception:
 80            return False
 81
 82        return True
 83
 84    def _get_parameters_from_docstring(self, obj):
 85        docstring = inspect.getdoc(obj)
 86
 87        if not docstring:
 88            return []
 89
 90        match = re.search(
 91            r"^Parameters\s*\n-+\s*\n(.*?)(?:\n\s*\n|\Z)",
 92            docstring,
 93            re.MULTILINE | re.DOTALL,
 94        )
 95
 96        if not match:
 97            return []
 98
 99        block = match.group(1)
100        parameters = []
101
102        for line in block.splitlines():
103            param_match = re.match(r"\s*([a-zA-Z_]\w*)\s*:\s*(.+)\s*$", line)
104
105            if param_match:
106                name = param_match.group(1)
107                type_text = param_match.group(2)
108                parameters.append((name, type_text))
109
110        return parameters
111
112    def _values_for_type(self, type_text):
113        parts = self._split_union(type_text)
114
115        values = []
116
117        for part in parts:
118            values.append(self._value_for_single_type(part.strip()))
119
120        return values
121
122    def _split_union(self, type_text):
123        parts = []
124        current = ""
125        brackets = 0
126
127        for char in type_text:
128            if char == "[":
129                brackets += 1
130            elif char == "]":
131                brackets -= 1
132
133            if char == "|" and brackets == 0:
134                parts.append(current.strip())
135                current = ""
136            else:
137                current += char
138
139        parts.append(current.strip())
140        return parts
141
142    def _value_for_single_type(self, type_text):
143        base_values = {
144            "int": 1,
145            "float": 1.5,
146            "str": "text",
147            "bool": True,
148            "complex": 1 + 2j,
149            "bytes": b"text",
150        }
151
152        if type_text in base_values:
153            return base_values[type_text]
154
155        list_match = re.match(r"list\[(.+)\]$", type_text)
156        if list_match:
157            inner_type = list_match.group(1).strip()
158            return [self._value_for_single_type(inner_type)]
159
160        tuple_match = re.match(r"tuple\[(.+)\]$", type_text)
161        if tuple_match:
162            inner_type = tuple_match.group(1).strip()
163            return (self._value_for_single_type(inner_type),)
164
165        set_match = re.match(r"set\[(.+)\]$", type_text)
166        if set_match:
167            inner_type = set_match.group(1).strip()
168            return {self._value_for_single_type(inner_type)}
169
170        dict_match = re.match(r"dict\[(.+),\s*(.+)\]$", type_text)
171        if dict_match:
172            key_type = dict_match.group(1).strip()
173            value_type = dict_match.group(2).strip()
174
175            key = self._value_for_single_type(key_type)
176            value = self._value_for_single_type(value_type)
177
178            return {key: value}
179
180        return None
181
182    def _has_secret_answer(self, obj):
183        for attribute_name in dir(obj):
184            if self._is_valid_secret_attribute(attribute_name):
185                return True
186
187        return False
188
189    def _is_valid_secret_attribute(self, name):
190        if name.startswith("__") and name.endswith("__"):
191            return False
192
193        if re.search(r"[aeiouAEIOU]{4,}", name):
194            return False
195
196        letters = re.findall(r"[a-zA-Z]", name)
197
198        if not letters:
199            return False
200
201        return letters[-1].isupper()
202
203
204class _FilteredModule:
205    def __init__(self, allowed_objects):
206        self.allowed_objects = allowed_objects
207
208    def __getattr__(self, name):
209        if name in self.allowed_objects:
210            return self.allowed_objects[name]
211
212        raise AttributeError(f"Object '{name}' is not allowed by the BridgeKeeper")

.........................
----------------------------------------------------------------------
Ran 25 tests in 0.026s

OK

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