@@ -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 (" \n Key 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
59304Thank you for contributing to Cachier! These guidelines help ensure a robust, maintainable, and user-friendly package for everyone.
0 commit comments