23 Commits

Author SHA1 Message Date
Gary Gale
6af6dd3407 docs: change log for 0.2.3 2025-01-18 11:43:18 +00:00
Gary Gale
8058ed6bd0 docs: add news files for all recent changes 2025-01-18 11:42:10 +00:00
Gary Gale
6353cbb20a docs: add more structure to Towncrier change log 2025-01-18 11:40:48 +00:00
Gary Gale
5ca866301f build: bump version to 0.2.3 2025-01-18 10:47:06 +00:00
Gary Gale
20935c7b0b fix #469: Bump towncrier from 22.12.0 to 24.8.0 2025-01-18 10:22:10 +00:00
Gary Gale
6f4876ff7d fix #509: fix up linting and tests due to aiobotocore 2.18.0 changes 2025-01-18 09:52:34 +00:00
Gary Gale
7dade61a49 fix #507: Bump pyright from 1.1.381 to 1.1.392.post0 2025-01-17 17:53:54 +00:00
Gary Gale
a7c47cf7dd fix #493: Bump uvicorn from 0.30.6 to 0.33.0 2025-01-17 17:51:33 +00:00
Gary Gale
1185ac33ab fix #490: Bump redis from 5.0.8 to 5.2.1 2025-01-17 17:50:45 +00:00
Gary Gale
e0eb1ac878 fix #486: Bump fastapi from 0.115.0 to 0.115.6 2025-01-17 17:49:38 +00:00
Gary Gale
601a64c067 fix #466: Bump tox from 4.20.0 to 4.23.2 2025-01-17 17:45:26 +00:00
Gary Gale
060d1dad00 fix #464: Bump dependabot/fetch-metadata from 1 to 2 2025-01-17 17:30:54 +00:00
Gary Gale
b39792f967 fix #378: Bump actions/cache from 3 to 4 2025-01-17 17:23:49 +00:00
Gary Gale
ce1ef73f08 fix #359: Bump actions/download-artifact from 2 to 4 2025-01-17 17:23:08 +00:00
Gary Gale
5651e50534 fix #360: Bump actions/upload-artifact from 3 to 4 2025-01-17 17:22:28 +00:00
Gary Gale
8356502a00 fix #358: Bump github/codeql-action from 2 to 3 2025-01-17 17:21:23 +00:00
Gary Gale
f3293ad220 fix #293: Bump actions/checkout from 2 to 4 2025-01-17 17:17:14 +00:00
Gary Gale
6ea931a166 test #459: only run GHA pipeline build and publish jobs on main 2025-01-17 16:51:49 +00:00
Gary Gale
73a51fb980 test #459: allow GHA pipeline to run on release-* branches 2025-01-17 16:40:50 +00:00
Gary Gale
0ced61788b test #459: ensure CI/CD pipeline lint and test workflows pass when run manually 2025-01-17 16:30:18 +00:00
Gary Gale
7513c47ca0 test #459: fix failing non HTTP GET tests which called unimplemented example code endpoints 2025-01-17 16:23:06 +00:00
Gary Gale
b2e7bad0bb test #459: fix failing datetime and date logic tests 2025-01-17 16:20:43 +00:00
Gary Gale
06f4a09d6c test #459: (hopefully temporarily) disable failing tests for the JSON coder which rely on decoding the (currently unsupported) custom non-JSON data types 2025-01-17 16:19:38 +00:00
17 changed files with 1087 additions and 1254 deletions

View File

