diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48a8540..21b6247 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,7 +81,7 @@ jobs: # a missing backend package would turn the gate below into a formality, # so it does not rely on a dev-group entry staying put. - name: Sync dependencies - run: uv sync --group dev --extra jwt --extra redis --extra memcache + run: uv sync --group dev --extra jwt --extra redis --extra memcached # The gate. A release is the one run where a red test result arrives too # late to matter, so everything CI checks elsewhere is checked here too, diff --git a/CHANGELOG.md b/CHANGELOG.md index bff1d18..390e755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,11 @@ Note that 0.3.3 was never released; 0.3.4 follows 0.3.2. - **`CacheLock`, a distributed lock built on backend primitives.** Usable as an async context manager or with direct `acquire`/`release`/`extend`/`locked` calls, raising `LockTimeoutError` on timeout. ([#64](https://github.com/allen0099/FastAPI-CacheX/issues/64)) - **`expire_if_equals()` backend primitive for owner-checked TTL renewal.** Added to `BaseCacheBackend`, `MemoryBackend`, `AsyncRedisCacheBackend`, and `MemcachedBackend`. ([#64](https://github.com/allen0099/FastAPI-CacheX/issues/64)) +- **`memcached` extra for `MemcachedBackend`.** Install with + `fastapi-cachex[memcached]`, matching the backend's name. It pulls in the + same `pymemcache` dependency as the old `memcache` extra. + ([#201](https://github.com/allen0099/FastAPI-CacheX/issues/201)) + ### Changed - **GitHub release notes list one line per change.** Each changelog entry now @@ -37,6 +42,11 @@ Note that 0.3.3 was never released; 0.3.4 follows 0.3.2. `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)) + ### Fixed - **Redis `clear_pattern()` no longer strips a pattern that starts with the key diff --git a/CLAUDE.md b/CLAUDE.md index 61713b9..2c050af 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,7 +80,7 @@ The library has four independent subsystems: All backends implement `BaseCacheBackend` (abstract base in `backends/base.py`): - `MemoryBackend`: In-process dict with background cleanup task. Not suitable for multi-process production use. - `AsyncRedisCacheBackend` (`backends/redis.py`): Fully async; uses `SCAN` (not `KEYS`) for pattern operations. Requires `redis[hiredis]` and `orjson` extras. -- `MemcachedBackend` (`backends/memcached.py`): `clear_pattern`/`get_all_keys` are no-ops (return `0`/`[]` with a `RuntimeWarning`) since the Memcached protocol has no key enumeration. Runs the sync pymemcache client in worker threads with connection pooling (`use_pooling=True`, `default_noreply=False`), so concurrent calls never share a socket and every write is acknowledged before the next call on another socket can observe it. Requires `pymemcache` extra. +- `MemcachedBackend` (`backends/memcached.py`): `clear_pattern`/`get_all_keys` are no-ops (return `0`/`[]` with a `RuntimeWarning`) since the Memcached protocol has no key enumeration. Runs the sync pymemcache client in worker threads with connection pooling (`use_pooling=True`, `default_noreply=False`), so concurrent calls never share a socket and every write is acknowledged before the next call on another socket can observe it. Requires the `memcached` extra (`memcache` is a deprecated alias until 0.4.0). Backend keys are namespaced automatically (default prefix: `fastapi_cachex:`). diff --git a/README.md b/README.md index 616e2b4..6b4b50f 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ backends and the optional session transports ship as extras: | Extra | Install | Pulls in | Needed for | |-------|---------|----------|------------| | `redis` | `uv add "fastapi-cachex[redis]"` | `redis[hiredis]`, `orjson` | `AsyncRedisCacheBackend` | -| `memcache` | `uv add "fastapi-cachex[memcache]"` | `pymemcache` | `MemcachedBackend` (note: `memcache`, not `memcached`) | +| `memcached` | `uv add "fastapi-cachex[memcached]"` | `pymemcache` | `MemcachedBackend` (the older `memcache` name still works until 0.4.0) | | `jwt` | `uv add "fastapi-cachex[jwt]"` | `PyJWT` | `SessionConfig(token_format="jwt")` | Extras combine: `uv add "fastapi-cachex[redis,jwt]"`. diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 9d096df..0da3990 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -94,8 +94,10 @@ hiredis will fail to negotiate it. ## Memcached -Install the extra with `uv add "fastapi-cachex[memcache]"` (note: `memcache`, not -`memcached`). +Install the extra with `uv add "fastapi-cachex[memcached]"`. Before 0.3.8 it was +called `memcache`; that name still works but is deprecated and will be removed in +0.4.0. An unknown extra only produces a warning at install time, so after 0.4.0 +`fastapi-cachex[memcache]` would install without `pymemcache`. ```python from fastapi_cachex.backends import MemcachedBackend diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 0d43f68..926257e 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -109,7 +109,7 @@ Worth doing whenever a test is written for something security-relevant. ## Using tox tox ensures the code works across different Python versions (3.10-3.14, the -`env_list` in `tox.ini`). Each environment installs the `redis` and `memcache` +`env_list` in `tox.ini`). Each environment installs the `redis` and `memcached` extras through `tox-uv` and passes the `CACHEX_TEST_*` and `CACHEX_REQUIRE_LIVE_SERVERS` variables through, so the opt-in rules above apply unchanged. diff --git a/fastapi_cachex/backends/memcached.py b/fastapi_cachex/backends/memcached.py index 89476b5..2dde101 100644 --- a/fastapi_cachex/backends/memcached.py +++ b/fastapi_cachex/backends/memcached.py @@ -77,7 +77,7 @@ def __init__( try: from pymemcache import HashClient except ImportError: - msg = "pymemcache is not installed. Please install it with 'pip install pymemcache'" + msg = "pymemcache is not installed. Install it with the extra: pip install 'fastapi-cachex[memcached]'" raise CacheXError(msg) # Pooled connections have no ordering guarantee between each other, so diff --git a/i18n/zh-TW/docs/BACKENDS.md b/i18n/zh-TW/docs/BACKENDS.md index d6fc9ac..67cdf2e 100644 --- a/i18n/zh-TW/docs/BACKENDS.md +++ b/i18n/zh-TW/docs/BACKENDS.md @@ -71,7 +71,7 @@ BackendProxy.set(backend) ## Memcached {#memcached} -以 `uv add "fastapi-cachex[memcache]"` 安裝此 extra(注意是 `memcache`,不是 `memcached`)。 +以 `uv add "fastapi-cachex[memcached]"` 安裝此 extra。0.3.8 以前這個 extra 名為 `memcache`;舊名稱仍可使用但已棄用,將於 0.4.0 移除。安裝時遇到不存在的 extra 只會顯示警告,因此 0.4.0 之後 `fastapi-cachex[memcache]` 會裝好套件但不含 `pymemcache`。 ```python from fastapi_cachex.backends import MemcachedBackend diff --git a/i18n/zh-TW/docs/index.md b/i18n/zh-TW/docs/index.md index 470fe88..351a3d0 100644 --- a/i18n/zh-TW/docs/index.md +++ b/i18n/zh-TW/docs/index.md @@ -37,7 +37,7 @@ uv add fastapi-cachex | Extra | 安裝 | 帶入套件 | 用途 | |-------|------|---------|------| | `redis` | `uv add "fastapi-cachex[redis]"` | `redis[hiredis]`、`orjson` | `AsyncRedisCacheBackend` | -| `memcache` | `uv add "fastapi-cachex[memcache]"` | `pymemcache` | `MemcachedBackend`(注意是 `memcache`,不是 `memcached`) | +| `memcached` | `uv add "fastapi-cachex[memcached]"` | `pymemcache` | `MemcachedBackend`(舊名稱 `memcache` 在 0.4.0 之前仍可使用) | | `jwt` | `uv add "fastapi-cachex[jwt]"` | `PyJWT` | `SessionConfig(token_format="jwt")` | Extra 可以組合:`uv add "fastapi-cachex[redis,jwt]"`。 diff --git a/pyproject.toml b/pyproject.toml index 6ff777a..0000617 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,8 @@ docs = [ ] [project.optional-dependencies] +memcached = ["pymemcache"] +# Deprecated alias of `memcached`, removed in 0.4.0 (#202). memcache = ["pymemcache"] redis = ["redis[hiredis]>=5.3.0", "orjson"] jwt = ["PyJWT>=2.9.0"] diff --git a/tox.ini b/tox.ini index 5ebc3e6..895dfe1 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,7 @@ env_list = runner = uv-venv-lock-runner extras = redis - memcache + memcached # The Redis/Memcached suites are opt-in because they wipe the server they # connect to; without these they skip. See tests/live_servers.py. passenv = diff --git a/uv.lock b/uv.lock index 4d8c6c9..f9272f5 100644 --- a/uv.lock +++ b/uv.lock @@ -520,6 +520,9 @@ jwt = [ memcache = [ { name = "pymemcache" }, ] +memcached = [ + { name = "pymemcache" }, +] redis = [ { name = "orjson" }, { name = "redis", extra = ["hiredis"] }, @@ -559,9 +562,10 @@ requires-dist = [ { name = "pydantic" }, { name = "pyjwt", marker = "extra == 'jwt'", specifier = ">=2.9.0" }, { name = "pymemcache", marker = "extra == 'memcache'" }, + { name = "pymemcache", marker = "extra == 'memcached'" }, { name = "redis", extras = ["hiredis"], marker = "extra == 'redis'", specifier = ">=5.3.0" }, ] -provides-extras = ["memcache", "redis", "jwt"] +provides-extras = ["memcached", "memcache", "redis", "jwt"] [package.metadata.requires-dev] dev = [