Spaces:
Sleeping
Sleeping
Claude
Claude Sonnet 5
Add rate limiting, tests, better extraction, and HF auto-deploy
ebab0e2 unverified Download app/cache.py from Almaatla/web-scraper: direct link, hf CLI and curl.
- Browser
- Download file 1.46 kB
-
https://huggingface.co/spaces/Almaatla/web-scraper/resolve/main/app/cache.py
- Command line
-
hf download hf://spaces/Almaatla/web-scraper/app/cache.py
-
curl -L -o cache.py https://huggingface.co/spaces/Almaatla/web-scraper/resolve/main/app/cache.py
1.46 kB
| import time | |
| from collections import OrderedDict | |
| from threading import Lock | |
| from typing import Any, Callable, Optional | |
| class TTLCache: | |
| """Small in-memory LRU+TTL cache, used to avoid re-rendering a page | |
| with a full browser on every repeat request for the same URL. | |
| In-memory is sufficient here: the service runs as a single process, so | |
| there's no need for a shared store like Redis. | |
| """ | |
| def __init__(self, ttl_seconds: float, max_size: int = 256, *, clock: Callable[[], float] = time.monotonic): | |
| self.ttl_seconds = ttl_seconds | |
| self.max_size = max_size | |
| self._clock = clock | |
| self._store: "OrderedDict[str, tuple[float, Any]]" = OrderedDict() | |
| self._lock = Lock() | |
| def get(self, key: str) -> Optional[Any]: | |
| with self._lock: | |
| entry = self._store.get(key) | |
| if entry is None: | |
| return None | |
| expires_at, value = entry | |
| if self._clock() >= expires_at: | |
| del self._store[key] | |
| return None | |
| self._store.move_to_end(key) | |
| return value | |
| def set(self, key: str, value: Any) -> None: | |
| if self.ttl_seconds <= 0: | |
| return | |
| with self._lock: | |
| self._store[key] = (self._clock() + self.ttl_seconds, value) | |
| self._store.move_to_end(key) | |
| while len(self._store) > self.max_size: | |
| self._store.popitem(last=False) | |