@@ -3,6 +3,7 @@ on:
push: push:
branches: branches:
- main - main
- 'release-*'
tags: tags:
- 'v*' - 'v*'
pull_request: pull_request:
@@ -14,7 +15,7 @@ jobs:
name: Linter name: Linter
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Install Poetry - name: Install Poetry
run: pipx install poetry run: pipx install poetry
- name: Setup Python - name: Setup Python
@@ -24,7 +25,7 @@ jobs:
python-version: '3.x' python-version: '3.x'
cache: poetry cache: poetry
- name: Cache mypy cache - name: Cache mypy cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: .mypy_cache path: .mypy_cache
key: ${{ runner.os }}-mypy-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} key: ${{ runner.os }}-mypy-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock') }}
@@ -48,7 +49,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Install Poetry - name: Install Poetry
run: pipx install poetry run: pipx install poetry
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
@@ -75,11 +76,12 @@ jobs:
build: build:
name: Build distributions name: Build distributions
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [test-summary] needs: [test-summary]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Install Poetry - name: Install Poetry
run: pipx install poetry run: pipx install poetry
- name: Setup Python - name: Setup Python
@@ -91,7 +93,7 @@ jobs:
run: run:
make build make build
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: dist name: dist
path: dist path: dist
@@ -110,7 +112,7 @@ jobs:
id-token: write id-token: write
steps: steps:
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v2 uses: actions/download-artifact@v4
with: with:
name: dist name: dist
path: dist path: dist

View File

@@ -20,15 +20,15 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v3
with: with:
languages: python languages: python
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v3
with: with:
category: "/language:python" category: "/language:python"

View File

@@ -12,9 +12,9 @@ jobs:
steps: steps:
- name: Dependabot metadata - name: Dependabot metadata
id: dependabot-metadata id: dependabot-metadata
uses: dependabot/fetch-metadata@v1 uses: dependabot/fetch-metadata@v2
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Approve PR - name: Approve PR
# only auto-approve direct deps that are minor or patch updates # only auto-approve direct deps that are minor or patch updates
# dependency type is indirect, direct:development or direct:production # dependency type is indirect, direct:development or direct:production

View File

@@ -23,7 +23,7 @@ jobs:
|| contains(github.event.pull_request.labels.*.name, 'skip-changelog') || contains(github.event.pull_request.labels.*.name, 'skip-changelog')
) )
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Install Poetry - name: Install Poetry
run: pipx install poetry run: pipx install poetry
- name: Setup Python - name: Setup Python

View File

