This allows more fine-grained lint rule adjustments, and allows
the examples to treat fastapi_cache as a 'third party' module, just
like users of the library would use it.
The linter has been used in the past, so most assertions for these were
already there but needed to be updated to use `noqa: S` instead of
`nosec: B` annotations.
Ruff handles black, flake8 and isort in one package, and is way faster.
The isort rules had not been enforced, so this commit includes a lot
of import resorting changes.
I switched to flake8-bugbear and the standard black-compatible line
length of 80 + 10% (so max 88 characters), so some line reflowing is
included too.
Finally, because bugbear rightly points out that `setattr()` is less
performant, I've switched the `__signature__` assigment back to using
a direct assignment with type ignore comment.
Tox manages test environments for all supported Python versions, as
well as linting and formatting tools. On GitHub, the test and lint
steps are kept as close as possible to the Makefile equivalents.
The header name is configurable, and defaults to `X-FastAPI-Cache`,
the value is either `HIT` or `MISS`.
Note that the header is not set at all when the cache is disabled.
Use just three code paths: uncacheable, cache miss and cache hit. This
makes it much easier to follow what happens for each case. the only
places where the inner function now exits early are when the call is
uncacheable, or when there is a cache hit and the request included a
matching If-Not-Modified header.
- Use a utility function to capture when a request should not use the
cache
- Use the starlette.status constant for the 'not modified' status for
code clarity.
- Use `setattr()` for the inner function signature, avoiding the need
for a type checker override comment.
This ensures that any syntax issues are caught early (by type checkers
and tests). Backends that are missing dependencies are skipped. By
importing, this exposed an issue where the redis type annotations
raised an exception, which has been fixed by using forward annotations.
To help avoid import dependency hell, the Backend ABC has been moved to
`fastapi_cache.types`. In the process, it has been made an actual ABC.
- Compatibility with older Python versions
- use `Optional` and `Union` instead of `... | None` and `a | b`
- use `typing_extensions.Protocol` instead of `typing.Protocol`
- use `typing.Dict`, `typing.List`, etc. instead of the concrete types.
- Fix backend `.get()` annotations; not all were marked as `Optional[str]`
- Don't return anything from `Backend.set()` methods.
- The `Coder.decode_as_type()` type parameter must be a type to be
compatible with `ModelField(..., type_=...)`.
- Clean up `Optional[]` use, remove where it is not needed.
- Clean up variable use in decorator, keeping the raw cached value
separate from the return value from the wrapped endpoint.
- Annotate the wrapper as returning either the original type _or_ a
Response (returning a 304 Not Modified response).
- Clean up small edge-case where `response` could be `None`.
- Correct type annotation on `JsonCoder.decode()` to match `Coder.decode()`.
This is, for the majority of backends, the native format anyway, and so
we save encoding and decoding when using the PickleCodec or if (in future)
a orjson Coder was to be added.
For the JsonCodec, the only thing that changed is the location where the
JSON data is encoded to bytes and decoded back again to a string.
In `timeit` tests, 10.000 calls to `ModelField()` could take up to half
a second on my Macbook Pro M1, depending on the type annotation used.
Given that the method is called for every cache hit, this can really add
up. The number of different return types for endpoints is very much
finite however, so caching is a definite win here.
Instead of assuming that the Request and Response injected keyword
arguments can be named `request` and `response`, use namespaced
keyword names starting with a double underscore.
By default the parameter names now start with `__fastapi_cache_` and so
are a) clearly marked as internal, and b) highly unlikely to clash with
existing keyword arguments. The prefix is configurable in the unlikely
event that the names would clash in specific cases.
Use the return annotation to decode cached data to the correct type.
This follows the same logic FastAPI uses to JSON request bodies.
For the PickleCoder, this is a no-op as pickle already stores type
information in the serialised data.
This signals to automated tools what names usually can be imported from
the package, as otherwise you'd get warnings like `"default_key_builder"
is not exported from module "fastapi_cache"`.