Домашни > The Old Man from Scene #24 > Решения > Решението на Илиян Архангелов

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

4 точки общо

24 успешни теста
1 неуспешни теста
Код

  1import importlib
  2import itertools
  3import re
  4
  5
  6def _check_name(obj):
  7    try:
  8        name = getattr(obj, '__name__', None)
  9        if isinstance(name, str) and name and name[0].isupper():
 10            return True
 11    except Exception:
 12        pass
 13    return False
 14
 15
 16def _generate_dummies(type_str):
 17    results = []
 18    for t in type_str.split('|'):
 19        t = t.strip()
 20        if t == 'int':
 21            results.append(1)
 22        elif t == 'float':
 23            results.append(1.0)
 24        elif t == 'str':
 25            results.append('a')
 26        elif t == 'bool':
 27            results.append(True)
 28        elif match := re.match(r'list\[(.+)\]', t):
 29            results.extend([[sub] for sub in _generate_dummies(match.group(1))])
 30        elif match := re.match(r'tuple\[(.+)\]', t):
 31            results.extend([(sub,) for sub in _generate_dummies(match.group(1))])
 32        elif match := re.match(r'set\[(.+)\]', t):
 33            for sub in _generate_dummies(match.group(1)):
 34                try:
 35                    results.append({sub})
 36                except TypeError:
 37                    pass
 38        elif match := re.match(r'dict\[(.+?),\s*(.+)\]', t):
 39            k_types = _generate_dummies(match.group(1))
 40            v_types = _generate_dummies(match.group(2))
 41            for k in k_types:
 42                for v in v_types:
 43                    try:
 44                        results.append({k: v})
 45                    except TypeError:
 46                        pass
 47    return results
 48
 49
 50def _check_quest(obj):
 51    if not callable(obj):
 52        return False
 53
 54    try:
 55        doc = getattr(obj, '__doc__', None)
 56    except Exception:
 57        return False
 58
 59    param_types = []
 60    if doc:
 61        match = re.search(r'Parameters\r?\n-+\r?\n(.*?)(?:\r?\n\r?\n|\Z)', doc, re.DOTALL)
 62        if match:
 63            block = match.group(1)
 64            for line in block.strip().split('\n'):
 65                line = line.strip()
 66                if ':' in line:
 67                    _, p_type = line.split(':', 1)
 68                    param_types.append(p_type.strip())
 69
 70    if not param_types:
 71        try:
 72            obj()
 73            return True
 74        except Exception:
 75            return False
 76
 77    try:
 78        param_options = [_generate_dummies(pt) for pt in param_types]
 79        for args in itertools.product(*param_options):
 80            obj(*args)
 81        return True
 82    except Exception:
 83        return False
 84
 85
 86def _is_valid_secret_attr(attr):
 87    if attr.startswith('__') and attr.endswith('__'):
 88        return False
 89    
 90    if re.search(r'[aeiouAEIOU]{4,}', attr):
 91        return False
 92    
 93    letters = re.findall(r'[a-zA-Z]', attr)
 94    return bool(letters and letters[-1].isupper())
 95
 96
 97def _check_secret_attr(obj):
 98    try:
 99        attrs = dir(obj)
100    except Exception:
101        return False
102
103    return any(_is_valid_secret_attr(attr) for attr in attrs)
104
105
106class FilteredModuleProxy:
107    def __init__(self, module):
108        self._module = module
109
110    def __getattr__(self, name):
111        try:
112            obj = getattr(self._module, name)
113        except AttributeError:
114            raise AttributeError(f"Module has no attribute '{name}'")
115
116        if not (_check_name(obj) and _check_quest(obj) and _check_secret_attr(obj)):
117            raise AttributeError(f"Attribute '{name}' failed the BridgeKeeper's questions.")
118
119        return obj
120
121
122class BridgeKeeper:
123    def __init__(self, module_name):
124        self.module_name = module_name
125
126    def __enter__(self):
127        module = importlib.import_module(self.module_name)
128        return FilteredModuleProxy(module)
129
130    def __exit__(self, exc_type, exc_val, exc_tb):
131        pass

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

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