1from abc import ABC, abstractmethod
2from collections import deque
3
4
5class LimitPolicy(ABC):
6 @abstractmethod
7 def validate(self, candidate, current):
8 pass
9
10
11class ObjectLimitPolicy(LimitPolicy):
12 def __init__(self, limit):
13 self.__limit = limit
14
15 def validate(self, candidate, current):
16 if len(current) + 1 > self.__limit:
17 raise ValueError(f"Cannot insert item! Object limit [ {self.__limit} ] would be exceeded!")
18
19
20class SizeLimitPolicy(LimitPolicy):
21 def __init__(self, limit):
22 self.__limit = limit
23
24 def validate(self, candidate, current):
25 if sum(len(item) for item in current) + len(candidate) > self.__limit:
26 raise ValueError(f"Cannot insert item! Size limit [ {self.__limit} ] would be exceeded!")
27
28
29class SlotStorage:
30 """ Holds the actual data for one slot on one instance """
31 __slots__ = ("__queue", "__policies")
32
33 def __init__(self, policies):
34 self.__queue = deque()
35 self.__policies = policies
36
37 def __len__(self):
38 return len(self.__queue)
39
40 def __iter__(self):
41 return iter(self.__queue)
42
43 def push(self, item):
44 for policy in self.__policies:
45 policy.validate(item, self.__queue)
46 self.__queue.append(item)
47
48 def pop(self):
49 if self.__queue:
50 self.__queue.popleft()
51
52 def snapshot(self):
53 return tuple(self.__queue)
54
55
56class Slot:
57 """ Connects each instance ot its SlotStorage """
58 __slots__ = ("__policies", "__attr")
59
60 def __init__(self, *, object_limit=None, size_limit=None):
61 candidates = ((object_limit, ObjectLimitPolicy), (size_limit, SizeLimitPolicy))
62 self.__policies = tuple(cls(limit) for limit, cls in candidates if limit is not None)
63 self.__attr = ""
64
65 def __set_name__(self, owner, name):
66 self.__attr = f"_slot_storage_{name}"
67
68 def __get__(self, instance, owner):
69 if instance is None: # Class access
70 return self
71
72 return self.__storage(instance).snapshot() # Instance access
73
74 def __set__(self, instance, value):
75 self.__storage(instance).push(value)
76
77 def __delete__(self, instance):
78 self.__storage(instance).pop()
79
80 def __storage(self, instance):
81 return instance.__dict__.setdefault(self.__attr, SlotStorage(self.__policies))
.......
----------------------------------------------------------------------
Ran 7 tests in 0.000s
OK