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

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

3 точки общо

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

  1import importlib
  2import inspect
  3import re
  4
  5class BridgeKeeper:
  6    def __init__(self, module_name):
  7       self.module_name = module_name
  8
  9    def __enter__(self):
 10        module = importlib.import_module(self.module_name)
 11        return ModuleProxy(module)
 12
 13    def __exit__(self, exc_type, exc_val, exc_tb):
 14        return False
 15
 16class ModuleProxy:
 17    def __init__(self, module):
 18        self.module = module
 19
 20    def __getattr__(self, name):
 21        if not hasattr(self.module, name):
 22            raise AttributeError(name)
 23
 24        obj = getattr(self.module, name)
 25
 26        # Question 1
 27        if not NameValidator.is_valid(obj):
 28            raise AttributeError(name)
 29
 30        #Question 2
 31        if callable(obj) and not CallableValidator.is_valid(obj):
 32            raise AttributeError(name)
 33
 34        # Question 3
 35        if not self._has_valid_attribute(obj):
 36            raise AttributeError(name)
 37
 38        return obj
 39
 40    def _has_valid_attribute(self, obj):
 41        for attr in dir(obj):
 42            if AttributeValidator.is_valid(attr):
 43                return True
 44
 45        return False
 46
 47
 48class NameValidator:
 49    @staticmethod
 50    def is_valid(obj):
 51        if not hasattr(obj, "__name__"):
 52            return False
 53        name = obj.__name__
 54        return bool(name) and name[0].isupper()
 55
 56
 57class AttributeValidator:
 58    @staticmethod
 59    def is_valid(name):
 60        return (AttributeValidator._not_dunder(name) and
 61                AttributeValidator._no_vowel_overflow(name) and
 62                AttributeValidator._last_letter_is_upper(name))
 63
 64    @staticmethod
 65    def _not_dunder(name):
 66        return re.fullmatch(r"__.*__", name) is None
 67
 68    @staticmethod
 69    def _no_vowel_overflow(name):
 70        return re.search(r"[aeiouAEIOU]{4,}", name) is None
 71
 72    @staticmethod
 73    def _last_letter_is_upper(name):
 74        return re.fullmatch(r".*[A-Z][^a-zA-Z]*", name) is not None
 75
 76
 77class CallableValidator:
 78    @staticmethod
 79    def is_valid(func):
 80        try:
 81            sig = inspect.signature(func)
 82        except Exception:
 83            return False
 84
 85        doc = inspect.getdoc(func)
 86        expected = CallableValidator._parse_doc(doc)
 87
 88        if expected is None:
 89            return len(sig.parameters) == 0
 90
 91        if list(sig.parameters.keys()) != [p[0] for p in expected]:
 92            return False
 93
 94        try:
 95            args = [CallableValidator._dummy_value(t) for _, t in expected]
 96            sig.bind(*args)
 97        except Exception:
 98            return False
 99
100        return True
101
102    @staticmethod
103    def _parse_doc(doc):
104        if not doc:
105            return None
106
107        match = re.search(r"Parameters\s*-+\s*(.*?)(\n\s*\n|$)", doc, re.DOTALL)
108
109        if not match:
110            return None
111
112        block = match.group(1)
113
114        params = []
115        for line in block.splitlines():
116            m = re.match(r"\s*(\w+)\s*:\s*(.+)", line)
117            if m:
118                name = m.group(1)
119                type_str = m.group(2).strip()
120                params.append((name, type_str))
121
122        return params
123
124    @staticmethod
125    def _dummy_value(type_str):
126        type_str = type_str.strip()
127
128        # basic types
129        if type_str == "int":
130            return 1
131        if type_str == "float":
132            return 1.0
133        if type_str == "str":
134            return "a"
135        if type_str == "bool":
136            return True
137
138        # union A | B
139        if "|" in type_str:
140            return CallableValidator._dummy_value(type_str.split("|")[0])
141
142        # list[T]
143        if type_str.startswith("list["):
144            inner = type_str[5:-1]
145            return [CallableValidator._dummy_value(inner)]
146
147        # tuple[T]
148        if type_str.startswith("tuple["):
149            inner = type_str[6:-1]
150            return (CallableValidator._dummy_value(inner),)
151
152        # set[T]
153        if type_str.startswith("set["):
154            inner = type_str[4:-1]
155            return {CallableValidator._dummy_value(inner)}
156
157        # dict[K,V]
158        if type_str.startswith("dict["):
159            inner = type_str[5:-1]
160            k_t, v_t = map(str.strip, inner.split(","))
161            return {
162                CallableValidator._dummy_value(k_t):
163                CallableValidator._dummy_value(v_t)
164            }
165
166        return None

..............F..F.F.F.FF
======================================================================
FAIL: test_rejects_dict_when_callable_does_not_work_with_documented_key_and_value_types (test.TestBridgeKeeper.test_rejects_dict_when_callable_does_not_work_with_documented_key_and_value_types)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 90, in test_rejects_dict_when_callable_does_not_work_with_documented_key_and_value_types
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised

======================================================================
FAIL: test_rejects_list_when_callable_does_not_work_with_documented_element_type (test.TestBridgeKeeper.test_rejects_list_when_callable_does_not_work_with_documented_element_type)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 102, in test_rejects_list_when_callable_does_not_work_with_documented_element_type
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised

======================================================================
FAIL: test_rejects_non_callable_object (test.TestBridgeKeeper.test_rejects_non_callable_object)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 27, in test_rejects_non_callable_object
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised

======================================================================
FAIL: test_rejects_set_when_callable_does_not_work_with_documented_element_type (test.TestBridgeKeeper.test_rejects_set_when_callable_does_not_work_with_documented_element_type)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 112, in test_rejects_set_when_callable_does_not_work_with_documented_element_type
with self.assertRaises(AttributeError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: AttributeError not raised

======================================================================
FAIL: test_rejects_tuple_when_callable_does_not_work_with_documented_element_type (test.TestBridgeKeeper.test_rejects_tuple_when_callable_does_not_work_with_documented_element_type)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 107, in test_rejects_tuple_when_callable_does_not_work_with_documented_element_type
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.004s

FAILED (failures=6)

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