Feat/exclude params - #574
Open
achrafBenHamou wants to merge 2 commits into
Open
Conversation
decode_as_type stopped converting cached values to the endpoint return type when the Pydantic v1 `fields.ModelField` validation was dropped instead of being ported to v2. Cached hits came back as raw JSON primitives (tuples as lists, dataclasses and BaseModels as dicts) and invalid payloads no longer raised ValidationError. Port the validation to Pydantic v2 `TypeAdapter`, reusing the existing per-type cache, and drop the leftover `ModelField` placeholder. Also fix two broken tests: - test_datetime compared timestamps taken at different times, so restore the microsecond truncation that made the comparison meaningful. - test_non_get sent PUT to /cached_put, a GET-only route, and only asserted on the resulting 405; point it back at /uncached_put.
Some arguments should not split one logical result into separate cache entries: a database session, an authenticated user, a request id or a tracing nonce. Listing their names in exclude_params drops them from the arguments handed to the key builder, so calls differing only in those values share a single entry. The filtering runs before the key builder, so it applies to the default builder and custom ones alike. Positional arguments are matched to their parameter name by position; anything absorbed by a variadic positional parameter is kept. Names absent from the decorated signature raise a ValueError at decoration time so typos surface at import, except for functions taking **kwargs, where any name can be valid. Adds an /excluded_params route to the in-memory example, tests covering keyword, positional, unknown-name and **kwargs cases, and README docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add
exclude_paramsto leave selected arguments out of the cache keyThe problem
default_key_builderhashesf"{func.__module__}:{func.__name__}:{args}:{kwargs}", i.e. therepr()of every argument. That is the right default, but it means any argument that is not part of the identity of the result silently breaks caching:Sessionhas no__repr__, so its default one contains the object's memory address. Every request produces a different address, therefore a different key: the endpoint is a permanent MISS, and the cache quietly fills with entries nobody will ever read. The same happens with an injectedcurrent_user, an httpx/aiobotocore client, or any per-request object. Query parameters that shouldn't split the cache — a request id, a tracing nonce, a cache-buster — have the same effect, only deterministically.Today the only way out is to write a custom
key_builder. But a custom builder receives the wholeargs/kwargs, so it has to re-implement the default hashing and then hard-code the names to skip — per project, and re-done for every endpoint that needs a different set. That is a lot of boilerplate for "ignore this one argument".The change
One new optional keyword argument on
@cache:The named parameters are dropped from the arguments passed to the key builder, so calls differing only in those values share one entry.
Design notes:
default_key_builder. So it works with custom key builders too, and no builder has to know about the feature. The one case it cannot cover is a builder that derives its key from theRequestitself (request.url,request.query_params) — that is documented in the README section.*argsis kept, since it has no name to exclude by.ValueErrorwhen the decorator is applied, so a misspelling surfaces at import rather than as a cache that silently never hits. Functions taking**kwargsare exempt from that check, as any name can legitimately be an argument there.exclude_params=None(the default) the new code paths are skipped entirely —if exclude_params:guards both the validation and the filtering. No existing key changes, so no cache invalidation for anyone upgrading.Scope
fastapi_cache/decorator.py: theexclude_paramsargument plus two small module-level helpers (_check_excluded_params,_exclude_params), in the same style as the existing_locate_param/_uncacheablehelpers.make lintclean (mypy --strict/pyrightstrict onfastapi_cache).ValueError, and a**kwargsfunction./excluded_paramsroute added to the in-memory example, which is what the endpoint test exercises.Happy to adjust the naming (
exclude_paramsvs. something likekey_exclude), to make the unknown-name check a warning instead of an error, or to split the docs out, if you'd prefer any of those differently.