From 128ecc19ae7d6ec9c425d08646390fae03a75530 Mon Sep 17 00:00:00 2001 From: allen0099 Date: Sat, 26 Sep 2026 18:57:12 +0000 Subject: [PATCH] feat: deprecate the unused CacheError fastapi_cachex.exceptions.CacheError is never raised. Accessing or importing it now emits a DeprecationWarning pointing at the caller; the class itself is unchanged, so except CacheError keeps working until it is removed in 0.4.0 (#130). Closes #163 --- CHANGELOG.md | 5 ++++- docs/api/types.md | 4 ++++ fastapi_cachex/exceptions.py | 32 ++++++++++++++++++++++++++++++-- tests/test_exceptions.py | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 tests/test_exceptions.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cd96a26..113e84d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,11 +57,14 @@ Note that 0.3.3 was never released; 0.3.4 follows 0.3.2. pattern clears nothing, the prefix-stripped form is still tried and emits a `DeprecationWarning` if it clears anything. The retry will be removed in 0.4.0. ([#125](https://github.com/allen0099/FastAPI-CacheX/issues/125)) - - **The `memcache` extra.** Use `memcached` instead. The old name keeps working until 0.4.0 removes it; after that, pip and uv only warn about the unknown extra and install without `pymemcache`. ([#202](https://github.com/allen0099/FastAPI-CacheX/issues/202)) +- **`fastapi_cachex.exceptions.CacheError`.** Nothing in the package raises it. + Accessing or importing it emits a `DeprecationWarning`; catch `CacheXError` + instead. It will be removed in 0.4.0. + ([#163](https://github.com/allen0099/FastAPI-CacheX/issues/163)) ### Fixed diff --git a/docs/api/types.md b/docs/api/types.md index 5bd2647..b1b25cb 100644 --- a/docs/api/types.md +++ b/docs/api/types.md @@ -3,3 +3,7 @@ ::: fastapi_cachex.types ::: fastapi_cachex.exceptions + +`fastapi_cachex.exceptions.CacheError` is deprecated since 0.3.8 and not listed +above. Nothing in the package raises it, and accessing it emits a +`DeprecationWarning`. Catch `CacheXError` instead. It will be removed in 0.4.0. diff --git a/fastapi_cachex/exceptions.py b/fastapi_cachex/exceptions.py index e956ba8..c79f859 100644 --- a/fastapi_cachex/exceptions.py +++ b/fastapi_cachex/exceptions.py @@ -1,12 +1,26 @@ """Custom exception classes for FastAPI-CacheX.""" +import warnings +from typing import TYPE_CHECKING + class CacheXError(Exception): """Base class for all exceptions in FastAPI-CacheX.""" -class CacheError(CacheXError): - """Exception raised for cache-related errors.""" +class _CacheError(CacheXError): + """Exception raised for cache-related errors. + + .. deprecated:: 0.3.8 + Never raised by FastAPI-CacheX. Catch :class:`CacheXError` instead. + Will be removed in version 0.4.0. + """ + + +_CacheError.__name__ = _CacheError.__qualname__ = "CacheError" + +if TYPE_CHECKING: + CacheError = _CacheError class BackendNotFoundError(CacheXError): @@ -28,3 +42,17 @@ class RequestNotFoundError(CacheXError): class LockTimeoutError(CacheXError): """Exception raised when acquiring a lock times out.""" + + +def __getattr__(name: str) -> type[CacheXError]: + """Return the deprecated ``CacheError`` with a ``DeprecationWarning``.""" + if name == "CacheError": + warnings.warn( + "CacheError is deprecated and never raised, catch CacheXError instead. " + "Will be removed in version 0.4.0.", + DeprecationWarning, + stacklevel=2, + ) + return _CacheError + msg = f"module {__name__!r} has no attribute {name!r}" + raise AttributeError(msg) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 0000000..2756b05 --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,35 @@ +"""Tests for the package-level exceptions.""" + +import importlib + +import pytest + +from fastapi_cachex.exceptions import CacheXError + + +def test_cache_error_access_warns_and_still_works() -> None: + """Test accessing the deprecated CacheError warns (#163).""" + exceptions = importlib.import_module("fastapi_cachex.exceptions") + + with pytest.warns(DeprecationWarning, match="CacheError is deprecated") as record: + cache_error = exceptions.CacheError + + assert record[0].filename == __file__ + assert cache_error.__name__ == "CacheError" + assert issubclass(cache_error, CacheXError) + with pytest.raises(CacheXError): + raise cache_error + + +def test_cache_error_from_import_warns() -> None: + """Test ``from fastapi_cachex.exceptions import CacheError`` warns (#163).""" + with pytest.warns(DeprecationWarning, match="removed in version 0.4.0"): + from fastapi_cachex.exceptions import CacheError # noqa: F401 + + +def test_unknown_attribute_still_raises_attribute_error() -> None: + """Test the module ``__getattr__`` only handles CacheError.""" + exceptions = importlib.import_module("fastapi_cachex.exceptions") + + with pytest.raises(AttributeError, match="has no attribute 'Missing'"): + _ = exceptions.Missing