Skip to content

Commit 8d0102c

Browse files
Add a few more instructions for coding assistants (#289)
* a few more agents instructions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 5e0d41c commit 8d0102c

3 files changed

Lines changed: 1088 additions & 0 deletions

File tree

.github/copilot-instructions.md

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,251 @@ Welcome to the Cachier codebase! Please follow these guidelines to ensure code s
5454
- Follow the existing code style and conventions for imports, docstrings, and type annotations.
5555
- Prefer explicit, readable code over cleverness.
5656

57+
## 9. The Code Base
58+
59+
### General structure
60+
61+
The repository contains a Python package called Cachier that provides persistent function caching with several backends:
62+
63+
cachier/
64+
├── src/cachier/ # Main library code
65+
│ ├── __init__.py
66+
│ ├── core.py # Decorator logic, backend selection
67+
│ ├── cores/ # Backend implementations
68+
│ │ ├── pickle.py
69+
│ │ ├── memory.py
70+
│ │ ├── mongo.py
71+
│ │ ├── sql.py
72+
│ │ ├── redis.py
73+
│ │ └── base.py
74+
│ ├── config.py # Global/default config
75+
│ ├── \_types.py # Type definitions
76+
│ ├── _version.py
77+
│ └── __main__.py
78+
├── tests/ # Pytest-based tests, backend-marked
79+
│ ├── test_\*.py
80+
│ └── \*\_requirements.txt # Backend-specific test requirements
81+
├── examples/ # Usage examples
82+
├── README.rst # Main documentation
83+
└── ...
84+
85+
### Key functionality
86+
87+
- core.py exposes the cachier decorator. It chooses a backend (pickle, mongo, memory, SQL, or Redis) and wraps the target function:
88+
89+
```python
90+
backend = _update_with_defaults(backend, "backend")
91+
mongetter = _update_with_defaults(mongetter, "mongetter")
92+
if callable(mongetter):
93+
backend = "mongo"
94+
...
95+
elif backend == "redis":
96+
core = _RedisCore(
97+
hash_func=hash_func,
98+
redis_client=redis_client,
99+
wait_for_calc_timeout=wait_for_calc_timeout,
100+
)
101+
else:
102+
raise ValueError("specified an invalid core: %s" % backend)
103+
```
104+
105+
- Global defaults and cache-entry structures are defined in config.py:
106+
107+
```python
108+
@dataclass
109+
class Params:
110+
caching_enabled: bool = True
111+
hash_func: HashFunc = _default_hash_func
112+
backend: Backend = "pickle"
113+
mongetter: Optional[Mongetter] = None
114+
stale_after: timedelta = timedelta.max
115+
next_time: bool = False
116+
cache_dir: Union[str, os.PathLike] = field(default_factory=LazyCacheDir)
117+
pickle_reload: bool = True
118+
separate_files: bool = False
119+
wait_for_calc_timeout: int = 0
120+
allow_none: bool = False
121+
```
122+
123+
- The project supports multiple backends; each resides under src/cachier/cores/ (e.g., redis.py, mongo.py, etc.). The Redis example demonstrates how to use one backend:
124+
125+
```python
126+
import time
127+
from datetime import timedelta
128+
129+
try:
130+
import redis
131+
132+
from cachier import cachier
133+
except ImportError as e:
134+
print(f"Missing required package: {e}")
135+
print("Install with: pip install redis cachier")
136+
exit(1)
137+
138+
139+
def setup_redis_client():
140+
"""Set up a Redis client for caching."""
141+
try:
142+
# Connect to Redis (adjust host/port as needed)
143+
client = redis.Redis(
144+
host="localhost",
145+
port=6379,
146+
db=0,
147+
decode_responses=False, # Important: keep as bytes for pickle
148+
)
149+
# Test connection
150+
client.ping()
151+
print("✓ Connected to Redis successfully")
152+
return client
153+
except redis.ConnectionError:
154+
print("✗ Could not connect to Redis")
155+
print("Make sure Redis is running on localhost:6379")
156+
print("Or install and start Redis with: docker run -p 6379:6379 redis")
157+
return None
158+
159+
160+
def expensive_calculation(n):
161+
"""Simulate an expensive calculation."""
162+
print(f" Computing expensive_calculation({n})...")
163+
time.sleep(2) # Simulate work
164+
return n * n + 42
165+
166+
167+
def demo_basic_caching():
168+
"""Demonstrate basic Redis caching."""
169+
print("\n=== Basic Redis Caching ===")
170+
171+
@cachier(backend="redis", redis_client=setup_redis_client())
172+
def cached_calculation(n):
173+
return expensive_calculation(n)
174+
175+
# First call - should be slow
176+
start = time.time()
177+
result1 = cached_calculation(5)
178+
time1 = time.time() - start
179+
print(f"First call: {result1} (took {time1:.2f}s)")
180+
181+
# Second call - should be fast (cached)
182+
start = time.time()
183+
result2 = cached_calculation(5)
184+
time2 = time.time() - start
185+
print(f"Second call: {result2} (took {time2:.2f}s)")
186+
187+
assert result1 == result2
188+
assert time2 < time1
189+
print("✓ Caching working correctly!")
190+
191+
192+
def demo_stale_after():
193+
"""Demonstrate stale_after functionality with Redis."""
194+
print("\n=== Stale After Demo ===")
195+
196+
@cachier(
197+
backend="redis",
198+
redis_client=setup_redis_client(),
199+
stale_after=timedelta(seconds=3),
200+
)
201+
def time_sensitive_calculation(n):
202+
return expensive_calculation(n)
203+
204+
# First call
205+
result1 = time_sensitive_calculation(10)
206+
print(f"First call: {result1}")
207+
208+
# Second call within 3 seconds - should use cache
209+
result2 = time_sensitive_calculation(10)
210+
print(f"Second call (within 3s): {result2}")
211+
assert result1 == result2
212+
213+
# Wait for cache to become stale
214+
print("Waiting 4 seconds for cache to become stale...")
215+
time.sleep(4)
216+
217+
# Third call after 4 seconds - should recalculate
218+
result3 = time_sensitive_calculation(10)
219+
print(f"Third call (after 4s): {result3}")
220+
assert result3 != result1
221+
print("✓ Stale after working correctly!")
222+
223+
224+
def demo_callable_client():
225+
"""Demonstrate using a callable Redis client."""
226+
print("\n=== Callable Client Demo ===")
227+
228+
def get_redis_client():
229+
"""Get a Redis client."""
230+
return redis.Redis(host="localhost", port=6379, db=0, decode_responses=False)
231+
232+
@cachier(backend="redis", redis_client=get_redis_client)
233+
def cached_with_callable(n):
234+
return expensive_calculation(n)
235+
236+
result1 = cached_with_callable(15)
237+
result2 = cached_with_callable(15)
238+
assert result1 == result2
239+
print(f"Callable client result: {result1}")
240+
print("✓ Callable client working correctly!")
241+
242+
243+
def demo_cache_management():
244+
"""Demonstrate cache management functions."""
245+
print("\n=== Cache Management Demo ===")
246+
247+
@cachier(backend="redis", redis_client=setup_redis_client())
248+
def managed_calculation(n):
249+
return expensive_calculation(n)
250+
251+
# Cache some values
252+
managed_calculation(20)
253+
managed_calculation(21)
254+
255+
# Clear the cache
256+
managed_calculation.clear_cache()
257+
print("✓ Cache cleared successfully!")
258+
259+
# Verify cache is empty
260+
start = time.time()
261+
result = managed_calculation(20) # Should be slow again
262+
time_taken = time.time() - start
263+
print(f"After clearing cache: {result} (took {time_taken:.2f}s)")
264+
265+
266+
def main():
267+
"""Run all Redis core demonstrations."""
268+
print("Cachier Redis Core Demo")
269+
print("=" * 50)
270+
271+
# Check if Redis is available
272+
client = setup_redis_client()
273+
if client is None:
274+
return
275+
276+
try:
277+
demo_basic_caching()
278+
demo_stale_after()
279+
demo_callable_client()
280+
demo_cache_management()
281+
282+
print("\n" + "=" * 50)
283+
print("✓ All Redis core demonstrations completed successfully!")
284+
print("\nKey benefits of Redis core:")
285+
print("- High-performance in-memory caching")
286+
print("- Cross-process and cross-machine caching")
287+
print("- Optional persistence with Redis configuration")
288+
print("- Built-in expiration and eviction policies")
289+
290+
except Exception as e:
291+
print(f"\n✗ Demo failed with error: {e}")
292+
finally:
293+
# Clean up
294+
if client:
295+
client.close()
296+
297+
298+
if __name__ == "__main__":
299+
main()
300+
```
301+
57302
______________________________________________________________________
58303

59304
Thank you for contributing to Cachier! These guidelines help ensure a robust, maintainable, and user-friendly package for everyone.

0 commit comments

Comments
 (0)