-
Notifications
You must be signed in to change notification settings - Fork 73
minor update by agents #305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c4ad0da
d3a13a1
3cb3972
42f931c
3ff9acf
eae914d
2555317
0007645
74f36af
64eee63
6e14b05
aad1de0
ea8d186
db9c131
a75068f
c4f2da6
ee81a92
581df6f
076428b
4956562
174d8ba
a833a9c
ba13199
d065e5b
cd51d0c
c13f08e
b51e5ee
8e43f06
b14cf9b
d97a8d9
9c51b94
fb56f2b
b7deae2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,12 +27,17 @@ class RecalculationNeeded(Exception): | |
|
|
||
|
|
||
| def _get_func_str(func: Callable) -> str: | ||
| return f".{func.__module__}.{func.__name__}" | ||
| """Return a string identifier for the function (module + name). | ||
|
|
||
| We accept Any here because static analysis can't always prove that the | ||
| runtime object will have __module__ and __name__, but at runtime the | ||
| decorated functions always do. | ||
|
|
||
| """ | ||
|
Comment on lines
+30
to
+36
|
||
| return f".{func.__module__}.{func.__name__}" | ||
|
|
||
| class _BaseCore: | ||
| __metaclass__ = abc.ABCMeta | ||
|
|
||
| class _BaseCore(metaclass=abc.ABCMeta): | ||
| def __init__( | ||
| self, | ||
| hash_func: Optional[HashFunc], | ||
|
|
@@ -90,8 +95,8 @@ def check_calc_timeout(self, time_spent): | |
| def get_entry_by_key(self, key: str) -> Tuple[str, Optional[CacheEntry]]: | ||
| """Get entry based on given key. | ||
|
|
||
| Return the result mapped to the given key in this core's cache, if such | ||
| a mapping exists. | ||
| Return the key and the :class:`~cachier.config.CacheEntry` mapped | ||
| to the given key in this core's cache, if such a mapping exists. | ||
|
|
||
| """ | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,6 +73,54 @@ def set_func(self, func): | |
| super().set_func(func) | ||
| self._func_str = _get_func_str(func) | ||
|
|
||
| @staticmethod | ||
| def _loading_pickle(raw_value) -> Any: | ||
|
||
| """Load pickled data with some recovery attempts.""" | ||
| try: | ||
| if isinstance(raw_value, bytes): | ||
| return pickle.loads(raw_value) | ||
| elif isinstance(raw_value, str): | ||
| # try to recover by encoding; prefer utf-8 but fall | ||
| # back to latin-1 in case raw binary was coerced to str | ||
| try: | ||
| return pickle.loads(raw_value.encode("utf-8")) | ||
| except Exception: | ||
| return pickle.loads(raw_value.encode("latin-1")) | ||
| else: | ||
| # unexpected type; attempt pickle.loads directly | ||
| try: | ||
| return pickle.loads(raw_value) | ||
| except Exception: | ||
| return None | ||
| except Exception as exc: | ||
| warnings.warn( | ||
| f"Redis value deserialization failed: {exc}", | ||
| stacklevel=2, | ||
| ) | ||
| return None | ||
|
|
||
| @staticmethod | ||
| def _get_raw_field(cached_data, field: str): | ||
|
||
| """Fetch field from cached_data with bytes/str key handling.""" | ||
| # try bytes key first, then str key | ||
| bkey = field.encode("utf-8") | ||
| if bkey in cached_data: | ||
| return cached_data[bkey] | ||
| return cached_data.get(field) | ||
|
|
||
| @staticmethod | ||
| def _get_bool_field(cached_data, name: str) -> bool: | ||
|
||
| """Fetch boolean field from cached_data.""" | ||
| raw = _RedisCore._get_raw_field(cached_data, name) or b"false" | ||
| if isinstance(raw, bytes): | ||
| try: | ||
| s = raw.decode("utf-8") | ||
| except Exception: | ||
| s = raw.decode("latin-1", errors="ignore") | ||
| else: | ||
| s = str(raw) | ||
| return s.lower() == "true" | ||
|
Comment on lines
+76
to
+122
|
||
|
|
||
| def get_entry_by_key(self, key: str) -> Tuple[str, Optional[CacheEntry]]: | ||
| """Get entry based on given key from Redis.""" | ||
| redis_client = self._resolve_redis_client() | ||
|
|
@@ -86,32 +134,28 @@ def get_entry_by_key(self, key: str) -> Tuple[str, Optional[CacheEntry]]: | |
|
|
||
| # Deserialize the value | ||
| value = None | ||
| if cached_data.get(b"value"): | ||
| value = pickle.loads(cached_data[b"value"]) | ||
| raw_value = _RedisCore._get_raw_field(cached_data, "value") | ||
| if raw_value is not None: | ||
| value = self._loading_pickle(raw_value) | ||
|
|
||
| # Parse timestamp | ||
| timestamp_str = cached_data.get(b"timestamp", b"").decode("utf-8") | ||
| raw_ts = _RedisCore._get_raw_field(cached_data, "timestamp") or b"" | ||
| if isinstance(raw_ts, bytes): | ||
| try: | ||
| timestamp_str = raw_ts.decode("utf-8") | ||
| except UnicodeDecodeError: | ||
| timestamp_str = raw_ts.decode("latin-1", errors="ignore") | ||
| else: | ||
| timestamp_str = str(raw_ts) | ||
| timestamp = ( | ||
| datetime.fromisoformat(timestamp_str) | ||
| if timestamp_str | ||
| else datetime.now() | ||
| ) | ||
|
|
||
| # Parse boolean fields | ||
| stale = ( | ||
| cached_data.get(b"stale", b"false").decode("utf-8").lower() | ||
| == "true" | ||
| ) | ||
| processing = ( | ||
| cached_data.get(b"processing", b"false") | ||
| .decode("utf-8") | ||
| .lower() | ||
| == "true" | ||
| ) | ||
| completed = ( | ||
| cached_data.get(b"completed", b"false").decode("utf-8").lower() | ||
| == "true" | ||
| ) | ||
| stale = _RedisCore._get_bool_field(cached_data, "stale") | ||
| processing = _RedisCore._get_bool_field(cached_data, "processing") | ||
| completed = _RedisCore._get_bool_field(cached_data, "completed") | ||
|
|
||
| entry = CacheEntry( | ||
| value=value, | ||
|
|
@@ -126,9 +170,9 @@ def get_entry_by_key(self, key: str) -> Tuple[str, Optional[CacheEntry]]: | |
| return key, None | ||
|
|
||
| def set_entry(self, key: str, func_res: Any) -> bool: | ||
| """Map the given result to the given key in Redis.""" | ||
| if not self._should_store(func_res): | ||
| return False | ||
| """Map the given result to the given key in Redis.""" | ||
| redis_client = self._resolve_redis_client() | ||
| redis_key = self._get_redis_key(key) | ||
|
|
||
|
|
@@ -242,8 +286,16 @@ def delete_stale_entries(self, stale_after: timedelta) -> None: | |
| ts = redis_client.hget(key, "timestamp") | ||
| if ts is None: | ||
| continue | ||
| # ts may be bytes or str depending on client configuration | ||
| if isinstance(ts, bytes): | ||
| try: | ||
| ts_s = ts.decode("utf-8") | ||
| except Exception: | ||
| ts_s = ts.decode("latin-1", errors="ignore") | ||
| else: | ||
| ts_s = str(ts) | ||
| try: | ||
| ts_val = datetime.fromisoformat(ts.decode("utf-8")) | ||
| ts_val = datetime.fromisoformat(ts_s) | ||
| except Exception as exc: | ||
| warnings.warn( | ||
| f"Redis timestamp parse failed: {exc}", stacklevel=2 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commented-out
--unsafe-fixesargument suggests that unsafe fixes were considered but not enabled. While commenting is fine for documentation, it would be clearer to either remove the comment entirely or add an explanatory comment about why unsafe fixes are not enabled. The current inline comment without explanation may cause confusion for future maintainers.