Skip to content

Commit e73858c

Browse files
committed
1 parent c5c3456 commit e73858c

File tree

7 files changed

+225
-91
lines changed

7 files changed

+225
-91
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020 Rajat Singh
3+
Copyright (c) 2020 doubledare704
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# async-cache
2+
> A high-performance async caching solution for Python
3+
4+
[![PyPI version](https://img.shields.io/pypi/v/async-cache.svg)](https://pypi.python.org/pypi/async-cache)
5+
[![Downloads](https://static.pepy.tech/personalized-badge/async-cache?period=total&units=international_system&left_color=black&right_color=blue&left_text=Downloads)](https://pepy.tech/project/async-cache)
6+
[![Security Rating](https://snyk.io/advisor/python/async-cache/badge.svg)](https://snyk.io/advisor/python/async-cache)
7+
8+
A lightweight, efficient caching solution designed specifically for asyncio applications. Supports both LRU (Least Recently Used) and TTL (Time To Live) caching strategies with a clean, decorator-based API.
9+
10+
## Features
11+
12+
- 🚀 **Async-First**: Built specifically for asyncio applications
13+
- 🔄 **Multiple Cache Types**:
14+
- LRU (Least Recently Used) cache
15+
- TTL (Time To Live) cache
16+
- 🎯 **Flexible Key Generation**: Works with primitive types, custom objects, and ORM models
17+
- 🛠 **Configurable**: Adjustable cache size and TTL duration
18+
- 🧹 **Cache Management**: Clear cache on demand
19+
- 💡 **Smart Argument Handling**: Skip specific arguments in cache key generation
20+
- 🔍 **Cache Bypass**: Ability to bypass cache for specific calls
21+
22+
## Installation
23+
24+
```bash
25+
pip install async-cache
26+
```
27+
28+
## Basic Usage
29+
30+
### LRU Cache
31+
32+
The LRU cache maintains a fixed number of items, removing the least recently used item when the cache is full.
33+
34+
```python
35+
from cache import AsyncLRU
36+
37+
@AsyncLRU(maxsize=128)
38+
async def get_user_data(user_id: int) -> dict:
39+
# Expensive database operation
40+
data = await db.fetch_user(user_id)
41+
return data
42+
```
43+
44+
### TTL Cache
45+
46+
The TTL cache automatically expires entries after a specified time period.
47+
48+
```python
49+
from cache import AsyncTTL
50+
51+
@AsyncTTL(time_to_live=60, maxsize=1024)
52+
async def get_weather(city: str) -> dict:
53+
# External API call
54+
weather = await weather_api.get_data(city)
55+
return weather
56+
```
57+
58+
## Advanced Usage
59+
60+
### Working with Custom Objects
61+
62+
The cache works seamlessly with custom objects and ORM models:
63+
64+
```python
65+
from dataclasses import dataclass
66+
from cache import AsyncLRU
67+
68+
@dataclass
69+
class UserFilter:
70+
age: int
71+
country: str
72+
status: str
73+
74+
@AsyncLRU(maxsize=128)
75+
async def filter_users(filter_params: UserFilter) -> list:
76+
# Complex filtering operation
77+
users = await db.filter_users(
78+
age=filter_params.age,
79+
country=filter_params.country,
80+
status=filter_params.status
81+
)
82+
return users
83+
```
84+
85+
### Skipping Arguments
86+
87+
Useful for methods where certain arguments shouldn't affect the cache key:
88+
89+
```python
90+
from cache import AsyncTTL
91+
92+
class UserService:
93+
@AsyncTTL(time_to_live=300, maxsize=1000, skip_args=1)
94+
async def get_user_preferences(self, user_id: int) -> dict:
95+
# 'self' is skipped in cache key generation
96+
return await self.db.get_preferences(user_id)
97+
```
98+
99+
### Cache Management
100+
101+
#### Bypassing Cache
102+
103+
```python
104+
# Normal cached call
105+
result = await get_user_data(123)
106+
107+
# Force fresh data
108+
fresh_result = await get_user_data(123, use_cache=False)
109+
```
110+
111+
#### Clearing Cache
112+
113+
```python
114+
# Clear the entire cache
115+
get_user_data.cache_clear()
116+
```
117+
118+
## Performance Considerations
119+
120+
- The LRU cache is ideal for frequently accessed data with no expiration requirements
121+
- The TTL cache is perfect for data that becomes stale after a certain period
122+
- Choose `maxsize` based on your memory constraints and data access patterns
123+
- Consider using `skip_args` when caching class methods to avoid instance-specific caching
124+
125+
## Contributing
126+
127+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
128+
129+
## License
130+
131+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

README.rst

Lines changed: 0 additions & 84 deletions
This file was deleted.

cache/lru.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections import OrderedDict
2+
from copy import deepcopy
23

34

45
class LRU(OrderedDict):
@@ -7,12 +8,12 @@ def __init__(self, maxsize, *args, **kwargs):
78
super().__init__(*args, **kwargs)
89

910
def __getitem__(self, key):
10-
value = super().__getitem__(key)
11+
value = deepcopy(super().__getitem__(key))
1112
self.move_to_end(key)
1213
return value
1314

1415
def __setitem__(self, key, value):
15-
super().__setitem__(key, value)
16+
super().__setitem__(key, deepcopy(value))
1617
if self.maxsize and len(self) > self.maxsize:
1718
oldest = next(iter(self))
1819
del self[oldest]

pyproject.toml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
[build-system]
2+
requires = ["setuptools>=79.0"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "async-cache"
7+
version = "1.1.1"
8+
description = "A high-performance async caching solution for Python"
9+
authors = [
10+
{name = "Rajat Singh", email = "iamsinghrajat@gmail.com"},
11+
]
12+
readme = "README.md"
13+
requires-python = ">=3.3"
14+
keywords = ["asyncio", "lru", "cache", "async", "cache", "lru-cache", "ttl"]
15+
license = {text = "MIT"}
16+
classifiers = [
17+
"Programming Language :: Python :: 3",
18+
"License :: OSI Approved :: MIT License",
19+
"Operating System :: OS Independent",
20+
]
21+
22+
[tool.ruff]
23+
line-length = 88
24+
target-version = "py38"
25+
26+
# Enable all rules by default
27+
select = ["ALL"]
28+
ignore = [
29+
"D", # Ignore docstring rules
30+
"ANN", # Ignore type annotation rules
31+
"ERA", # Ignore eradicate rules
32+
"UP037", # Ignore quotes in type annotations
33+
"PLR0913", # Ignore too many arguments
34+
]
35+
36+
[tool.ruff.format]
37+
quote-style = "double"
38+
indent-style = "space"
39+
skip-magic-trailing-comma = false
40+
line-ending = "auto"
41+
42+
[tool.pyright]
43+
include = ["cache", "tests"]
44+
exclude = ["**/node_modules", "**/__pycache__"]
45+
pythonVersion = "3.8"
46+
typeCheckingMode = "basic"
47+
reportMissingImports = true
48+
reportMissingTypeStubs = false
49+
reportUnknownMemberType = false
50+
reportUnknownVariableType = false
51+
reportUnknownArgumentType = false
52+
53+
[tool.pytest.ini_options]
54+
minversion = "8.0.0"
55+
addopts = """
56+
--strict-markers
57+
--strict-config
58+
--cov=cache
59+
--cov-report=term-missing
60+
--cov-report=xml
61+
--cov-report=html
62+
-v
63+
"""
64+
testpaths = ["tests"]
65+
python_files = ["*_test.py"]
66+
asyncio_mode = "auto"
67+
68+
[tool.coverage.run]
69+
branch = true
70+
source = ["cache"]
71+
72+
[tool.coverage.report]
73+
exclude_lines = [
74+
"pragma: no cover",
75+
"def __repr__",
76+
"if __name__ == .__main__.:",
77+
"raise NotImplementedError",
78+
"if TYPE_CHECKING:",
79+
]

requirements-dev.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
ruff==0.11.6
2+
pyright==1.1.399
3+
4+
pytest==8.3.5
5+
pytest-asyncio==0.26.0
6+
pytest-cov==6.1.1

setup.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import setuptools
22

3-
with open("README.rst", "r") as fh:
4-
long_description = fh.read()
5-
63
setuptools.setup(
74
name="async-cache",
85
version="1.1.1",
96
author="Rajat Singh",
107
author_email="iamsinghrajat@gmail.com",
118
description="An asyncio Cache",
12-
long_description=long_description,
9+
long_description="""
10+
# A high-performance async caching solution for Python
11+
A lightweight, efficient caching solution designed specifically for asyncio applications.
12+
Supports both LRU (Least Recently Used) and TTL (Time To Live) caching strategies with a clean, decorator-based API.
13+
""",
1314
long_description_content_type="text/x-rst",
1415
url="https://github.com/iamsinghrajat/async-cache",
1516
packages=setuptools.find_packages(),

0 commit comments

Comments
 (0)