From a8fbf2b340de73f9c50da01001e0d41db5904cee Mon Sep 17 00:00:00 2001 From: vvanglro <947001731@qq.com> Date: Fri, 4 Nov 2022 16:56:43 +0800 Subject: [PATCH 1/7] fix: request / router KeyError --- fastapi_cache/decorator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fastapi_cache/decorator.py b/fastapi_cache/decorator.py index 0aea981..fb08e00 100644 --- a/fastapi_cache/decorator.py +++ b/fastapi_cache/decorator.py @@ -78,9 +78,9 @@ def cache( # 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 if not request_param: - kwargs.pop("request") + kwargs.pop("request", None) if not response_param: - kwargs.pop("response") + kwargs.pop("response", None) if inspect.iscoroutinefunction(func): # async, return as is. From 4cb4afeff052064b282b4596e6ef8aee4acf45bf Mon Sep 17 00:00:00 2001 From: vvanglro <947001731@qq.com> Date: Fri, 4 Nov 2022 17:31:37 +0800 Subject: [PATCH 2/7] feat: support cache JSONResponse --- fastapi_cache/coder.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fastapi_cache/coder.py b/fastapi_cache/coder.py index b6cd683..7dd5dfe 100644 --- a/fastapi_cache/coder.py +++ b/fastapi_cache/coder.py @@ -6,6 +6,7 @@ from typing import Any, Dict, Union import pendulum from fastapi.encoders import jsonable_encoder +from starlette.responses import JSONResponse from starlette.templating import _TemplateResponse as TemplateResponse CONVERTERS = { @@ -51,6 +52,8 @@ class Coder: class JsonCoder(Coder): @classmethod def encode(cls, value: Any) -> str: + if isinstance(value, JSONResponse): + return value.body return json.dumps(value, cls=JsonEncoder) @classmethod From 2710129c4e85dfd2770108aef6d3cdd11bfa8c5f Mon Sep 17 00:00:00 2001 From: vvanglro <947001731@qq.com> Date: Fri, 4 Nov 2022 17:34:20 +0800 Subject: [PATCH 3/7] feat: cache response obj add test case --- examples/in_memory/main.py | 9 ++++++++- examples/redis/main.py | 8 +++++++- tests/test_decorator.py | 10 ++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/examples/in_memory/main.py b/examples/in_memory/main.py index 1df6bc2..471899e 100644 --- a/examples/in_memory/main.py +++ b/examples/in_memory/main.py @@ -2,7 +2,7 @@ import pendulum import uvicorn from fastapi import FastAPI from starlette.requests import Request -from starlette.responses import Response +from starlette.responses import Response, JSONResponse from fastapi_cache import FastAPICache from fastapi_cache.backends.inmemory import InMemoryBackend @@ -42,6 +42,7 @@ async def get_date(): async def get_datetime(request: Request, response: Response): return {"now": pendulum.now()} + @app.get("/sync-me") @cache(namespace="test") def sync_me(): @@ -50,6 +51,12 @@ def sync_me(): 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") async def startup(): FastAPICache.init(InMemoryBackend()) diff --git a/examples/redis/main.py b/examples/redis/main.py index 773416e..98d1401 100644 --- a/examples/redis/main.py +++ b/examples/redis/main.py @@ -6,7 +6,7 @@ import uvicorn from fastapi import FastAPI from redis.asyncio.connection import ConnectionPool from starlette.requests import Request -from starlette.responses import Response +from starlette.responses import Response, JSONResponse from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates @@ -73,6 +73,12 @@ async def cache_html(request: Request): }) +@app.get("/cache_response_obj") +@cache(namespace="test", expire=5) +async def cache_response_obj(): + return JSONResponse({"a": 1}) + + @app.on_event("startup") async def startup(): pool = ConnectionPool.from_url(url="redis://redis") diff --git a/tests/test_decorator.py b/tests/test_decorator.py index 6d54aef..9ebb1ac 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -58,3 +58,13 @@ def test_sync() -> None: with TestClient(app) as client: response = client.get("/sync-me") 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") From c1484a46fd80ece74d4635493405ddb6c43c893d Mon Sep 17 00:00:00 2001 From: vvanglro <947001731@qq.com> Date: Fri, 4 Nov 2022 17:44:41 +0800 Subject: [PATCH 4/7] feat: CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a136d3b..c7ab938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ ### 0.2.1 - Support cache jinja2 template response. +- Support cache `JSONResponse` +- Add `py.typed` file and type hints +- Add TestCase +- Fix cache decorate sync function ### 0.2.0 From b420f26e9b6d42c2f6d8698ba57ad822d5e3bf57 Mon Sep 17 00:00:00 2001 From: hackjammer Date: Thu, 5 Jan 2023 18:44:40 +0000 Subject: [PATCH 5/7] transparent passthrough in the event of backend connection issues --- CHANGELOG.md | 1 + fastapi_cache/decorator.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a136d3b..47c7187 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### 0.2.1 - Support cache jinja2 template response. +- Transparently handle backend connection failures. ### 0.2.0 diff --git a/fastapi_cache/decorator.py b/fastapi_cache/decorator.py index 6869885..bf1ccff 100644 --- a/fastapi_cache/decorator.py +++ b/fastapi_cache/decorator.py @@ -123,13 +123,18 @@ def cache( args=args, kwargs=copy_kwargs, ) - - ttl, ret = await backend.get_with_ttl(cache_key) + try: + ttl, ret = await backend.get_with_ttl(cache_key) + except ConnectionError: + ttl, ret = 0, None if not request: if ret is not None: return coder.decode(ret) 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 if request.method != "GET": @@ -148,7 +153,10 @@ def cache( 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 inner From 0d964fcf9f6cec23facc6ce0022d98475dfa4703 Mon Sep 17 00:00:00 2001 From: vvanglro <947001731@qq.com> Date: Sat, 7 Jan 2023 13:55:41 +0800 Subject: [PATCH 6/7] fix: remove unused --- examples/redis/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/redis/main.py b/examples/redis/main.py index e4c0ceb..19a272a 100644 --- a/examples/redis/main.py +++ b/examples/redis/main.py @@ -4,7 +4,7 @@ import pendulum import redis.asyncio as redis import uvicorn from fastapi import FastAPI -from starlette.responses import Response, JSONResponse +from starlette.responses import JSONResponse from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates From 59a47b7faec895b56dfd6f3d75a286d539dc3cd6 Mon Sep 17 00:00:00 2001 From: long2ice Date: Wed, 11 Jan 2023 21:20:41 +0800 Subject: [PATCH 7/7] chore: set version 0.2.0 --- CHANGELOG.md | 9 +++------ examples/in_memory/main.py | 2 +- examples/redis/main.py | 3 +-- fastapi_cache/decorator.py | 4 +++- pyproject.toml | 4 ++-- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a047f..f52d7f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,10 @@ ## 0.2 -### 0.2.1 +### 0.2.0 +- Make `request` and `response` optional. +- Add typing info to the `cache` decorator. - Support cache jinja2 template response. - Support cache `JSONResponse` - Add `py.typed` file and type hints @@ -11,11 +13,6 @@ - Fix cache decorate sync function - Transparently handle backend connection failures. -### 0.2.0 - -- Make `request` and `response` optional. -- Add typing info to the `cache` decorator. - ## 0.1 ### 0.1.10 diff --git a/examples/in_memory/main.py b/examples/in_memory/main.py index 471899e..68b6e17 100644 --- a/examples/in_memory/main.py +++ b/examples/in_memory/main.py @@ -2,7 +2,7 @@ import pendulum import uvicorn from fastapi import FastAPI from starlette.requests import Request -from starlette.responses import Response, JSONResponse +from starlette.responses import JSONResponse, Response from fastapi_cache import FastAPICache from fastapi_cache.backends.inmemory import InMemoryBackend diff --git a/examples/redis/main.py b/examples/redis/main.py index 19a272a..e05eda5 100644 --- a/examples/redis/main.py +++ b/examples/redis/main.py @@ -4,13 +4,12 @@ import pendulum import redis.asyncio as redis import uvicorn from fastapi import FastAPI -from starlette.responses import JSONResponse from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from redis.asyncio.connection import ConnectionPool from starlette.requests import Request -from starlette.responses import Response +from starlette.responses import JSONResponse, Response from fastapi_cache import FastAPICache from fastapi_cache.backends.redis import RedisBackend diff --git a/fastapi_cache/decorator.py b/fastapi_cache/decorator.py index bcfe9b5..5018b1e 100644 --- a/fastapi_cache/decorator.py +++ b/fastapi_cache/decorator.py @@ -132,7 +132,9 @@ def cache( return coder.decode(ret) 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 diff --git a/pyproject.toml b/pyproject.toml index 36998f7..292d273 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "fastapi-cache2" -version = "0.2.1" +version = "0.2.0" description = "Cache for FastAPI" authors = ["long2ice "] license = "Apache-2.0" @@ -23,7 +23,7 @@ aiomcache = { version = "*", optional = true } pendulum = "*" aiobotocore = { version = "^1.4.1", optional = true } 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] flake8 = "*"