1import re
2
3class Singleton(type):
4 objects = {}
5 def __call__(cls, *args, **kwargs):
6 if cls not in cls.objects:
7 cls.objects[cls] = super().__call__(*args, **kwargs)
8 return cls.objects[cls]
9
10class Santa(metaclass=Singleton):
11 _year = 0
12 _MAX_CHILD_AGE = 5
13
14 def __init__(self):
15 self._wishes = {}
16 self._curr_year_wishes = []
17
18 @staticmethod
19 def get_id_from_a_str(text: str) -> int:
20 match = re.search(r"^\s*(\d+)\s*$", text, re.MULTILINE)
21 if match:
22 return int(match.group(1))
23 return None
24
25 @staticmethod
26 def get_wish_from_a_str(text: str) -> str:
27 match = re.search(r'["\']([a-zA-Z0-9 ]+)["\']', text)
28 if match:
29 return match.group(1)
30 return None
31
32 def __call__(self, kid, letter):
33 wish = self.get_wish_from_a_str(letter)
34 self._wishes[id(kid)] = wish
35 self._curr_year_wishes.append(wish)
36
37 def __matmul__(self, other):
38 child_id = self.get_id_from_a_str(other)
39 wish = self.get_wish_from_a_str(other)
40 self._wishes[child_id] = wish
41 self._curr_year_wishes.append(wish)
42
43 def __iter__(self):
44 return iter(self._curr_year_wishes)
45
46 def xmas(self):
47 Santa._year += 1
48 if not self._curr_year_wishes:
49 self._wishes.clear()
50 self._curr_year_wishes.clear()
51 print("The magic is dead :(")
52 return
53
54 gift_counter = {}
55 for _, gift in self._wishes.items():
56 gift_counter[gift] = gift_counter.get(gift, 0) + 1
57 most_wanted = max(gift_counter, key=gift_counter.get)
58
59 for _, child in Kid.all_children.items():
60 if Santa._year - Kid.start_year[id(child)] >= Santa._MAX_CHILD_AGE:
61 continue
62 if child.naughty:
63 child('coal')
64 print("receives: coal")
65 else:
66 gift = self._wishes.get(id(child), most_wanted)
67 child(gift)
68 print(f"receives: {gift}")
69
70 self._wishes.clear()
71 self._curr_year_wishes.clear()
72
73
74class Kid(type):
75 all_children = {}
76 start_year = {}
77
78 def __new__(cls, name, bases, dct):
79 if "__call__" not in dct:
80 raise NotImplementedError("I love you, but I need a __call__ method!... P.S(5 hours later) I hate you this hw almost killed me <3")
81 for name, value in dct.items():
82 if not name.startswith('__') and callable(value):
83 dct[name] = cls.hadle_exceptions(value)
84 return super().__new__(cls, name, bases, dct)
85
86 def __call__(cls, *args, **kwargs):
87 instance = super().__call__(*args, **kwargs)
88 instance.naughty = False
89 cls.all_children[id(instance)] = instance
90 cls.start_year[id(instance)] = Santa._year
91 return instance
92
93 def hadle_exceptions(method):
94 def wrapper(self, *args, **kwargs):
95 try:
96 return method(self, *args, **kwargs)
97 except Exception:
98 self.naughty = True
99 raise
100 return wrapper
101
102##TESTS
103# class Child(metaclass=Kid):
104# def __call__(self, present):
105# pass
106
107# class BulgarianKid(metaclass=Kid):
108# def __call__(self, present):
109# pass
110
111# def be_naughty(self):
112# raise RuntimeError('Няма да си изям зеленчуците!')
113
114# class ChineseKid(metaclass=Kid):
115# def __call__(self, present):
116# pass
117
118# santa = Santa()
119
120# goshko = BulgarianKid()
121# toshko = BulgarianKid()
122# chen = ChineseKid()
123
124# # Santa receiving wishes
125# santa(goshko, "'gun'")
126# santa @ f"'gun'\n{id(toshko)}"
127
128# # Handling naughtiness for Goshko
129# try:
130# goshko.be_naughty()
131# except:
132# pass
133
134# # Process gifts
135# santa.xmas()
136# # Expected output:
137# # Гошко получава coal
138# # Тошко получава gun
139# # # Chen получава gun
140
141
142# test2
143# santa = Santa()
144# santa @ "През по-голямата 2945526885456 част от тази година бях много послушен! Понякога бях само леко но т'ва не е никакъв проблем, защото всички сме хора и понякога искаме да теглим една '...' на всичко. Life is tough! Моля те донеси ми 'doll'."
145# kid1 = Child()
146# santa(kid1, "През по-голямата 2945526885456 част от тази година бях много послушен! Понякога бях само леко но т'ва не е никакъв проблем, защото всички сме хора и понякога искаме да теглим една '...' на всичко. Life is tough! Моля те донеси ми 'ball'.")
147# for present in santa:
148# print(present)
149
150#test3
151# santa1 = Santa()
152# santa2 = Santa()
153# print(santa1 is santa2)
...FF..F........F
Stdout:
receives: coal
..F
Stdout:
receives: toy1
The magic is dead :(
The magic is dead :(
The magic is dead :(
======================================================================
FAIL: test_call_and_mail_same_kid (test.TestSanta.test_call_and_mail_same_kid)
Test that calls and mails work for the same kid.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 71, in test_call_and_mail_same_kid
self.assertEqual(list(self.santa), ['toy1'])
AssertionError: Lists differ: ['toy1', 'toy1'] != ['toy1']
First list contains 1 additional elements.
First extra element 1:
'toy1'
- ['toy1', 'toy1']
+ ['toy1']
======================================================================
FAIL: test_iterable (test.TestSanta.test_iterable)
Ensure Santa can be iterated multiple times including overwriting presents.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 129, in test_iterable
self.assertEqual(list(self.santa), ['something', 'something else'])
AssertionError: Lists differ: ['something', 'something not used', 'something else'] != ['something', 'something else']
First differing element 1:
'something not used'
'something else'
First list contains 1 additional elements.
First extra element 2:
'something else'
- ['something', 'something not used', 'something else']
+ ['something', 'something else']
======================================================================
FAIL: test_santa_gift_order (test.TestSanta.test_santa_gift_order)
Test ordering of the Santa iterator.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 260, in test_santa_gift_order
self.assertEqual(list(self.santa), ["toy2v2", "toy3", "toy1"])
AssertionError: Lists differ: ['toy2', 'toy3', 'toy1', 'toy2v2'] != ['toy2v2', 'toy3', 'toy1']
First differing element 0:
'toy2'
'toy2v2'
First list contains 1 additional elements.
First extra element 3:
'toy2v2'
- ['toy2', 'toy3', 'toy1', 'toy2v2']
+ ['toy2v2', 'toy3', 'toy1']
======================================================================
FAIL: test_xmass_private_with_error (test.TestSanta.test_xmass_private_with_error)
Test a Christmas with not-so-naughty kids.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 240, in test_xmass_private_with_error
self.assertEqual(kid1.SECRET_PRESENT, 'sirenie')
AssertionError: 'coal' != 'sirenie'
- coal
+ sirenie
Stdout:
receives: coal
======================================================================
FAIL: test_xmass_years_under_5 (test.TestSanta.test_xmass_years_under_5)
Test with passing years with a kid under 5 years old.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 201, in test_xmass_years_under_5
self.assertEqual(kid1.SECRET_PRESENT, 'toy2')
AssertionError: None != 'toy2'
Stdout:
receives: toy1
The magic is dead :(
The magic is dead :(
The magic is dead :(
----------------------------------------------------------------------
Ran 20 tests in 0.027s
FAILED (failures=5)
20.12.2024 10:15
20.12.2024 10:17
20.12.2024 10:16