1import io
2import sys
3import unittest
4
5from secret_results import RESULTS
6
7
8class StdBuffer:
9 def __init__(self):
10 self.buffer = io.StringIO()
11
12 def __enter__(self):
13 self.stds = sys.stdin, sys.stderr, sys.stdout
14 sys.stdin = self.buffer
15 sys.stderr = self.buffer
16 sys.stdout = self.buffer
17
18 def __exit__(self, *args, **kwargs):
19 sys.stdin, sys.stderr, sys.stdout = self.stds
20
21
22with StdBuffer():
23 import solution
24
25
26class TestLockPicker(unittest.TestCase):
27 """Unit test for the LockPicker_* class."""
28
29 def test_sanity(self):
30 """Ensure the class with expected FN is found."""
31 self.assertTrue(found, "Не намирам клас с очакваното име.")
32
33
34found = False
35# Look for a class in student's solution that matches the expected format
36for item in dir(solution):
37 if item.startswith("LockPicker_"):
38 # Try to extract the FN from the class name.
39 fn = item.replace("LockPicker_", "")
40 # If not possible, show relevant error and break -> just run the tests
41 if fn not in RESULTS:
42 print("Факултетният номер е невалиден. Убеди се, че класът ти "
43 "е във формат LockPicker_FN1234.")
44 else:
45 found = True
46 break
47# If there was no break above, no valid class name was found.
48else:
49 print("Не намирам клас с очакваното име. "
50 "Очаквам формат LockPicker_FN1234, "
51 "където FN1234 е валиден ФН на студент от курса.")
52
53
54if __name__ == "__main__":
55 unittest.main()
1import unittest
2import traceback
3
4import timeout_decorator
5
6from secret_results import RESULTS
7
8import solution
9
10
11# Secret lock combination
12_SECRET_COMBINATION = (1, {'реч': 'ник'}, 'Монти Пайтън',
13 {1, 2, 'три'}, True, 3.14159)
14
15
16class LockError(Exception):
17 """Exception that can store additional attributes."""
18
19 def __init__(self, message, position, expected):
20 self.position = position
21 self.expected = expected
22 super().__init__(message)
23
24
25class LockTypeError(LockError, TypeError):
26 """A kind of a TypeError that stores additional attributes."""
27 pass
28
29
30class LockValueError(LockError, ValueError):
31 """A kind of a ValueError that stores additional attributes."""
32 pass
33
34
35class Lock:
36 """A lock mechanism, defended by a secret combination."""
37
38 def __init__(self, *slots):
39 """Initialize based on secret slots."""
40 self._slots = slots
41 self.is_unlocked = False
42
43 def pick(self, *attempts):
44 """Try to pick using attempt values.
45
46 Raise informative exceptions when failed.
47 Change the state of the instance when successfull and return True.
48 """
49 if len(attempts) != len(self._slots):
50 raise LockTypeError("This is a different kind of lock.",
51 None, len(self._slots))
52 for position, (attempt, slot) in enumerate(zip(attempts, self._slots),
53 start=1):
54 if type(attempt) is not type(slot):
55 raise LockTypeError("You shall not pass!", position, type(slot))
56 for position, (attempt, slot) in enumerate(zip(attempts, self._slots),
57 start=1):
58 if attempt != slot:
59 raise LockValueError("You shall not pass!", position, slot)
60 # Unlocked only when the method is called with a correct attempt
61 self.is_unlocked = True
62 return True
63
64
65class TestLockPicker(unittest.TestCase):
66 """Unit test for the LockPicker_* class."""
67
68 def test_picking(self):
69 """Ensure the lock is picked based on already stored boolean var."""
70 # This is just to assign points on success
71 # as the automatic test runner expects at least one test case.
72 self.assertTrue(correct, "Не успя да отключиш.")
73
74
75@timeout_decorator.timeout(1)
76def test_unlocker(unlocker):
77 unlocker.unlock()
78
79
80# Define a lock based on secret
81lock = Lock(*_SECRET_COMBINATION)
82# Assume the lock is not picked
83correct = False
84# Look for a class in student's solution that matches the expected format
85for item in dir(solution):
86 if item.startswith("LockPicker_"):
87 # Try to extract the FN from the class name.
88 fn = item.replace("LockPicker_", "")
89 # If not possible, show relevant error and break -> just run the tests
90 if fn not in RESULTS:
91 print("Факултетният номер е невалиден. Убеди се, че класът ти "
92 "е във формат LockPicker_FN1234.")
93 break
94 # Get the LockPicker class from the solution
95 LockPicker = getattr(solution, item)
96 try:
97 # Try to unlock the lock
98 test_unlocker(LockPicker(lock))
99 # If successfull, assign correct = True to affect unit tests
100 # and show the results
101 if lock.is_unlocked:
102 correct = True
103 print("Резултат от контролното:\n"
104 f" {RESULTS[fn]['correct']}/25 верни отговора.\n"
105 f" {RESULTS[fn]['points']} точки.")
106 # If the lock is still locked - sorry
107 else:
108 print("Неуспешно отключване.")
109 # Any error that the unlock() method from the solution might throw
110 except Exception:
111 print(f"Грешка при опит за отключване:\n{traceback.format_exc()}")
112 break
113# If there was no break above, no valid class name was found.
114else:
115 print("Не намирам клас с очакваното име. "
116 "Очаквам формат LockPicker_FN1234, "
117 "където FN1234 е валиден ФН на студент от курса.")
118
119
120if __name__ == "__main__":
121 unittest.main()
Георги Кунчев
13.11.2024 19:18След като крайният срок дойде и публикуваме резултатите, ще можете да видите тестове, както винаги.
Този път, обаче, в тях има два импорта, които ще ви гръмнат. Съветвам просто да вземете дефинициите на класа за ключалка и да тествате директно. Ако някой има проблеми, нека пише.
```
import timeout_decorator
from secret_results import RESULTS
```
Иначе, ако махнете таймаут декоратора и дефинирате RESULTS като речник с ключ вашия ФН, зад който има речник с ключове "correct" и "points", зад които има рандом стрингове, всичко трябва да сработи
|
Георги Кунчев
13.11.2024 18:31Да дефинираш клас динамично чрез `type` - не. Напълно валидно.
Да сетваш името в `globals` - бих казал доста неприятно.
Не мисля, че има вариант, в който това да е нужно. Независимо от случая, трябва да има по-адекватен вариант.
За текущия контекс с предизвикателството ви, едва ли.
|
Павел Петков
13.11.2024 17:10Проблем ли е кода да се напише динамично, така че да няма значение какъв е факултетния номер.
```
def unlock(self):
pass
fn = "на някой номера"
globals()[f"LockPicker_{fn}"] = type(
f"LockPicker_{fn}",
(object,),
{
"unlock": lambda self: unlock(self)
"__init__" : lambda self, lock: setattr(self, "lock", lock)
}
)
```
Въпроса ми е този код лоша практика ли е?
|
Виктор Бечев
13.11.2024 15:50Аз ще бъда задник и ще ти я махна поради две причини:
1. Кодът има грешки и не тръгва.
2. Лошото форматиране на блока _(просто сложи всичко в \```, за да се мине с един copy-paste)_.
Ако ги оправиш - ще си я получиш обратно.
|
Георги Кунчев
13.11.2024 15:23@Даниел_Манчевски, давам точка за споделеният ти код, но не давам гаранция, че работи.
|
Даниел Манчевски
13.11.2024 14:50```
class MyTypeError(TypeError):
def __init__(self, position, expected):
self.position = position
self.expected = expected
class MyValueError(ValueError):
def __init__(self, position, expected):
self.position = position
self.expected = expected
class Lock:
def __init__(self, *args):
self.ideal = list(args)
def pick(self, *args):
if len(self.ideal) != len(*args):
raise MyTypeError(None, len(self.ideal))
for i, el in enumerate(*args):
if type(el) is not type(self.ideal[i]):
raise MyTypeError(i + 1, type(self.ideal[i]))
if el != self.ideal[i]:
raise MyValueError(i + 1, self.ideal[i])
return True
lock = Lock("123", 65)
lock_picker = LockPicker_XXX(lock)
print(lock_picker.unlock())
```
|
|
Стефан Шиваров
13.11.2024 12:09Тоест идеята е да пробваме да викаме pick с някакви аргументи, и в зависимост от върнатата грешка да променяме аргументите, и така докато не се отключи?
|
Георги Кунчев
13.11.2024 11:37Няма значение какво връща. Просто след изпълнение трябва да е отключил ключалката, т.е. да е извикал `pick` с правилните аргументи.
|
|