@@ -54,6 +54,252 @@ 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(
231+ host = " localhost" , port = 6379 , db = 0 , decode_responses = False
232+ )
233+
234+ @cachier (backend = " redis" , redis_client = get_redis_client)
235+ def cached_with_callable (n ):
236+ return expensive_calculation(n)
237+
238+ result1 = cached_with_callable(15 )
239+ result2 = cached_with_callable(15 )
240+ assert result1 == result2
241+ print (f " Callable client result: { result1} " )
242+ print (" ✓ Callable client working correctly!" )
243+
244+
245+ def demo_cache_management ():
246+ """ Demonstrate cache management functions."""
247+ print (" \n === Cache Management Demo ===" )
248+
249+ @cachier (backend = " redis" , redis_client = setup_redis_client())
250+ def managed_calculation (n ):
251+ return expensive_calculation(n)
252+
253+ # Cache some values
254+ managed_calculation(20 )
255+ managed_calculation(21 )
256+
257+ # Clear the cache
258+ managed_calculation.clear_cache()
259+ print (" ✓ Cache cleared successfully!" )
260+
261+ # Verify cache is empty
262+ start = time.time()
263+ result = managed_calculation(20 ) # Should be slow again
264+ time_taken = time.time() - start
265+ print (f " After clearing cache: { result} (took { time_taken:.2f } s) " )
266+
267+
268+ def main ():
269+ """ Run all Redis core demonstrations."""
270+ print (" Cachier Redis Core Demo" )
271+ print (" =" * 50 )
272+
273+ # Check if Redis is available
274+ client = setup_redis_client()
275+ if client is None :
276+ return
277+
278+ try :
279+ demo_basic_caching()
280+ demo_stale_after()
281+ demo_callable_client()
282+ demo_cache_management()
283+
284+ print (" \n " + " =" * 50 )
285+ print (" ✓ All Redis core demonstrations completed successfully!" )
286+ print (" \n Key benefits of Redis core:" )
287+ print (" - High-performance in-memory caching" )
288+ print (" - Cross-process and cross-machine caching" )
289+ print (" - Optional persistence with Redis configuration" )
290+ print (" - Built-in expiration and eviction policies" )
291+
292+ except Exception as e:
293+ print (f " \n ✗ Demo failed with error: { e} " )
294+ finally :
295+ # Clean up
296+ if client:
297+ client.close()
298+
299+
300+ if __name__ == " __main__" :
301+ main()
302+ ```
57303______________________________________________________________________
58304
59305Thank you for contributing to Cachier! These guidelines help ensure a robust, maintainable, and user-friendly package for everyone.
0 commit comments