Initial release

This commit is contained in:
2026-03-05 14:02:00 +01:00
parent 5e02361649
commit 2df3ed5835
5 changed files with 430 additions and 0 deletions
View File
+153
View File
@@ -0,0 +1,153 @@
# simple-cache
A simple library for caching data.
## Installation
You can install the library using `pip`:
```bash
pip install simple-cache --index-url https://repo.jcloud-services.ddns.net/simple/
```
## Full documentation
### `CacheItem`
A cache item.
#### Params
- `value` (type: `Any`): The value of the item
- `creation_time` (type: `Union[int, float]`): The time the item was created
#### Exceptions
No exceptions.
#### Methods
No methods.
### `Cache`
A thread-safe cache.
#### Parameters
- `generate_value_func` (type: `Optional[FunctionType]`, default: `None`): The function to generate a new value. It has to take one positional argument.
- `max_size` (type: `int`, default: `256`): The maximum size of the cache.
- `ttl` (type: `int`, default: `120`): The TTL (time to live) in seconds.
#### Exceptions
No exceptions.
#### Methods
##### `set`
Sets or updates an item (thread-safe).
###### Parameters
- `key` (type: `str`): The key.
- `value` (type: `Any`): The value.
###### Exceptions
No exceptions.
###### Returns
`None`
##### `delete`
Deletes an item (thread-safe).
###### Parameters
- `key` (type: `str`): The key.
###### Exceptions
- `KeyError`: If the item does not exist.
###### Returns
`None`
##### `get`
Returns an item (thread-safe).
If the item does not exist, it uses ``self.generate_value_func`` to
get the value. If no ``generate_value_func`` was specified in the
constructor of the ``Cache`` object, the value will be set to ``None``.
###### Parameters
- `key` (type: `str`): The key.
###### Exceptions
No exceptions.
###### Returns
The value.
Type: `Any`
##### `clean_item`
Removes an item if it is expired (thread-safe).
###### Parameters
- `key` (type: `key`): The key
###### Exceptions
No exceptions.
###### Returns
`None`
##### `clear_expired`
Removes all expired items (thread-safe).
###### Parameters
No parameters.
###### Exceptions
No Exceptions.
###### Returns
`None`
##### `clear`
Removes all items (thread-safe).
###### Parameters
No parameters.
###### Exceptions
No Exceptions.
###### Returns
`None`
## Changelog
### Version 0.1.0
- initial release
- thread safety
- set, update, delete and get items
- TTL
- support for specifying a function to generate new values
- deletion of expired items when getting them
- deletion of the least used item when a new item is set and the maximum size is reached
- support for clearing the cache
+9
View File
@@ -0,0 +1,9 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "simple-cache"
version = "0.1.0"
description = "A simple library for caching data."
license = "Apache-2.0"
+24
View File
@@ -0,0 +1,24 @@
# Copyright 2026 jCloud Services GbR
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''
A simple library for caching data.
'''
from ._core import *
from ._core import __all__ as _core__all__
__all__ = [
*_core__all__
]
+244
View File
@@ -0,0 +1,244 @@
from heapq import nsmallest
import time
import threading
from types import FunctionType
from typing import Optional, Any, Union
__all__ = [
'CacheItem',
'Cache'
]
def _genval(_) -> None:
'''
The fallback function for the ``generate_value_func`` argument for the
constructor of ``Cache``. It simply returns ``None``, without performing any
additional operations.
'''
pass
class CacheItem:
def __init__(self, value: Any, creation_time: Union[int, float]):
'''
A cache item.
:param value: The value of the item
:type value: Any
:param creation_time: The time the item was created
:type creation_time: Union[int, float]
'''
self.value = value
self.creation_time = creation_time
class Cache:
def __init__(self, generate_value_func: Optional[FunctionType] = None, max_size: int = 256, ttl: int = 120):
'''
A thread-safe cache.
:param generate_value_func: The function to generate a new value. It has to take one positional argument.
:type generate_value_func: Optional[FunctionType]
:param max_size: The maximum size of the cache.
:type max_size: int
:param ttl: The TTL (time to live) in seconds.
:type ttl: int
'''
if generate_value_func is None:
generate_value_func = _genval
self._cache = dict()
self.max_size = max_size
self.ttl = ttl
self.generate_value_func = generate_value_func
self._accesses = dict()
self._all_accesses = 0
self._lock = threading.RLock()
def _get_least_used_keys(self, number_of_keys: int = 1) -> list:
'''
Returns the keys that are used the least. The number of keys that are
returned can be specified with the parameter ``number_of_keys``.
:param number_of_keys: The number of keys to return.
:type number_of_keys: int
:return: The ``number_of_keys``-th least used keys.
:rtype: list
'''
if not self._accesses or number_of_keys <= 0:
return []
return [k for k in nsmallest(number_of_keys, self._accesses, key = lambda k: self._accesses[k])]
def _set(self, key: str, value: Any) -> None:
'''
Sets or updated an item.
:param key: The key.
:type key: str
:param value: The value.
:type value: Any
'''
new_key = key not in self._cache.keys()
if len(self._cache) >= self.max_size and new_key:
self._clear_expired()
if len(self._cache) >= self.max_size:
least_used_keys = self._get_least_used_keys(1)
if least_used_keys:
self._delete(least_used_keys[0])
self._accesses[key] = 0 if new_key else self._accesses[key]
self._cache[key] = CacheItem(value, time.time())
def _delete(self, key: str) -> None:
'''
Deletes an item.
:param key: The key.
:type key: str
:raises KeyError: If the item does not exist.
'''
if key in self._cache:
self._all_accesses -= self._accesses[key]
del self._cache[key]
del self._accesses[key]
else:
raise KeyError(key)
def _get(self, key: str) -> Any:
'''
Returns an item.
If the item does not exist, it uses ``self.generate_value_func`` to
get the value. If no ``generate_value_func`` was specified in the
constructor of the ``Cache`` object, the value will be set to ``None``.
:param key: The key.
:type key: str
:return: The value.
:rtype: Any
'''
self._clean_item(key)
if key in self._cache:
value = self._cache[key].value
else:
value = self.generate_value_func(key)
self._set(key, value)
self._accesses[key] += 1
self._all_accesses += 1
return value
def _clean_item(self, key: str) -> None:
'''
Removes an item if it is expired.
:param key: The key.
:type key: str
'''
if key in self._cache:
ct = self._cache[key].creation_time
if time.time() - ct > self.ttl:
self._delete(key)
def _clear_expired(self) -> None:
'''
Removes all expired items.
'''
expired = [k for k, v in self._cache.items() if time.time() - v.creation_time > self.ttl]
for e in expired:
self._delete(e)
def _clear(self) -> None:
'''
Removes all items.
'''
self._all_accesses = 0
self._accesses = dict()
self._cache = dict()
def set(self, key: str, value: Any) -> None:
'''
Sets or updates an item (thread-safe).
:param key: The key.
:type key: str
:param value: The value.
:type value: Any
'''
with self._lock:
self._set(key, value)
def delete(self, key: str) -> None:
'''
Deletes an item (thread-safe).
:param key: The key.
:type key: str
:raises KeyError: If the item does not exist.
'''
with self._lock:
self._delete(key)
def get(self, key: str) -> Any:
'''
Returns an item (thread-safe).
If the item does not exist, it uses ``self.generate_value_func`` to
get the value. If no ``generate_value_func`` was specified in the
constructor of the ``Cache`` object, the value will be set to ``None``.
:param key: The key.
:type key: str
:return: The value.
:rtype: Any
'''
with self._lock:
return self._get(key)
def clean_item(self, key: str) -> None:
'''
Removes an item if it is expired (thread-safe).
:param key: The key.
:type key: str
'''
with self._lock:
self._clean_item(key)
def clear_expired(self) -> None:
'''
Removes all expired items (thread-safe).
'''
with self._lock:
self._clear_expired()
def clear(self) -> None:
'''
Removes all items (thread-safe).
'''
with self._lock:
self._clear()
def __getitem__(self, key: str) -> Any:
return self.get(key)
def __setitem__(self, key: str, value: Any) -> None:
self.set(key, value)
def __delitem__(self, key: str) -> None:
self.delete(key)