eric7/E5Utilities/E5Cache.py

branch
eric7
changeset 8312
800c432b34c8
parent 8207
d359172d11be
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2017 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing classes used for caching objects.
8 """
9
10 from PyQt5.QtCore import QDateTime, QTimer
11
12
13 class E5Cache:
14 """
15 Class implementing a LRU cache of a specific size.
16
17 If the maximum number of entries is exceeded, the least recently used item
18 is removed from the cache. A cache hit moves the entry to the front of the
19 cache.
20 """
21 def __init__(self, size=100):
22 """
23 Constructor
24
25 @param size maximum number of entries that may be stored in the cache
26 @type int
27 @exception ValueError raised to indicate an illegal 'size' parameter
28 """
29 if size < 0:
30 raise ValueError("'size' parameter must be positive.")
31
32 self.__size = size
33
34 # internal objects
35 self.__keyList = []
36 self.__store = {} # stores the cache entries
37 self.__accesStore = {} # stores the last access date and times
38 self.__hits = 0
39 self.__misses = 0
40 self.__maxsize = 0
41 self.__maxCacheTime = 0 # 0 seconds means aging is disabled
42
43 self.__cacheTimer = QTimer()
44 self.__cacheTimer.setSingleShot(True)
45 self.__cacheTimer.timeout.connect(self.__pruneCache)
46
47 def __moveLast(self, key):
48 """
49 Private method to move a cached item to the MRU position.
50
51 @param key key of the item to be retrieved
52 @type any hashable type that can be used as a dict key
53 """
54 self.__keyList.remove(key)
55 self.__keyList.append(key)
56
57 def __adjustToSize(self):
58 """
59 Private method to adjust the cache to its size.
60 """
61 if self.__size:
62 removeList = self.__keyList[:-self.__size]
63 self.__keyList = self.__keyList[-self.__size:]
64 for key in removeList:
65 del self.__store[key]
66 del self.__accesStore[key]
67 else:
68 self.reset()
69
70 def getSize(self):
71 """
72 Public method to get the maximum size of the cache.
73
74 @return maximum number of entries of the cache
75 @rtype int
76 """
77 return self.__size
78
79 def setSize(self, newSize):
80 """
81 Public method to change the maximum size of the cache.
82
83 @param newSize maximum number of entries that may be stored in the
84 cache
85 @type int
86 """
87 if newSize >= 0:
88 self.__size = newSize
89 self.__adjustToSize()
90
91 def getMaximumCacheTime(self):
92 """
93 Public method to get the maximum time entries may exist in the cache.
94
95 @return maximum cache time in seconds
96 @rtype int
97 """
98 return self.__maxCacheTime
99
100 def setMaximumCacheTime(self, time):
101 """
102 Public method to set the maximum time entries may exist in the cache.
103
104 @param time maximum cache time in seconds
105 @type int
106 """
107 if time != self.__maxCacheTime:
108 self.__cacheTimer.stop()
109 self.__pruneCache()
110 self.__maxCacheTime = time
111 if self.__maxCacheTime > 0:
112 self.__cacheTimer.setInterval(self.__maxCacheTime * 1000)
113 self.__cacheTimer.start()
114
115 def get(self, key):
116 """
117 Public method to get an entry from the cache given its key.
118
119 If the key is present in the cache, it is moved to the MRU position.
120
121 @param key key of the item to be retrieved
122 @type any hashable type that can be used as a dict key
123 @return cached item for the given key or None, if the key is not
124 present
125 @rtype object or None
126 """
127 if key in self.__store:
128 self.__hits += 1
129 self.__moveLast(key)
130 self.__accesStore[key] = QDateTime.currentDateTimeUtc()
131 return self.__store[key]
132 else:
133 self.__misses += 1
134 return None
135
136 def add(self, key, item):
137 """
138 Public method to add an item to the cache.
139
140 If the key is already in use, the cached item is replaced by the new
141 one given and is moved to the MRU position
142
143 @param key key of the item to be retrieved
144 @type any hashable type that can be used as a dict key
145 @param item item to be cached under the given key
146 @type object
147 """
148 if key in self.__store:
149 self.__moveLast(key)
150 else:
151 self.__keyList.append(key)
152 self.__store[key] = item
153 self.__accesStore[key] = QDateTime.currentDateTimeUtc()
154
155 self.__adjustToSize()
156
157 self.__maxsize = max(self.__maxsize, len(self.__keyList))
158
159 def remove(self, key):
160 """
161 Public method to remove an item from the cache.
162
163 @param key key of the item to be retrieved
164 @type any hashable type that can be used as a dict key
165 """
166 if key in self.__store:
167 del self.__store[key]
168 del self.__accesStore[key]
169 self.__keyList.remove(key)
170
171 def clear(self):
172 """
173 Public method to clear the cache.
174 """
175 self.__keyList = []
176 self.__store = {}
177 self.__accesStore = {}
178
179 def reset(self):
180 """
181 Public method to reset the cache.
182
183 This is like clear() but sets the various counters to their initial
184 value as well.
185 """
186 self.clear()
187 self.__hits = 0
188 self.__misses = 0
189 self.__maxsize = 0
190
191 def length(self):
192 """
193 Public method to get the current length of the cache.
194
195 @return current length of the cache
196 @rtype int
197 """
198 return len(self.__keyList)
199
200 def info(self):
201 """
202 Public method to get some information about the cache.
203
204 @return dictionary containing the cache info
205 @rtype dict (with keys "hits", "misses", "maxsize", "currsize")
206 """
207 return {
208 "hits": self.__hits,
209 "misses": self.__misses,
210 "maxsize": self.__maxsize,
211 "currsize": self.length(),
212 }
213
214 def __pruneCache(self):
215 """
216 Private slot to prune outdated cache entries and restart the timer.
217 """
218 if self.__maxCacheTime > 0:
219 current = QDateTime.currentDateTimeUtc()
220
221 keysToBeDeleted = []
222 for key, lastAccessTime in self.__accesStore.items():
223 if lastAccessTime.secsTo(current) > self.__maxCacheTime:
224 keysToBeDeleted.append(key)
225 for key in keysToBeDeleted:
226 self.remove(key)
227
228 self.__cacheTimer.start()

eric ide

mercurial