Skip to content

Commit 49c5ffd

Browse files
committed
Write a part of FIFOCache
1 parent 3bdec75 commit 49c5ffd

File tree

8 files changed

+690
-25
lines changed

8 files changed

+690
-25
lines changed

python/cachebox/_cachebox.py

+130-9
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class BaseCacheImpl(typing.Generic[KT, VT]):
3535
"""
3636
This is the base class of all cache classes such as Cache, FIFOCache, ...
3737
"""
38+
3839
pass
3940

4041

@@ -57,13 +58,12 @@ class Cache(BaseCacheImpl[KT, VT]):
5758
"""
5859
A simple cache that has no algorithm; this is only a hashmap.
5960
60-
Cache vs dict:
61-
62-
it is thread-safe and unordered, while dict isn't thread-safe and ordered (Python 3.6+).
63-
it uses very lower memory than dict.
64-
it supports useful and new methods for managing memory, while dict does not.
65-
it does not support popitem, while dict does.
66-
You can limit the size of Cache, but you cannot for dict.
61+
`Cache` vs `dict`:
62+
- it is thread-safe and unordered, while `dict` isn't thread-safe and ordered (Python 3.6+).
63+
- it uses very lower memory than `dict`.
64+
- it supports useful and new methods for managing memory, while `dict` does not.
65+
- it does not support popitem, while `dict` does.
66+
- You can limit the size of Cache, but you cannot for `dict`.
6767
"""
6868

6969
def __init__(
@@ -165,14 +165,16 @@ def __setitem__(self, key: KT, value: VT) -> None:
165165
self.insert(key, value)
166166

167167
def __getitem__(self, key: KT) -> VT:
168-
val = self.get(key, _sential)
168+
val = self._raw.get(key, _sential)
169169
if val is _sential:
170170
raise KeyError(key)
171171

172172
return val
173173

174174
def __delitem__(self, key: KT) -> None:
175-
self._raw.remove(key)
175+
val = self._raw.pop(key, _sential)
176+
if val is _sential:
177+
raise KeyError(key)
176178

177179
def __eq__(self, other) -> bool:
178180
return self._raw == other
@@ -232,3 +234,122 @@ def __repr__(self) -> str:
232234
self._raw.maxsize(),
233235
_items_to_str(self._raw.items(), len(self._raw)),
234236
)
237+
238+
239+
class FIFOCache(BaseCacheImpl[KT, VT]):
240+
def __init__(
241+
self,
242+
maxsize: int,
243+
iterable: typing.Union["Cache", dict, tuple, typing.Generator, None] = None,
244+
*,
245+
capacity: int = 0,
246+
) -> None:
247+
self._raw = _core.FIFOCache(maxsize, capacity=capacity)
248+
249+
if iterable is not None:
250+
self.update(iterable)
251+
252+
@property
253+
def maxsize(self) -> int:
254+
return self._raw.maxsize()
255+
256+
def capacity(self) -> int:
257+
return self._raw.capacity()
258+
259+
def __len__(self) -> int:
260+
return len(self._raw)
261+
262+
def __sizeof__(self):
263+
return self._raw.__sizeof__()
264+
265+
def __contains__(self, key: KT) -> bool:
266+
return key in self._raw
267+
268+
def __bool__(self) -> bool:
269+
return not self.is_empty()
270+
271+
def is_empty(self) -> bool:
272+
return self._raw.is_empty()
273+
274+
def is_full(self) -> bool:
275+
return self._raw.is_full()
276+
277+
def insert(self, key: KT, value: VT) -> typing.Optional[VT]:
278+
return self._raw.insert(key, value)
279+
280+
def get(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
281+
return self._raw.get(key, default)
282+
283+
def pop(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
284+
return self._raw.pop(key, default)
285+
286+
def setdefault(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
287+
return self._raw.setdefault(key, default)
288+
289+
def popitem(self) -> typing.Tuple[KT, VT]:
290+
return self._raw.popitem()
291+
292+
def drain(self, n: int) -> int:
293+
if n == 0:
294+
return 0
295+
296+
for i in range(n):
297+
try:
298+
self._raw.popitem()
299+
except KeyError:
300+
return i
301+
302+
return i
303+
304+
def update(self, iterable: typing.Union["Cache", dict, tuple, typing.Generator]) -> None:
305+
if hasattr(iterable, "items"):
306+
iterable = iterable.items()
307+
308+
self._raw.update(iterable)
309+
310+
def __setitem__(self, key: KT, value: VT) -> None:
311+
self.insert(key, value)
312+
313+
def __getitem__(self, key: KT) -> VT:
314+
val = self._raw.get(key, _sential)
315+
if val is _sential:
316+
raise KeyError(key)
317+
318+
return val
319+
320+
def __delitem__(self, key: KT) -> None:
321+
val = self._raw.pop(key, _sential)
322+
if val is _sential:
323+
raise KeyError(key)
324+
325+
def __eq__(self, other) -> bool:
326+
return self._raw == other
327+
328+
def __ne__(self, other) -> bool:
329+
return self._raw != other
330+
331+
def shrink_to_fit(self) -> None:
332+
self._raw.shrink_to_fit()
333+
334+
def clear(self, *, reuse: bool = False) -> None:
335+
self._raw.clear(reuse)
336+
337+
def items(self) -> IteratorView[typing.Tuple[KT, VT]]:
338+
return IteratorView(self._raw.items(), lambda x: x)
339+
340+
def keys(self) -> IteratorView[KT]:
341+
return IteratorView(self._raw.items(), lambda x: x[0])
342+
343+
def values(self) -> IteratorView[VT]:
344+
return IteratorView(self._raw.items(), lambda x: x[1])
345+
346+
def __iter__(self) -> IteratorView[KT]:
347+
return self.keys()
348+
349+
def __repr__(self) -> str:
350+
return "{}[{}/{}]({})".format(
351+
type(self).__name__,
352+
len(self._raw),
353+
self._raw.maxsize(),
354+
_items_to_str(self._raw.items(), len(self._raw)),
355+
)

src/bridge/cache.rs

+3-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
//! implement Cache, a simple cache without any algorithms and policies
2-
3-
use crate::common::{Entry, ObservedIterator, PreHashObject};
1+
use crate::common::Entry;
2+
use crate::common::ObservedIterator;
3+
use crate::common::PreHashObject;
44

55
/// A simple cache that has no algorithm; this is only a hashmap.
66
///
@@ -101,19 +101,6 @@ impl Cache {
101101
}
102102
}
103103

104-
fn remove(&self, py: pyo3::Python<'_>, key: pyo3::PyObject) -> pyo3::PyResult<()> {
105-
let key = PreHashObject::from_pyobject(py, key)?;
106-
let mut lock = self.raw.lock();
107-
108-
match lock.entry(py, &key)? {
109-
Entry::Occupied(entry) => {
110-
entry.remove();
111-
Ok(())
112-
}
113-
Entry::Absent(_) => Err(pyo3::PyErr::new::<pyo3::exceptions::PyKeyError, _>(key.obj)),
114-
}
115-
}
116-
117104
fn update(
118105
slf: pyo3::PyRef<'_, Self>,
119106
py: pyo3::Python<'_>,

0 commit comments

Comments
 (0)