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

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

4 точки общо

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

  1"""Homework 4."""
  2import importlib
  3import itertools
  4import re
  5
  6
  7class BridgeKeeper:
  8    """Brigde keeper class."""
  9
 10    def __init__(self, module_name):
 11        self.module_name = module_name
 12        self._module = None
 13
 14    def __enter__(self):
 15        self._module = importlib.import_module(self.module_name)
 16        return FilteredModule(self._module)
 17
 18    def __exit__(self, exc_type, exc_value, traceback):
 19        return False
 20
 21
 22class FilteredModule:
 23    """Filter module class, which determinated which module is allowed."""
 24
 25    def __init__(self, module):
 26        self._module = module
 27        self._validator = ValidationHelperClass()
 28
 29    def __getattr__(self, name):
 30        try:
 31            attribute = getattr(self._module, name)
 32        except AttributeError:
 33            raise AttributeError(name) from None
 34
 35        if self._validator.is_module_allowed(attribute):
 36            return attribute
 37
 38        raise AttributeError(name)
 39
 40
 41class ValidationHelperClass:
 42    """Helper validation class that contains all validation helper functions."""
 43
 44    BASIC_DUMMY_VALUES = {
 45        "int": 1,
 46        "float": 1.0,
 47        "str": "dummy",
 48        "bool": False,
 49        "complex": 1 + 1j,
 50        "bytes": b"dummy",
 51        "None": None,
 52    }
 53
 54    VALID_ATTRIBUTE_PATTERN = re.compile(
 55        r"^(?!__)(?!.*[aeiouAEIOU]{4})[A-Za-z0-9_]*[A-Z][0-9_]*$"
 56    )
 57
 58    PARAMETERS_PATTERN = re.compile(
 59        r"Parameters\s*\n-+\s*\n([\s\S]*?)(?=\n\s*\n|$)"
 60    )
 61
 62    @classmethod
 63    def is_module_allowed(cls, candidate):
 64        """ Method that checks id module is allowed per 3 conditions """
 65        return (
 66            cls.has_valid_name(candidate)
 67            and cls.has_valid_attribute(candidate)
 68            and cls.has_valid_quest(candidate)
 69        )
 70
 71    @staticmethod
 72    def has_valid_name(candidate):
 73        """ First conditiona for valid name """
 74        name = getattr(candidate, "__name__", None)
 75
 76        if not isinstance(name, str) or not name:
 77            return False
 78
 79        return name[0].isupper()
 80
 81    @classmethod
 82    def has_valid_attribute(cls, candidate):
 83        """ Second condition for at least 1 valid attribute """
 84        return any(
 85            cls.VALID_ATTRIBUTE_PATTERN.search(attribute)
 86            for attribute in dir(candidate)
 87        )
 88
 89    @classmethod
 90    def has_valid_quest(cls, candidate):
 91        """ Third condition for valid params and doc params """
 92        if not callable(candidate):
 93            return False
 94
 95        doc = getattr(candidate, "__doc__", "") or ""
 96
 97        doc_parameters_match = re.search(cls.PARAMETERS_PATTERN, doc)
 98
 99        try:
100            if doc_parameters_match:
101                parameters = list(
102                    cls._parse_doc_parameters(
103                        doc_parameters_match.group(1).splitlines()
104                    )
105                )
106            else:
107                parameters = []
108
109            dummy_args = [
110                cls._dummy_value_complex_structure(parameter_type)
111                for _, parameter_type in parameters
112            ]
113        except ValueError:
114            return False
115
116        for arguments in itertools.product(*dummy_args):
117            try:
118                candidate(*arguments)
119            except Exception:
120                return False
121
122        return True
123
124    @classmethod
125    def _parse_doc_parameters(cls, parameter_lines):
126        """ Parse doc parameters"""
127        for line in parameter_lines:
128            stripped_line = line.strip()
129
130            if not stripped_line:
131                break
132
133            if " : " not in stripped_line:
134                raise ValueError("Invalid parameter line")
135
136            name, parameter_type = stripped_line.split(":", 1)
137            yield name.strip(), parameter_type.strip()
138
139    @classmethod
140    def _dummy_value_complex_structure(cls, type_name):
141        """ Generate dummy values for complex structures """
142        union_parts = [part.strip() for part in type_name.split("|")]
143
144        if len(union_parts) > 1:
145            values = []
146
147            for part in union_parts:
148                values.extend(
149                    cls._dummy_value_complex_structure(part)
150                )
151
152            return values
153
154        return cls._dummy_value_simple(type_name.strip())
155
156    @classmethod
157    def _dummy_value_simple(cls, type_name):
158        """ Generate dummy values simple structures """
159        if type_name in cls.BASIC_DUMMY_VALUES:
160            return [cls.BASIC_DUMMY_VALUES[type_name]]
161
162        list_match = re.fullmatch(r"list\[(.+)\]", type_name)
163        if list_match:
164            inner_type = list_match.group(1).strip()
165            return [
166                [value]
167                for value in cls._dummy_value_complex_structure(inner_type)
168            ]
169        tuple_match = re.fullmatch(r"tuple\[(.+)\]", type_name)
170        if tuple_match:
171            inner_type = tuple_match.group(1).strip()
172            return [
173                (value,)
174                for value in cls._dummy_value_complex_structure(inner_type)
175            ]
176
177        set_match = re.fullmatch(r"set\[(.+)\]", type_name)
178        if set_match:
179            inner_type = set_match.group(1).strip()
180            values = []
181
182            for value in cls._dummy_value_complex_structure(inner_type):
183                try:
184                    values.append({value})
185                except TypeError:
186                    continue
187
188            if not values:
189                raise ValueError("Invalid set inner type")
190
191            return values
192
193        dict_match = re.fullmatch(r"dict\[(.+),\s*(.+)\]", type_name)
194        if dict_match:
195            key_type = dict_match.group(1).strip()
196            value_type = dict_match.group(2).strip()
197
198            keys = cls._dummy_value_complex_structure(key_type)
199            values = cls._dummy_value_complex_structure(value_type)
200
201            result = []
202
203            for key in keys:
204                for value in values:
205                    try:
206                        result.append({key: value})
207                    except TypeError:
208                        continue
209
210            if not result:
211                raise ValueError("Invalid dict key type")
212
213            return result
214
215        raise ValueError(f"Invalid type: {type_name}")

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

----------------------------------------------------------------------
Ran 25 tests in 0.002s

FAILED (failures=1)

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