@@ -8,6 +8,27 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang
<!-- towncrier release notes start --> <!-- towncrier release notes start -->
## [0.2.2](https://github.com/long2ice/fastapi-cache/tree/0.2.2) - 2025-01-18
### Bug Fixes
- Fix failing tests (#459) [#459](https://github.com/long2ice/fastapi-cache/issues/459)
### Build Changes
- Use `importlib.metadata` to include project version string as `fastapi_cache.__version__`. [#172](https://github.com/long2ice/fastapi-cache/issues/172)
- (dependabot) Bump actions/checkout from 2 to 4 [#293](https://github.com/long2ice/fastapi-cache/issues/293)
- (dependabot) Bump actions/download-artifact from 2 to 4 (#359) [#359](https://github.com/long2ice/fastapi-cache/issues/359)
- (dependabot) Bump actions/upload-artifact from 3 to 4 (#360) [#360](https://github.com/long2ice/fastapi-cache/issues/360)
- (dependabot) Bump actions/cache from 3 to 4 (#378) [#378](https://github.com/long2ice/fastapi-cache/issues/378)
- (dependabot) Bump dependabot/fetch-metadata from 1 to 2 (#464) [#464](https://github.com/long2ice/fastapi-cache/issues/464)
- (dependabot) Bump tox from 4.20.0 to 4.23.2 (#466) [#466](https://github.com/long2ice/fastapi-cache/issues/466)
- (dependabot) Bump fastapi from 0.115.0 to 0.115.6 (#486) [#486](https://github.com/long2ice/fastapi-cache/issues/486)
- (dependabot) Bump redis from 5.0.8 to 5.2.1 (#490) [#490](https://github.com/long2ice/fastapi-cache/issues/490)
- (dependabot) Bump uvicorn from 0.30.6 to 0.33.0 (#493) [#493](https://github.com/long2ice/fastapi-cache/issues/493)
- (dependabot) Bump pyright from 1.1.381 to 1.1.392.post0 (#507) [#507](https://github.com/long2ice/fastapi-cache/issues/507)
- (dependabot) Bump towncrier from 22.12.0 to 24.8.0 (#509) [#509](https://github.com/long2ice/fastapi-cache/issues/509)
## 0.2 ## 0.2
### 0.2.1 ### 0.2.1

View File

@@ -50,42 +50,13 @@ or
### Quick Start ### Quick Start
Using in-memory backend:
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi_cache import FastAPICache
from fastapi_cache.backends.inmemory import InMemoryBackend
from fastapi_cache.decorator import cache
@asynccontextmanager
async def lifespan(app: FastAPI):
FastAPICache.init(InMemoryBackend(), prefix="fastapi-cache")
yield
app = FastAPI(lifespan=lifespan)
@cache()
async def get_cache():
return 1
@app.get("/")
@cache(expire=60)
async def index():
return dict(hello="world")
```
or [Redis](https://redis.io) backend:
```python ```python
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from fastapi import FastAPI from fastapi import FastAPI
from starlette.requests import Request
from starlette.responses import 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
@@ -95,7 +66,7 @@ from redis import asyncio as aioredis
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(_: FastAPI) -> AsyncIterator[None]:
redis = aioredis.from_url("redis://localhost") redis = aioredis.from_url("redis://localhost")
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache") FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
yield yield
@@ -114,6 +85,7 @@ async def get_cache():
async def index(): async def index():
return dict(hello="world") return dict(hello="world")
``` ```
### Initialization ### Initialization
First you must call `FastAPICache.init` during startup FastAPI startup; this is where you set global configuration. First you must call `FastAPICache.init` during startup FastAPI startup; this is where you set global configuration.

View File

@@ -1 +0,0 @@
Use `importlib.metadata` to include project version string as `fastapi_cache.__version__`.

View File

@@ -1 +0,0 @@
Delete method support for memcached backend (from @xodiumx)

View File

@@ -1,5 +1,5 @@
import datetime import datetime
from typing import TYPE_CHECKING, Optional, Tuple from typing import TYPE_CHECKING, Optional, Tuple, Union
from aiobotocore.client import AioBaseClient from aiobotocore.client import AioBaseClient
from aiobotocore.session import AioSession, get_session from aiobotocore.session import AioSession, get_session
@@ -30,7 +30,7 @@ class DynamoBackend(Backend):
>> FastAPICache.init(dynamodb) >> FastAPICache.init(dynamodb)
""" """
client: DynamoDBClient client: Union[DynamoDBClient, None]
session: AioSession session: AioSession
table_name: str table_name: str
region: Optional[str] region: Optional[str]
@@ -46,58 +46,63 @@ class DynamoBackend(Backend):
).__aenter__() ).__aenter__()
async def close(self) -> None: async def close(self) -> None:
self.client = await self.client.__aexit__(None, None, None) if self.client:
await self.client.__aexit__(None, None, None)
self.client = None
async def get_with_ttl(self, key: str) -> Tuple[int, Optional[bytes]]: async def get_with_ttl(self, key: str) -> Tuple[int, Optional[bytes]]:
response = await self.client.get_item(TableName=self.table_name, Key={"key": {"S": key}}) if self.client:
response = await self.client.get_item(TableName=self.table_name, Key={"key": {"S": key}})
if "Item" in response: if "Item" in response:
value = response["Item"].get("value", {}).get("B") value = response["Item"].get("value", {}).get("B")
ttl = response["Item"].get("ttl", {}).get("N") ttl = response["Item"].get("ttl", {}).get("N")
if not ttl: if not ttl:
return -1, value return -1, value
# It's only eventually consistent so we need to check ourselves # It's only eventually consistent so we need to check ourselves
expire = int(ttl) - int(datetime.datetime.now().timestamp()) expire = int(ttl) - int(datetime.datetime.now().timestamp())
if expire > 0: if expire > 0:
return expire, value return expire, value
return 0, None return 0, None
async def get(self, key: str) -> Optional[bytes]: async def get(self, key: str) -> Optional[bytes]:
response = await self.client.get_item(TableName=self.table_name, Key={"key": {"S": key}}) if self.client:
if "Item" in response: response = await self.client.get_item(TableName=self.table_name, Key={"key": {"S": key}})
return response["Item"].get("value", {}).get("B") if "Item" in response:
return response["Item"].get("value", {}).get("B")
return None return None
async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> None: async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> None:
ttl = ( if self.client:
{ ttl = (
"ttl": { {
"N": str( "ttl": {
int( "N": str(
( int(
datetime.datetime.now() + datetime.timedelta(seconds=expire) (
).timestamp() datetime.datetime.now() + datetime.timedelta(seconds=expire)
).timestamp()
)
) )
) }
} }
} if expire
if expire else {}
else {} )
)
await self.client.put_item( await self.client.put_item(
TableName=self.table_name, TableName=self.table_name,
Item={ Item={
**{ **{
"key": {"S": key}, "key": {"S": key},
"value": {"B": value}, "value": {"B": value},
},
**ttl,
}, },
**ttl, )
},
)
async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int: async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
raise NotImplementedError raise NotImplementedError

View File

@@ -19,11 +19,4 @@ class MemcachedBackend(Backend):
await self.mcache.set(key.encode(), value, exptime=expire or 0) await self.mcache.set(key.encode(), value, exptime=expire or 0)
async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int: async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
is_deleted = False raise NotImplementedError
if key:
is_deleted = await self.mcache.delete(key=key.encode())
else:
await self.mcache.flush_all()
is_deleted = True
return int(is_deleted)

View File

@@ -90,7 +90,7 @@ def cache(
key_builder: Optional[KeyBuilder] = None, key_builder: Optional[KeyBuilder] = None,
namespace: str = "", namespace: str = "",
injected_dependency_namespace: str = "__fastapi_cache", injected_dependency_namespace: str = "__fastapi_cache",
) -> Callable[[Union[Callable[P, Awaitable[R]], Callable[P, R]]], Callable[P, Awaitable[Union[R, Response]]]]: ) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[Union[R, Response]]]]:
""" """
cache all function cache all function
:param injected_dependency_namespace: :param injected_dependency_namespace:
@@ -114,7 +114,7 @@ def cache(
) )
def wrapper( def wrapper(
func: Union[Callable[P, Awaitable[R]], Callable[P, R]] func: Callable[P, Awaitable[R]]
) -> Callable[P, Awaitable[Union[R, Response]]]: ) -> Callable[P, Awaitable[Union[R, Response]]]:
# get_typed_signature ensures that any forward references are resolved first # get_typed_signature ensures that any forward references are resolved first
wrapped_signature = get_typed_signature(func) wrapped_signature = get_typed_signature(func)
@@ -142,7 +142,7 @@ def cache(
# unintuitively, we have to await once here, so that caller # unintuitively, we have to await once here, so that caller
# does not have to await twice. See # does not have to await twice. See
# https://stackoverflow.com/a/59268198/532513 # https://stackoverflow.com/a/59268198/532513
return await func(*args, **kwargs) # type: ignore[no-any-return] return await func(*args, **kwargs)
else: else:
# sync, wrap in thread and return async # sync, wrap in thread and return async
# see above why we have to await even although caller also awaits. # see above why we have to await even although caller also awaits.
@@ -221,8 +221,7 @@ def cache(
return response return response
result = cast(R, coder.decode_as_type(cached, type_=return_type)) result = cast(R, coder.decode_as_type(cached, type_=return_type))
if response and isinstance(result, Response):
result.headers.update(response.headers)
return result return result
inner.__signature__ = _augment_signature(wrapped_signature, *to_inject) # type: ignore[attr-defined] inner.__signature__ = _augment_signature(wrapped_signature, *to_inject) # type: ignore[attr-defined]

2085
poetry.lock generated

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "fastapi-cache2" name = "fastapi-cache2"
version = "0.2.2" version = "0.2.3"
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,17 +23,17 @@ importlib-metadata = { version = "^6.6.0", python = "<3.8" }
pendulum = "^3.0.0" pendulum = "^3.0.0"
aiomcache = { version = "^0.8.2", optional = true } aiomcache = { version = "^0.8.2", optional = true }
aiobotocore = {version = "^2.13.1", optional = true} aiobotocore = {version = "^2.13.1", optional = true}
redis = {version = "^5.2.0", extras = ["redis"]} redis = {version = "^5.0.8", extras = ["redis"]}
[tool.poetry.group.linting] [tool.poetry.group.linting]
optional = true optional = true
[tool.poetry.group.linting.dependencies] [tool.poetry.group.linting.dependencies]
mypy = { version = "^1.13.0", python = "^3.10" } mypy = { version = "^1.2.0", python = "^3.10" }
pyright = { version = "^1.1.388", python = "^3.10" } pyright = { version = "^1.1.373", python = "^3.10" }
types-aiobotocore = { extras = ["dynamodb"], version = "^2.5.0.post2", python = "^3.10" } types-aiobotocore = { extras = ["dynamodb"], version = "^2.5.0.post2", python = "^3.10" }
types-redis = { version = "^4.5.4.2", python = "^3.10" } types-redis = { version = "^4.5.4.2", python = "^3.10" }
ruff = { version = ">=0.3.2", python = "^3.10" } ruff = { version = ">=0.0.267,<0.1.2", python = "^3.10" }
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
pytest = "*" pytest = "*"
@@ -41,7 +41,7 @@ requests = "*"
coverage = ">=6.5,<8.0" coverage = ">=6.5,<8.0"
httpx = "*" httpx = "*"
tox = "^4.5.1" tox = "^4.5.1"
towncrier = "^22.12.0" towncrier = "24.8.0"
[tool.poetry.group.distributing] [tool.poetry.group.distributing]
optional = true optional = true
@@ -88,6 +88,36 @@ template = "changelog.d/changelog_template.jinja"
title_format = "## [{version}](https://github.com/long2ice/fastapi-cache/tree/{version}) - {project_date}" title_format = "## [{version}](https://github.com/long2ice/fastapi-cache/tree/{version}) - {project_date}"
issue_format = "[#{issue}](https://github.com/long2ice/fastapi-cache/issues/{issue})" issue_format = "[#{issue}](https://github.com/long2ice/fastapi-cache/issues/{issue})"
[[tool.towncrier.type]]
directory = 'feature'
name = 'New Features'
showcontent = true
[[tool.towncrier.type]]
directory = 'removed'
name = 'Deprecated/Removed Features'
showcontent = true
[[tool.towncrier.type]]
directory = 'fix'
name = 'Bug Fixes'
showcontent = true
[[tool.towncrier.type]]
directory = 'build'
name = 'Build Changes'
showcontent = true
[[tool.towncrier.type]]
directory = 'doc'
name = 'Documentation'
showcontent = true
[[tool.towncrier.type]]
directory = 'misc'
name = 'Everything Else'
showcontent = true
[tool.pyright] [tool.pyright]
strict = ["fastapi_cache", "tests"] strict = ["fastapi_cache", "tests"]
pythonVersion = "3.8" pythonVersion = "3.8"
@@ -96,9 +126,9 @@ pythonVersion = "3.8"
addopts = "-p no:warnings" addopts = "-p no:warnings"
[tool.ruff] [tool.ruff]
ignore = ["E501"]
line-length = 80 line-length = 80
lint.ignore = ["E501"] select = [
lint.select = [
"B", # flake8-bugbear "B", # flake8-bugbear
"C4", # flake8-comprehensions "C4", # flake8-comprehensions
"E", # pycodestyle errors "E", # pycodestyle errors

View File

@@ -1,10 +1,10 @@
[tool.ruff] [tool.ruff]
extend = "../pyproject.toml" extend = "../pyproject.toml"
lint.extend-select = [ extend-select = [
"PT", # flake8-pytest-style "PT", # flake8-pytest-style
] ]
lint.ignore = ["S101"] ignore = ["S101"]
[tool.ruff.lint.isort] [tool.ruff.isort]
known-first-party = ["examples", "fastapi_cache"] known-first-party = ["examples", "fastapi_cache"]

View File

@@ -1,5 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Type from typing import Any, Optional, Type
import pytest import pytest
from pydantic import BaseModel from pydantic import BaseModel
@@ -41,7 +41,9 @@ def test_pickle_coder(value: Any) -> None:
assert decoded_value == value assert decoded_value == value
# vicchi: 2024/11/09 - some values commented out until #460 is resolved # vicchi: 2025/01/17
# test values and tests commented out until #460 is resolved due to removal
# of support for decoding JSON to a custom type
@pytest.mark.parametrize( @pytest.mark.parametrize(
("value", "return_type"), ("value", "return_type"),
[ [
@@ -50,9 +52,8 @@ def test_pickle_coder(value: Any) -> None:
("some_string", str), ("some_string", str),
("some_string", None), ("some_string", None),
# ((1, 2), Tuple[int, int]), # ((1, 2), Tuple[int, int]),
([1, 2, 3], List[int]),
([1, 2, 3], None), ([1, 2, 3], None),
({"some_key": 1, "other_key": 2}, Dict[str, int]), # ({"some_key": 1, "other_key": 2}, None),
({"some_key": 1, "other_key": 2}, None), ({"some_key": 1, "other_key": 2}, None),
# (DCItem(name="foo", price=42.0, description="some dataclass item", tax=0.2), DCItem), # (DCItem(name="foo", price=42.0, description="some dataclass item", tax=0.2), DCItem),
# (PDItem(name="foo", price=42.0, description="some pydantic item", tax=0.2), PDItem), # (PDItem(name="foo", price=42.0, description="some pydantic item", tax=0.2), PDItem),
@@ -65,7 +66,6 @@ def test_json_coder(value: Any, return_type: Type[Any]) -> None:
assert decoded_value == value assert decoded_value == value
# vicchi: 2024/11/09 - test commented out until #460 is resolved
# def test_json_coder_validation_error() -> None: # def test_json_coder_validation_error() -> None:
# invalid = b'{"name": "incomplete"}' # invalid = b'{"name": "incomplete"}'
# with pytest.raises(ValidationError): # with pytest.raises(ValidationError):

View File

@@ -23,6 +23,8 @@ def test_datetime() -> None:
response = client.get("/datetime") response = client.get("/datetime")
assert response.headers.get("X-FastAPI-Cache") == "MISS" assert response.headers.get("X-FastAPI-Cache") == "MISS"
now = response.json().get("now") now = response.json().get("now")
# now_ = pendulum.now()
# assert pendulum.parse(now) == now_
now_ = pendulum.parse(now) now_ = pendulum.parse(now)
response = client.get("/datetime") response = client.get("/datetime")
assert response.headers.get("X-FastAPI-Cache") == "HIT" assert response.headers.get("X-FastAPI-Cache") == "HIT"
@@ -30,10 +32,12 @@ def test_datetime() -> None:
assert pendulum.parse(now) == now_ assert pendulum.parse(now) == now_
time.sleep(3) time.sleep(3)
response = client.get("/datetime") response = client.get("/datetime")
# now = response.json().get("now")
assert response.headers.get("X-FastAPI-Cache") == "MISS" assert response.headers.get("X-FastAPI-Cache") == "MISS"
now = response.json().get("now") now = response.json().get("now")
now = pendulum.parse(now) now = pendulum.parse(now)
assert now != now_ assert now != now_
# assert now == pendulum.now()
def test_date() -> None: def test_date() -> None:

View File

@@ -30,7 +30,7 @@ skip_install = true
commands_pre = commands_pre =
poetry install --no-root --with=linting --sync --all-extras poetry install --no-root --with=linting --sync --all-extras
commands = commands =
ruff check . ruff check --show-source .
mypy mypy
pyright pyright