mirror of
https://github.com/long2ice/fastapi-cache.git
synced 2026-03-25 04:57:54 +00:00
Merge branch 'long2ice:main' into fix/piclke-coder
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -2,14 +2,16 @@
|
|||||||
|
|
||||||
## 0.2
|
## 0.2
|
||||||
|
|
||||||
### 0.2.1
|
|
||||||
|
|
||||||
- Support cache jinja2 template response.
|
|
||||||
|
|
||||||
### 0.2.0
|
### 0.2.0
|
||||||
|
|
||||||
- Make `request` and `response` optional.
|
- Make `request` and `response` optional.
|
||||||
- Add typing info to the `cache` decorator.
|
- Add typing info to the `cache` decorator.
|
||||||
|
- Support cache jinja2 template response.
|
||||||
|
- Support cache `JSONResponse`
|
||||||
|
- Add `py.typed` file and type hints
|
||||||
|
- Add TestCase
|
||||||
|
- Fix cache decorate sync function
|
||||||
|
- Transparently handle backend connection failures.
|
||||||
|
|
||||||
## 0.1
|
## 0.1
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import pendulum
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import Response
|
from starlette.responses import JSONResponse, Response
|
||||||
|
|
||||||
from fastapi_cache import FastAPICache
|
from fastapi_cache import FastAPICache
|
||||||
from fastapi_cache.backends.inmemory import InMemoryBackend
|
from fastapi_cache.backends.inmemory import InMemoryBackend
|
||||||
@@ -51,6 +51,12 @@ def sync_me():
|
|||||||
return 42
|
return 42
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/cache_response_obj")
|
||||||
|
@cache(namespace="test", expire=5)
|
||||||
|
async def cache_response_obj():
|
||||||
|
return JSONResponse({"a": 1})
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
async def startup():
|
async def startup():
|
||||||
FastAPICache.init(InMemoryBackend())
|
FastAPICache.init(InMemoryBackend())
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from fastapi.staticfiles import StaticFiles
|
|||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from redis.asyncio.connection import ConnectionPool
|
from redis.asyncio.connection import ConnectionPool
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import Response
|
from starlette.responses import JSONResponse, Response
|
||||||
|
|
||||||
from fastapi_cache import FastAPICache
|
from fastapi_cache import FastAPICache
|
||||||
from fastapi_cache.backends.redis import RedisBackend
|
from fastapi_cache.backends.redis import RedisBackend
|
||||||
@@ -73,6 +73,12 @@ async def cache_html(request: Request):
|
|||||||
return templates.TemplateResponse("index.html", {"request": request, "ret": await get_ret()})
|
return templates.TemplateResponse("index.html", {"request": request, "ret": await get_ret()})
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/cache_response_obj")
|
||||||
|
@cache(namespace="test", expire=5)
|
||||||
|
async def cache_response_obj():
|
||||||
|
return JSONResponse({"a": 1})
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
async def startup():
|
async def startup():
|
||||||
pool = ConnectionPool.from_url(url="redis://redis")
|
pool = ConnectionPool.from_url(url="redis://redis")
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from typing import Any
|
|||||||
|
|
||||||
import pendulum
|
import pendulum
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
from starlette.responses import JSONResponse
|
||||||
from starlette.templating import _TemplateResponse as TemplateResponse
|
from starlette.templating import _TemplateResponse as TemplateResponse
|
||||||
|
|
||||||
CONVERTERS = {
|
CONVERTERS = {
|
||||||
@@ -52,6 +53,8 @@ class Coder:
|
|||||||
class JsonCoder(Coder):
|
class JsonCoder(Coder):
|
||||||
@classmethod
|
@classmethod
|
||||||
def encode(cls, value: Any) -> str:
|
def encode(cls, value: Any) -> str:
|
||||||
|
if isinstance(value, JSONResponse):
|
||||||
|
return value.body
|
||||||
return json.dumps(value, cls=JsonEncoder)
|
return json.dumps(value, cls=JsonEncoder)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ def cache(
|
|||||||
# if the wrapped function does NOT have request or response in its function signature,
|
# if the wrapped function does NOT have request or response in its function signature,
|
||||||
# make sure we don't pass them in as keyword arguments
|
# make sure we don't pass them in as keyword arguments
|
||||||
if not request_param:
|
if not request_param:
|
||||||
kwargs.pop("request")
|
kwargs.pop("request", None)
|
||||||
if not response_param:
|
if not response_param:
|
||||||
kwargs.pop("response")
|
kwargs.pop("response", None)
|
||||||
|
|
||||||
if inspect.iscoroutinefunction(func):
|
if inspect.iscoroutinefunction(func):
|
||||||
# async, return as is.
|
# async, return as is.
|
||||||
@@ -123,13 +123,20 @@ def cache(
|
|||||||
args=args,
|
args=args,
|
||||||
kwargs=copy_kwargs,
|
kwargs=copy_kwargs,
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
ttl, ret = await backend.get_with_ttl(cache_key)
|
ttl, ret = await backend.get_with_ttl(cache_key)
|
||||||
|
except ConnectionError:
|
||||||
|
ttl, ret = 0, None
|
||||||
if not request:
|
if not request:
|
||||||
if ret is not None:
|
if ret is not None:
|
||||||
return coder.decode(ret)
|
return coder.decode(ret)
|
||||||
ret = await ensure_async_func(*args, **kwargs)
|
ret = await ensure_async_func(*args, **kwargs)
|
||||||
await backend.set(cache_key, coder.encode(ret), expire or FastAPICache.get_expire())
|
try:
|
||||||
|
await backend.set(
|
||||||
|
cache_key, coder.encode(ret), expire or FastAPICache.get_expire()
|
||||||
|
)
|
||||||
|
except ConnectionError:
|
||||||
|
pass
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
if request.method != "GET":
|
if request.method != "GET":
|
||||||
@@ -148,7 +155,10 @@ def cache(
|
|||||||
|
|
||||||
ret = await ensure_async_func(*args, **kwargs)
|
ret = await ensure_async_func(*args, **kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
await backend.set(cache_key, coder.encode(ret), expire or FastAPICache.get_expire())
|
await backend.set(cache_key, coder.encode(ret), expire or FastAPICache.get_expire())
|
||||||
|
except ConnectionError:
|
||||||
|
pass
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
return inner
|
return inner
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "fastapi-cache2"
|
name = "fastapi-cache2"
|
||||||
version = "0.2.1"
|
version = "0.2.0"
|
||||||
description = "Cache for FastAPI"
|
description = "Cache for FastAPI"
|
||||||
authors = ["long2ice <long2ice@gmail.com>"]
|
authors = ["long2ice <long2ice@gmail.com>"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
@@ -23,7 +23,7 @@ aiomcache = { version = "*", optional = true }
|
|||||||
pendulum = "*"
|
pendulum = "*"
|
||||||
aiobotocore = { version = "^1.4.1", optional = true }
|
aiobotocore = { version = "^1.4.1", optional = true }
|
||||||
typing-extensions = { version = ">=4.1.0", markers = "python_version < \"3.10\"" }
|
typing-extensions = { version = ">=4.1.0", markers = "python_version < \"3.10\"" }
|
||||||
aiohttp= { version = ">=3.8.3", markers = "python_version >= \"3.11\""}
|
aiohttp = { version = ">=3.8.3", markers = "python_version >= \"3.11\"" }
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
flake8 = "*"
|
flake8 = "*"
|
||||||
|
|||||||
@@ -57,3 +57,13 @@ def test_sync() -> None:
|
|||||||
with TestClient(app) as client:
|
with TestClient(app) as client:
|
||||||
response = client.get("/sync-me")
|
response = client.get("/sync-me")
|
||||||
assert response.json() == 42
|
assert response.json() == 42
|
||||||
|
|
||||||
|
|
||||||
|
def test_cache_response_obj() -> None:
|
||||||
|
with TestClient(app) as client:
|
||||||
|
cache_response = client.get("cache_response_obj")
|
||||||
|
assert cache_response.json() == {"a": 1}
|
||||||
|
get_cache_response = client.get("cache_response_obj")
|
||||||
|
assert get_cache_response.json() == {"a": 1}
|
||||||
|
assert get_cache_response.headers.get("cache-control")
|
||||||
|
assert get_cache_response.headers.get("etag")
|
||||||
|
|||||||
Reference in New Issue
Block a user