1import inspect
2
3class BridgeKeeper:
4 def __init__(self, module):
5 self.module = module
6
7 def __enter__(self):
8 return self.View(self.module)
9
10 def __exit__(self, error_type, error_value, error_trace):
11 return False
12
13
14 class View:
15 def __init__(self, module):
16 self.module = module
17
18 def __getattr__(self, name):
19 if name not in self.module:
20 raise AttributeError(name)
21 obj = self.module[name]
22 if not self._check_name(obj):
23 raise AttributeError(name)
24 if not self._check_callable(obj):
25 raise AttributeError(name)
26 if not self._check_hidden(obj):
27 raise AttributeError(name)
28 return obj
29
30 def _check_name(self, obj):
31 if not hasattr(obj, "__name__"):
32 return False
33 name = obj.__name__
34 return isinstance(name, str) and name and name[0].isupper()
35
36 def _check_callable(self, obj):
37 if not callable(obj):
38 return False
39 func_args = list(inspect.signature(obj).parameters.keys())
40 text_from_docstring = getattr(obj, "__doc__", "")
41 args_from_docstring = self._get_doc_params(text_from_docstring)
42 if not args_from_docstring:
43 return True
44 names = [p[0] for p in args_from_docstring]
45 types = [p[1] for p in args_from_docstring]
46 if func_args != names:
47 return False
48
49 test_values = []
50 for t in types:
51 test_values.append(self._make_dummy_value(t))
52 try:
53 obj(*test_values)
54 except TypeError:
55 return False
56 return True
57
58 def _get_doc_params(self, text_from_docstring):
59 if not text_from_docstring:
60 return []
61 lines = text_from_docstring.splitlines()
62 start_of_Parameters = -1
63 for i in range(len(lines)):
64 if lines[i].strip() == "Parameters":
65 start_of_Parameters = i
66 break
67 if start_of_Parameters == -1:
68 return []
69 curr = start_of_Parameters + 2
70 result = []
71 while curr < len(lines):
72 line = lines[curr].strip()
73 if line == "":
74 break
75 if ":" in line:
76 parts = line.split(":")
77 name = parts[0].strip()
78 type_ = parts[1].strip()
79 result.append((name, type_))
80 curr += 1
81 return result
82
83 def _make_dummy_value(self, type_str):
84 type_str = type_str.strip()
85
86 if "|" in type_str:
87 type_str = type_str.split("|")[0].strip()
88
89 if type_str.startswith("list"):
90 return []
91 if type_str.startswith("tuple"):
92 return ()
93 if type_str.startswith("set"):
94 return set()
95 if type_str.startswith("dict"):
96 return {}
97
98 if type_str == "int":
99 return 0
100 if type_str == "float":
101 return 0.0
102 if type_str == "str":
103 return ""
104 if type_str == "bool":
105 return False
106
107 return None
108
109 def _check_hidden(self, obj):
110 for attr in dir(obj):
111 if attr.startswith("__") and attr.endswith("__"):
112 continue
113 last_ch = None
114 for c in attr:
115 if c.isalpha():
116 last_ch = c
117 if not last_ch or not last_ch.isupper():
118 continue
119 vowel = "aeiou"
120 count = 0
121 for c in attr.lower():
122 if c in vowel:
123 count += 1
124 if count > 3:
125 break
126 else:
127 count = 0
128 else:
129 return True
130 return False
131
132
EEEEEEEEEEEE.............
======================================================================
ERROR: test_allows_attribute_whose_last_letter_is_uppercase_but_name_continues (test.TestBridgeKeeper.test_allows_attribute_whose_last_letter_is_uppercase_but_name_continues)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 55, in test_allows_attribute_whose_last_letter_is_uppercase_but_name_continues
self.assertEqual(filtered.trailing_marker(7), 70)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: trailing_marker
======================================================================
ERROR: test_allows_callable_instance (test.TestBridgeKeeper.test_allows_callable_instance)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 86, in test_allows_callable_instance
self.assertEqual(filtered.callable_box([1, 2, 3]), 6)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: callable_box
======================================================================
ERROR: test_allows_dict_typed_parameter (test.TestBridgeKeeper.test_allows_dict_typed_parameter)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 68, in test_allows_dict_typed_parameter
self.assertEqual(filtered.score_map({"alice": 3, "bob": 7}), 10)
^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: score_map
======================================================================
ERROR: test_allows_multi_argument_union_bonus_case (test.TestBridgeKeeper.test_allows_multi_argument_union_bonus_case)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 95, in test_allows_multi_argument_union_bonus_case
self.assertEqual(filtered.triple_union_pair(3, [1, 2]), 6)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: triple_union_pair
======================================================================
ERROR: test_allows_set_typed_parameter (test.TestBridgeKeeper.test_allows_set_typed_parameter)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 77, in test_allows_set_typed_parameter
self.assertEqual(filtered.gather_tags({"bridge", "keeper"}), "bridge,keeper")
^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: gather_tags
======================================================================
ERROR: test_allows_third_answer_name_with_trailing_double_underscores_if_not_dunder (test.TestBridgeKeeper.test_allows_third_answer_name_with_trailing_double_underscores_if_not_dunder)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 64, in test_allows_third_answer_name_with_trailing_double_underscores_if_not_dunder
self.assertEqual(filtered.trailing_underscores(4), 5)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: trailing_underscores
======================================================================
ERROR: test_allows_third_answer_with_single_uppercase_letter_name (test.TestBridgeKeeper.test_allows_third_answer_with_single_uppercase_letter_name)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 117, in test_allows_third_answer_with_single_uppercase_letter_name
self.assertEqual(filtered.single_letter_answer(5), 15)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: single_letter_answer
======================================================================
ERROR: test_allows_union_typed_parameter (test.TestBridgeKeeper.test_allows_union_typed_parameter)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 17, in test_allows_union_typed_parameter
self.assertEqual(filtered.sum_group([1, 2, 3]), 6)
^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: sum_group
======================================================================
ERROR: test_allows_valid_function (test.TestBridgeKeeper.test_allows_valid_function)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 9, in test_allows_valid_function
self.assertEqual(filtered.multiply_text(3, "na"), "nanana")
^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: multiply_text
======================================================================
ERROR: test_allows_zero_arg_callable_when_parameters_block_is_missing (test.TestBridgeKeeper.test_allows_zero_arg_callable_when_parameters_block_is_missing)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 13, in test_allows_zero_arg_callable_when_parameters_block_is_missing
self.assertEqual(filtered.silent_oracle(), 42)
^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: silent_oracle
======================================================================
ERROR: test_allows_zero_arg_callable_with_missing_docstring (test.TestBridgeKeeper.test_allows_zero_arg_callable_with_missing_docstring)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 51, in test_allows_zero_arg_callable_with_missing_docstring
self.assertEqual(filtered.ghost_zero(), "boo")
^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: ghost_zero
======================================================================
ERROR: test_ignores_parameter_like_lines_outside_parameters_block (test.TestBridgeKeeper.test_ignores_parameter_like_lines_outside_parameters_block)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 37, in test_ignores_parameter_like_lines_outside_parameters_block
self.assertEqual(filtered.tricky_doc(2, "xo"), "xoxo")
^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 20, in __getattr__
raise AttributeError(name)
AttributeError: tricky_doc
----------------------------------------------------------------------
Ran 25 tests in 0.004s
FAILED (errors=12)
Виктор Бечев
01.05.2026 15:27Ако беше използвала регулярните изрази, които взехме в лекцията баш преди домашното, щеше да си спестиш голям процент от откриването на топлата вода, итерирайки през стрингове и прочие.
|
01.05.2026 15:18
01.05.2026 15:19
01.05.2026 15:24
01.05.2026 15:21
01.05.2026 15:26
01.05.2026 15:26