287 Commits

Author SHA1 Message Date
dependabot[bot]
a13c963add Bump importlib-metadata from 8.5.0 to 8.6.1
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.5.0 to 8.6.1.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v8.5.0...v8.6.1)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-21 08:45:52 +00:00
long2ice
157a91359b chore: update deps 2024-09-19 15:38:04 +08:00
long2ice
567934300b ci: remove py37 2024-07-24 23:42:19 +08:00
long2ice
5f985a8506 ci: fix 2024-07-24 22:24:30 +08:00
long2ice
701d83fec3 Merge pull request #425 from PaleNeutron/fix-no-cache
fix #424, no-cache should store the result to cache
2024-07-24 21:46:19 +08:00
long2ice
8b5601cfdc Merge branch 'main' into fix-no-cache 2024-07-24 21:46:11 +08:00
long2ice
00d34e4ad8 Merge pull request #433 from nikstuckenbrock/feature/update-project-docs
Update project docs and examples
2024-07-24 21:42:53 +08:00
Nik Stuckenbrock
552d54c1ca Fix redis example 2024-07-19 15:09:39 +02:00
Nik Stuckenbrock
63f9bdd068 Fix in memory example 2024-07-19 15:08:43 +02:00
Nik Stuckenbrock
5ba03ca6f5 Correct example in README.md 2024-07-19 15:07:23 +02:00
long2ice
865dba19a5 Merge pull request #426 from CharlesPerrotMinotHCHB/patch-2
Switch from on_event to lifespan asynccontextmanager #422
2024-05-13 13:59:39 +08:00
Charles Perrot-Minot
f203d23194 Switch from on_event to lifespan asynccontextmanager
on_event is now deprecated, and to be replaced with lifespan: https://fastapi.tiangolo.com/advanced/events/
2024-05-12 22:57:14 -07:00
John Lyu
2739b2d0fe lint 2024-05-09 15:01:55 +08:00
John Lyu
74e827d3de fix tests 2024-05-09 14:51:58 +08:00
John Lyu
6ef59f0eb3 Merge branch 'fix-no-cache' into fix 2024-05-09 14:44:15 +08:00
John Lyu
6f8994b843 fix test 2024-05-09 14:43:53 +08:00
John Lyu
120553a36f version 2024-05-09 14:42:56 +08:00
John Lyu
af6301fffa Merge branch 'fix-no-cache' into fix 2024-05-09 14:34:03 +08:00
John Lyu
e4a0df62dd fix #424, no-cache should store the result to cache 2024-05-09 14:32:02 +08:00
John Lyu
19c4d0271a fix latest bug 2024-05-09 14:09:42 +08:00
long2ice
91ba6d7552 Merge pull request #353 from long2ice/dependabot/github_actions/main/actions/setup-python-5
Bump actions/setup-python from 4 to 5
2023-12-07 21:37:57 +08:00
dependabot[bot]
583df36da1 Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-07 08:11:07 +00:00
dependabot[bot]
005bac9d11 Bump pyright from 1.1.332 to 1.1.333 (#328)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.332 to 1.1.333.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.332...v1.1.333)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-26 08:52:27 +00:00
dependabot[bot]
a315bcf6a7 Bump pytest from 7.4.2 to 7.4.3 (#327)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.4.2 to 7.4.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.4.2...7.4.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-25 08:26:35 +00:00
dependabot[bot]
fab700af41 Bump mypy from 1.6.0 to 1.6.1 (#322)
Bumps [mypy](https://github.com/python/mypy) from 1.6.0 to 1.6.1.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.6.0...v1.6.1)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-20 08:30:32 +00:00
dependabot[bot]
0af1638d54 Bump ruff from 0.1.0 to 0.1.1 (#324)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.0...v0.1.1)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-20 08:26:14 +00:00
dependabot[bot]
8a257f2f7d Bump pyright from 1.1.331 to 1.1.332 (#323)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.331 to 1.1.332.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.331...v1.1.332)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-19 09:12:50 +00:00
dependabot[bot]
2747470ab2 Bump types-aiobotocore from 2.6.0 to 2.7.0 (#321)
Bumps [types-aiobotocore](https://github.com/youtype/mypy_boto3_builder) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/youtype/mypy_boto3_builder/releases)
- [Commits](https://github.com/youtype/mypy_boto3_builder/commits)

---
updated-dependencies:
- dependency-name: types-aiobotocore
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-18 09:06:21 +00:00
dependabot[bot]
c9d4d2afc9 Bump ruff from 0.0.292 to 0.1.0 (#320)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.292 to 0.1.0.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.292...v0.1.0)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-17 08:44:00 +00:00
dependabot[bot]
d465e82c1c Bump pyright from 1.1.330.post0 to 1.1.331 (#319)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.330.post0 to 1.1.331.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.330.post0...v1.1.331)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-12 08:48:47 +00:00
dependabot[bot]
eeef78d3aa Bump mypy from 1.5.1 to 1.6.0 (#318)
Bumps [mypy](https://github.com/python/mypy) from 1.5.1 to 1.6.0.
- [Commits](https://github.com/python/mypy/compare/v1.5.1...v1.6.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 08:29:41 +00:00
dependabot[bot]
42d868542c Bump pyright from 1.1.329 to 1.1.330.post0 (#317)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.329 to 1.1.330.post0.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.329...v1.1.330.post0)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-09 08:58:02 +00:00
dependabot[bot]
d65b12aaf7 Bump ruff from 0.0.290 to 0.0.292 (#313)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.290 to 0.0.292.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.290...v0.0.292)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 08:57:51 +00:00
dependabot[bot]
4ba67db7e9 Bump fastapi from 0.103.1 to 0.103.2 (#312)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.103.1 to 0.103.2.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.103.1...0.103.2)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 08:19:06 +00:00
dependabot[bot]
0ce7c61770 Bump pyright from 1.1.328 to 1.1.329 (#311)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.328 to 1.1.329.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.328...v1.1.329)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-28 08:37:45 +00:00
dependabot[bot]
6983c83124 Bump pyright from 1.1.327 to 1.1.328 (#310)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.327 to 1.1.328.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.327...v1.1.328)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:58:27 +00:00
dependabot[bot]
f54facf5bd Bump types-redis from 4.6.0.6 to 4.6.0.7 (#307)
Bumps [types-redis](https://github.com/python/typeshed) from 4.6.0.6 to 4.6.0.7.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-redis
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 08:16:13 +00:00
dependabot[bot]
a540ee30fe Bump pyright from 1.1.326 to 1.1.327 (#304)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.326 to 1.1.327.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.326...v1.1.327)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 09:08:36 +00:00
dependabot[bot]
3e905353e3 Bump ruff from 0.0.288 to 0.0.290 (#305)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.288 to 0.0.290.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.288...v0.0.290)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 09:03:13 +00:00
dependabot[bot]
e2eedace48 Bump types-redis from 4.6.0.5 to 4.6.0.6 (#301)
Bumps [types-redis](https://github.com/python/typeshed) from 4.6.0.5 to 4.6.0.6.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-redis
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-13 08:59:32 +00:00
dependabot[bot]
0af7761dff Bump pytest from 7.3.2 to 7.4.2 (#297)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.3.2 to 7.4.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.3.2...7.4.2)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-12 08:27:51 +00:00
dependabot[bot]
ddde88c7cf Bump ruff from 0.0.286 to 0.0.288 (#300)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.288.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.288)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-12 08:23:56 +00:00
dependabot[bot]
c2d9308057 Bump pyright from 1.1.325 to 1.1.326 (#296)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.325 to 1.1.326.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.325...v1.1.326)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-08 08:54:12 +00:00
dependabot[bot]
d748abd8c6 Bump fastapi from 0.101.1 to 0.103.1 (#289)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.101.1 to 0.103.1.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.101.1...0.103.1)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-04 08:15:56 +00:00
dependabot[bot]
6586ea96c4 Bump pyright from 1.1.324 to 1.1.325 (#288)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.324 to 1.1.325.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.324...v1.1.325)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-31 08:22:10 +00:00
dependabot[bot]
812ada8620 Bump mypy from 1.5.0 to 1.5.1 (#276)
Bumps [mypy](https://github.com/python/mypy) from 1.5.0 to 1.5.1.
- [Commits](https://github.com/python/mypy/compare/v1.5.0...v1.5.1)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-28 09:07:52 +00:00
dependabot[bot]
762c677786 Bump aiobotocore from 2.5.4 to 2.6.0 (#268)
Bumps [aiobotocore](https://github.com/aio-libs/aiobotocore) from 2.5.4 to 2.6.0.
- [Release notes](https://github.com/aio-libs/aiobotocore/releases)
- [Changelog](https://github.com/aio-libs/aiobotocore/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiobotocore/compare/2.5.4...2.6.0)

---
updated-dependencies:
- dependency-name: aiobotocore
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-28 09:04:05 +00:00
dependabot[bot]
daea1a4b66 Bump ruff from 0.0.285 to 0.0.286 (#286)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.285 to 0.0.286.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.285...v0.0.286)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-28 08:59:42 +00:00
dependabot[bot]
294d6151f9 Bump pyright from 1.1.320 to 1.1.324 (#284)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.320 to 1.1.324.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.320...v1.1.324)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-24 08:44:06 +00:00
dependabot[bot]
8e07be5b5d Bump types-redis from 4.6.0.4 to 4.6.0.5 (#282)
Bumps [types-redis](https://github.com/python/typeshed) from 4.6.0.4 to 4.6.0.5.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-redis
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-23 09:14:37 +00:00
long2ice
4e485d4bdb Merge pull request #280 from s-rigaud/update-readme
📝 Use create_some_model as a function
2023-08-22 10:00:15 +08:00
Samuel Rigaud
9217c85d94 📝 Use create_some_model as a function 2023-08-20 01:27:10 +02:00
dependabot[bot]
24c1f3d187 Bump tox from 4.6.4 to 4.8.0 (#269)
Bumps [tox](https://github.com/tox-dev/tox) from 4.6.4 to 4.8.0.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.6.4...4.8.0)

---
updated-dependencies:
- dependency-name: tox
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-18 08:47:03 +00:00
dependabot[bot]
541ddbc0c1 Bump ruff from 0.0.284 to 0.0.285 (#278)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.284 to 0.0.285.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.284...v0.0.285)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-18 08:42:33 +00:00
dependabot[bot]
3b78f211b2 Bump types-redis from 4.6.0.3 to 4.6.0.4 (#275)
Bumps [types-redis](https://github.com/python/typeshed) from 4.6.0.3 to 4.6.0.4.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-redis
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-17 08:30:23 +00:00
dependabot[bot]
2be6a8cfdc Bump fastapi from 0.101.0 to 0.101.1 (#271)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.101.0 to 0.101.1.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.101.0...0.101.1)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-15 08:40:30 +00:00
dependabot[bot]
14df5a39ed Bump types-aiobotocore from 2.5.4 to 2.6.0 (#267)
Bumps [types-aiobotocore](https://github.com/youtype/mypy_boto3_builder) from 2.5.4 to 2.6.0.
- [Release notes](https://github.com/youtype/mypy_boto3_builder/releases)
- [Commits](https://github.com/youtype/mypy_boto3_builder/commits)

---
updated-dependencies:
- dependency-name: types-aiobotocore
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-14 08:45:24 +00:00
dependabot[bot]
229fbf3bef Bump mypy from 1.4.0 to 1.5.0 (#265)
Bumps [mypy](https://github.com/python/mypy) from 1.4.0 to 1.5.0.
- [Commits](https://github.com/python/mypy/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-11 08:18:32 +00:00
dependabot[bot]
63c9fd5f8c Bump fastapi from 0.100.1 to 0.101.0 (#254)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.100.1 to 0.101.0.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.100.1...0.101.0)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-10 08:30:48 +00:00
dependabot[bot]
ea2dc76da1 Bump ruff from 0.0.282 to 0.0.284 (#261)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.282 to 0.0.284.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.282...v0.0.284)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-10 08:26:36 +00:00
dependabot[bot]
e87716cf99 Bump types-aiobotocore from 2.5.2 to 2.5.4 (#259)
Bumps [types-aiobotocore](https://github.com/youtype/mypy_boto3_builder) from 2.5.2 to 2.5.4.
- [Release notes](https://github.com/youtype/mypy_boto3_builder/releases)
- [Commits](https://github.com/youtype/mypy_boto3_builder/commits)

---
updated-dependencies:
- dependency-name: types-aiobotocore
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-09 08:27:54 +00:00
dependabot[bot]
c222ba078a Bump aiobotocore from 2.5.3 to 2.5.4 (#257)
Bumps [aiobotocore](https://github.com/aio-libs/aiobotocore) from 2.5.3 to 2.5.4.
- [Release notes](https://github.com/aio-libs/aiobotocore/releases)
- [Changelog](https://github.com/aio-libs/aiobotocore/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiobotocore/compare/2.5.3...2.5.4)

---
updated-dependencies:
- dependency-name: aiobotocore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 08:51:14 +00:00
dependabot[bot]
36aef927f9 Bump aiobotocore from 2.5.2 to 2.5.3 (#253)
Bumps [aiobotocore](https://github.com/aio-libs/aiobotocore) from 2.5.2 to 2.5.3.
- [Release notes](https://github.com/aio-libs/aiobotocore/releases)
- [Changelog](https://github.com/aio-libs/aiobotocore/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiobotocore/compare/2.5.2...2.5.3)

---
updated-dependencies:
- dependency-name: aiobotocore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-07 08:45:56 +00:00
dependabot[bot]
58eece7830 Bump pyright from 1.1.318 to 1.1.320 (#252)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.318 to 1.1.320.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.318...v1.1.320)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-03 09:12:02 +00:00
dependabot[bot]
9bb5e9475b Bump aiobotocore from 2.5.0 to 2.5.2 (#235)
Bumps [aiobotocore](https://github.com/aio-libs/aiobotocore) from 2.5.0 to 2.5.2.
- [Release notes](https://github.com/aio-libs/aiobotocore/releases)
- [Changelog](https://github.com/aio-libs/aiobotocore/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiobotocore/compare/2.5.0...2.5.2)

---
updated-dependencies:
- dependency-name: aiobotocore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-02 08:23:17 +00:00
dependabot[bot]
bd179f530f Bump ruff from 0.0.281 to 0.0.282 (#251)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.281 to 0.0.282.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.281...v0.0.282)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-02 08:19:24 +00:00
dependabot[bot]
a0d0e7b130 Bump typing-extensions from 4.7.0 to 4.7.1 (#228)
Bumps [typing-extensions](https://github.com/python/typing_extensions) from 4.7.0 to 4.7.1.
- [Release notes](https://github.com/python/typing_extensions/releases)
- [Changelog](https://github.com/python/typing_extensions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/python/typing_extensions/compare/4.7.0...4.7.1)

---
updated-dependencies:
- dependency-name: typing-extensions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-01 08:56:59 +00:00
dependabot[bot]
f614bc2de5 Bump ruff from 0.0.276 to 0.0.281 (#250)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.276 to 0.0.281.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.276...v0.0.281)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-01 08:52:18 +00:00
dependabot[bot]
30c0acc4d5 Bump fastapi from 0.99.1 to 0.100.1 (#247)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.99.1 to 0.100.1.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.99.1...0.100.1)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-28 08:24:51 +00:00
dependabot[bot]
9c1378c96f Bump pyright from 1.1.317 to 1.1.318 (#244)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.317 to 1.1.318.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.317...v1.1.318)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-24 08:56:57 +00:00
dependabot[bot]
300c9f0ae4 Bump types-redis from 4.6.0.2 to 4.6.0.3 (#243)
Bumps [types-redis](https://github.com/python/typeshed) from 4.6.0.2 to 4.6.0.3.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-redis
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-21 08:53:34 +00:00
dependabot[bot]
5cace7ee4b Bump pyright from 1.1.316 to 1.1.317 (#239)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.316 to 1.1.317.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.316...v1.1.317)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-13 09:00:21 +00:00
dependabot[bot]
fcc7f75d8f Bump types-aiobotocore from 2.5.1 to 2.5.2 (#237)
Bumps [types-aiobotocore](https://github.com/youtype/mypy_boto3_builder) from 2.5.1 to 2.5.2.
- [Release notes](https://github.com/youtype/mypy_boto3_builder/releases)
- [Commits](https://github.com/youtype/mypy_boto3_builder/commits)

---
updated-dependencies:
- dependency-name: types-aiobotocore
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-10 08:21:53 +00:00
dependabot[bot]
3639f76d47 Bump tox from 4.6.3 to 4.6.4 (#234)
Bumps [tox](https://github.com/tox-dev/tox) from 4.6.3 to 4.6.4.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.6.3...4.6.4)

---
updated-dependencies:
- dependency-name: tox
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-07 08:24:04 +00:00
dependabot[bot]
7a25b77295 Bump types-redis from 4.5.5.2 to 4.6.0.2 (#233)
Bumps [types-redis](https://github.com/python/typeshed) from 4.5.5.2 to 4.6.0.2.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-redis
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-05 08:23:07 +00:00
dependabot[bot]
9ec731be61 Bump tox from 4.6.1 to 4.6.3 (#214)
Bumps [tox](https://github.com/tox-dev/tox) from 4.6.1 to 4.6.3.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.6.1...4.6.3)

---
updated-dependencies:
- dependency-name: tox
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-04 08:36:23 +00:00
dependabot[bot]
e881a2a4d7 Bump ruff from 0.0.274 to 0.0.276 (#230)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.274 to 0.0.276.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.274...v0.0.276)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-04 08:31:06 +00:00
dependabot[bot]
6ea7d01c6f Bump fastapi from 0.98.0 to 0.99.1 (#227)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.98.0 to 0.99.1.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.98.0...0.99.1)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-03 09:10:23 +00:00
dependabot[bot]
c5aa99a17c Bump typing-extensions from 4.6.3 to 4.7.0 (#226)
Bumps [typing-extensions](https://github.com/python/typing_extensions) from 4.6.3 to 4.7.0.
- [Release notes](https://github.com/python/typing_extensions/releases)
- [Changelog](https://github.com/python/typing_extensions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/python/typing_extensions/compare/4.6.3...4.7.0)

---
updated-dependencies:
- dependency-name: typing-extensions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-29 08:13:39 +00:00
dependabot[bot]
b979a1f73c Bump types-aiobotocore from 2.5.0.post2 to 2.5.1 (#223)
Bumps [types-aiobotocore](https://github.com/youtype/mypy_boto3_builder) from 2.5.0.post2 to 2.5.1.
- [Release notes](https://github.com/youtype/mypy_boto3_builder/releases)
- [Commits](https://github.com/youtype/mypy_boto3_builder/commits)

---
updated-dependencies:
- dependency-name: types-aiobotocore
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-28 09:23:40 +00:00
dependabot[bot]
706048f0c3 Bump pyright from 1.1.315 to 1.1.316 (#222)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.315 to 1.1.316.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.315...v1.1.316)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 09:12:04 +00:00
dependabot[bot]
e775799d41 Bump redis from 4.5.5 to 4.6.0 (#219)
Bumps [redis](https://github.com/redis/redis-py) from 4.5.5 to 4.6.0.
- [Release notes](https://github.com/redis/redis-py/releases)
- [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES)
- [Commits](https://github.com/redis/redis-py/compare/v4.5.5...v4.6.0)

---
updated-dependencies:
- dependency-name: redis
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-26 09:11:36 +00:00
dependabot[bot]
d543c6f3c4 Bump fastapi from 0.96.0 to 0.98.0 (#216)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.96.0 to 0.98.0.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.96.0...0.98.0)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-23 09:13:13 +00:00
dependabot[bot]
3c0e25d854 Bump pyright from 1.1.314 to 1.1.315 (#215)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.314 to 1.1.315.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.314...v1.1.315)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-22 09:12:00 +00:00
dependabot[bot]
fd2baba920 Bump mypy from 1.3.0 to 1.4.0 (#213)
Bumps [mypy](https://github.com/python/mypy) from 1.3.0 to 1.4.0.
- [Commits](https://github.com/python/mypy/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-21 09:13:55 +00:00
dependabot[bot]
0df52ec740 Bump ruff from 0.0.272 to 0.0.274 (#212)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.272 to 0.0.274.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.272...v0.0.274)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-21 09:10:20 +00:00
dependabot[bot]
50c1f84f24 Bump importlib-metadata from 6.6.0 to 6.7.0 (#209)
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.6.0 to 6.7.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v6.6.0...v6.7.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-19 09:16:02 +00:00
dependabot[bot]
8bfe814c36 Bump tox from 4.6.0 to 4.6.1 (#207)
Bumps [tox](https://github.com/tox-dev/tox) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.6.0...4.6.1)

---
updated-dependencies:
- dependency-name: tox
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-16 09:14:31 +00:00
dependabot[bot]
c90291fccf Bump pyright from 1.1.311 to 1.1.314 (#206)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.311 to 1.1.314.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.311...v1.1.314)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-14 09:21:13 +00:00
dependabot[bot]
c4b0f47642 Bump pytest from 7.3.1 to 7.3.2 (#204)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.3.1 to 7.3.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.3.1...7.3.2)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-12 09:15:44 +00:00
dependabot[bot]
37baa6cd0d Bump ruff from 0.0.271 to 0.0.272 (#200)
Bumps [ruff](https://github.com/charliermarsh/ruff) from 0.0.271 to 0.0.272.
- [Release notes](https://github.com/charliermarsh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/charliermarsh/ruff/compare/v0.0.271...v0.0.272)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 09:12:43 +00:00
dependabot[bot]
e8cfa24c6e Bump ruff from 0.0.270 to 0.0.271 (#199)
Bumps [ruff](https://github.com/charliermarsh/ruff) from 0.0.270 to 0.0.271.
- [Release notes](https://github.com/charliermarsh/ruff/releases)
- [Changelog](https://github.com/charliermarsh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/charliermarsh/ruff/compare/v0.0.270...v0.0.271)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 09:14:17 +00:00
dependabot[bot]
8b99ddfeb1 Bump tox from 4.5.2 to 4.6.0 (#198)
Bumps [tox](https://github.com/tox-dev/tox) from 4.5.2 to 4.6.0.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.5.2...4.6.0)

---
updated-dependencies:
- dependency-name: tox
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-06 09:11:37 +00:00
dependabot[bot]
dfb747230c Bump fastapi from 0.95.2 to 0.96.0 (#197)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.95.2 to 0.96.0.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.95.2...0.96.0)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 09:14:32 +00:00
dependabot[bot]
eb84b1457e Bump typing-extensions from 4.6.2 to 4.6.3 (#196)
Bumps [typing-extensions](https://github.com/python/typing_extensions) from 4.6.2 to 4.6.3.
- [Changelog](https://github.com/python/typing_extensions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/python/typing_extensions/compare/4.6.2...4.6.3)

---
updated-dependencies:
- dependency-name: typing-extensions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-02 09:12:17 +00:00
dependabot[bot]
069a112e3b Bump pyright from 1.1.308 to 1.1.311 (#195)
Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.308 to 1.1.311.
- [Release notes](https://github.com/RobertCraigie/pyright-python/releases)
- [Commits](https://github.com/RobertCraigie/pyright-python/compare/v1.1.308...v1.1.311)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-31 09:12:07 +00:00
dependabot[bot]
8c67de4b44 Bump coverage from 7.2.6 to 7.2.7 (#193)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.6 to 7.2.7.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.2.6...7.2.7)

---
updated-dependencies:
- dependency-name: coverage
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-30 09:22:23 +00:00
dependabot[bot]
f218c9deb0 Bump tox from 4.5.1 to 4.5.2 (#190)
Bumps [tox](https://github.com/tox-dev/tox) from 4.5.1 to 4.5.2.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.5.1...4.5.2)

---
updated-dependencies:
- dependency-name: tox
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-29 09:17:01 +00:00
dependabot[bot]
bcbe201053 Bump typing-extensions from 4.6.1 to 4.6.2 (#187)
Bumps [typing-extensions](https://github.com/python/typing_extensions) from 4.6.1 to 4.6.2.
- [Changelog](https://github.com/python/typing_extensions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/python/typing_extensions/compare/4.6.1...4.6.2)

---
updated-dependencies:
- dependency-name: typing-extensions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-26 09:21:34 +00:00
dependabot[bot]
d395a16595 Bump coverage from 7.2.5 to 7.2.6 (#181)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.5 to 7.2.6.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.2.5...7.2.6)

---
updated-dependencies:
- dependency-name: coverage
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-25 09:17:20 +00:00
dependabot[bot]
09e0da20b0 Bump ruff from 0.0.267 to 0.0.270 (#184)
Bumps [ruff](https://github.com/charliermarsh/ruff) from 0.0.267 to 0.0.270.
- [Release notes](https://github.com/charliermarsh/ruff/releases)
- [Changelog](https://github.com/charliermarsh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/charliermarsh/ruff/compare/v0.0.267...v0.0.270)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-25 09:13:17 +00:00
dependabot[bot]
8945ac7a3a Bump typing-extensions from 4.5.0 to 4.6.1 (#180)
Bumps [typing-extensions](https://github.com/python/typing_extensions) from 4.5.0 to 4.6.1.
- [Changelog](https://github.com/python/typing_extensions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/python/typing_extensions/compare/4.5.0...4.6.1)

---
updated-dependencies:
- dependency-name: typing-extensions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-24 09:14:07 +00:00
dependabot[bot]
c1d2dd65fa Bump requests from 2.30.0 to 2.31.0 (#178)
Bumps [requests](https://github.com/psf/requests) from 2.30.0 to 2.31.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.30.0...v2.31.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 09:11:18 +00:00
dependabot[bot]
0b66fd7535 Bump httpx from 0.24.0 to 0.24.1 (#174)
Bumps [httpx](https://github.com/encode/httpx) from 0.24.0 to 0.24.1.
- [Release notes](https://github.com/encode/httpx/releases)
- [Changelog](https://github.com/encode/httpx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/httpx/compare/0.24.0...0.24.1)

---
updated-dependencies:
- dependency-name: httpx
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-19 09:13:15 +00:00
Martijn Pieters
fbdaa62e24 💄 Pull version from installation metadata (#172)
The version string in the `__init__` module is needed to support
towncrier, but that does mean there are now two locations for the
project version: pyproject.toml and the `__init__.py` file. Use
`importlib.metadata` to pull the version from the installation metadata.
2023-05-17 17:09:52 +00:00
Martijn Pieters
70d8fef402 Merge pull request #171 from long2ice/towncrier
📣 Start managing the changelog with towncrier
2023-05-17 17:37:51 +01:00
Martijn Pieters
42cb99d4eb 📣 Start managing the changelog with towncrier
This PR includes a workflow that validates that future PRs include
a changelog entry (unless the `skip-changelog` label is present on the
PR, or the PR is a dependabot PR).

Use 'poetry run towncrier create` to create entries for the changelog.
2023-05-17 17:31:03 +01:00
Martijn Pieters
b287f21043 📖 Copy-edit README (#168)
- Update workflow shields to point to new CI/CD pipeline, and link
  all shields to somewhere appropriate.
- Use product names instead of code-markup names.
- Edit for English grammar and style.
- Expand decorator argument table to add defaults
- Add more meaningful `Coder` and key builder examples and expand
  on what the default key builder does.
2023-05-17 11:34:40 +00:00
Martijn Pieters
826e785522 Merge pull request #166 from long2ice/release_wf
📦 Move PyPI release workflow into the main workflow.
2023-05-17 11:38:11 +01:00
Martijn Pieters
282fcc7cea 📦 Move PyPI release workflow into the main workflow.
This ensures that any releases are fully tested before publication.

The workflow first builds the distribution files (sdist, wheel) before
using a deployment environment to publish these to PyPI, using the
GitHub actions OpenID support to authenticate with PyPI.
2023-05-17 11:33:25 +01:00
dependabot[bot]
645cd94ec1 Bump fastapi from 0.95.1 to 0.95.2 (#167)
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.95.1 to 0.95.2.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.95.1...0.95.2)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-17 09:11:54 +00:00
Martijn Pieters
4aa8060faa Merge pull request #163 from long2ice/3777dependabot/pip/main/coverage-7.2.5 2023-05-16 15:07:50 +01:00
dependabot[bot]
189f997228 Bump coverage from 6.5.0 to 7.2.5
Bumps [coverage](https://github.com/nedbat/coveragepy) from 6.5.0 to 7.2.5.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/6.5.0...7.2.5)

---
updated-dependencies:
- dependency-name: coverage
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-16 13:46:35 +00:00
Martijn Pieters
3e7deea2ba Correct merge metadata step id, loosen version (#165) 2023-05-16 13:31:55 +00:00
Martijn Pieters
452eaedf5b 🤦 put dependabot wf in correct place (#164) 2023-05-16 13:24:45 +00:00
Martijn Pieters
24e1d8d40b Merge pull request #162 from long2ice/5db3dependabot/pip/main/aiobotocore-2.5.0
Bump aiobotocore from 1.4.2 to 2.5.0
2023-05-16 14:21:41 +01:00
dependabot[bot]
621103c2b8 Bump aiobotocore from 1.4.2 to 2.5.0
Bumps [aiobotocore](https://github.com/aio-libs/aiobotocore) from 1.4.2 to 2.5.0.
- [Release notes](https://github.com/aio-libs/aiobotocore/releases)
- [Changelog](https://github.com/aio-libs/aiobotocore/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiobotocore/compare/1.4.2...2.5.0)

---
updated-dependencies:
- dependency-name: aiobotocore
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-16 13:17:50 +00:00
Martijn Pieters
bb12a91b58 Create codeql scanning workflow (#161)
This helps find any security issues in PRs.
2023-05-16 13:14:58 +00:00
Martijn Pieters
4236b350d9 Configure dependabot and associated workflows (#160)
The auto-merge workflow automatically approves dependabot PRs that
update dependencies or github actions where the semver difference is
at most a minor upgrade. These PRs are then auto-merged once the CI
checks have passed.

The labeller workflow adds / removes the auto-merge label to make
auto-merging more visible in issue lists and to make it possible to
filter such PRs. This workflow is unfortunately not triggered
when the auto-approve workflow enables auto-merging due to GH anti-
recursion rules, so the auto-merge workflow adds the label explicitly.
2023-05-16 13:06:11 +00:00
Martijn Pieters
23bb4e9cd4 Add pytest-style linting (#159)
This does mean we need to tell pyright that the init_cache auto-use
fixture is still being used even though it now has a leading underscore.
2023-05-16 12:45:26 +00:00
Martijn Pieters
230c24a4e0 Give tests and examples separate ruff configs (#158)
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.
2023-05-16 12:42:12 +00:00
Martijn Pieters
d938bbe4d2 Add flake8-comprehensions linter (#157) 2023-05-16 12:18:53 +00:00
Martijn Pieters
50e3f91a87 Add flake8-bandit linting (#156)
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.
2023-05-16 12:11:10 +00:00
Martijn Pieters
cbad93c970 Merge pull request #155 from long2ice/pyupgrade
Add pyupgrade linter
2023-05-16 13:07:56 +01:00
Martijn Pieters
f7db5cceb1 Add pyupgrade linter
This ensures we use the best syntax for the minimal Python version used.
The linter found one issue, fixed with the auto-fixer.
2023-05-16 13:00:18 +01:00
Martijn Pieters
daad4cf9a8 Merge pull request #154 from long2ice/simplify_pyproject.toml
Clean up pyproject.toml
2023-05-16 12:59:16 +01:00
Martijn Pieters
d2947d01b5 Clean up pyproject.toml
- remove usused dependency
- use poetry syntax to specify minimal python versions
- simplify path selections in mypy and pyright
- correct pytest section name
2023-05-16 12:47:47 +01:00
Martijn Pieters
637c825dc8 Merge pull request #153 from long2ice/simplify_wf
CI: tox now takes care of extras and groups
2023-05-16 12:35:22 +01:00
Martijn Pieters
1cf352bc9c CI: tox now takes care of extras and groups
The outer Poetry installation can be simplified as long as tox is part
of the dev group.
2023-05-16 12:29:18 +01:00
Martijn Pieters
f314d84d31 Merge pull request #152 from long2ice/ruff
Switch to ruff to handle linting and formatting
2023-05-16 12:24:02 +01:00
Martijn Pieters
1d9e126037 Switch to ruff to handle linting and formatting
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.
2023-05-16 12:18:24 +01:00
Martijn Pieters
707b4aec95 Merge pull request #151 from long2ice/poetry_core
Use poetry-core as the build system
2023-05-16 11:08:03 +01:00
Martijn Pieters
293a06467a Use poetry-core as the build system
Poetry core is the lighter-weight build system for poetry; this makes
installing the project faster.
2023-05-16 11:03:53 +01:00
Martijn Pieters
7767d2460d Merge pull request #146 from long2ice/tox
Add tox configuration
2023-05-16 10:52:25 +01:00
Martijn Pieters
0e9a8baeb2 Add tox configuration
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.
2023-05-16 10:49:17 +01:00
Martijn Pieters
2eabc49d24 Merge pull request #150 from long2ice/cache_mypy_cache
CI: cache the mypy cache for faster runs
2023-05-15 17:49:38 +01:00
Martijn Pieters
b5aabaaf59 CI: cache the mypy cache for faster runs 2023-05-15 17:43:49 +01:00
Martijn Pieters
2da4701c1d Merge pull request #148 from long2ice/separate_linting
CI: use a separate step to run linters
2023-05-15 17:33:34 +01:00
Martijn Pieters
31d0b007cd CI: use a separate step to run linters
This makes it easier to separate linter dependencies from older Python
releases.
2023-05-15 17:30:06 +01:00
Martijn Pieters
e57cd59c98 Merge pull request #149 from long2ice/pr_is_not_push
CI: don't run this workflow twice on a PR
2023-05-15 17:25:29 +01:00
Martijn Pieters
4a012c7cae CI: don't run this workflow twice on a PR
If a maintainer pushes to a PR, the workflow push and pull_request events
both trigger. Limit the workflow to the main branch, explicitly.
2023-05-15 17:23:26 +01:00
Martijn Pieters
915f3dd8f2 Add a cache status header to the response
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.
2023-05-14 17:03:57 +01:00
Martijn Pieters
29426de95f Refactor decorator, consolidate miss / hit paths
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.
2023-05-14 17:03:57 +01:00
Martijn Pieters
d10f4af6d6 Import supported backends
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.
2023-05-14 17:02:30 +01:00
long2ice
9638d70dfe Merge pull request #133 from mjpieters/correct_json_decode_return
Clean up type annotations
2023-05-12 14:53:40 +08:00
Martijn Pieters
0763cd7b95 Add pyright strict type checking 2023-05-11 12:57:28 +01:00
Martijn Pieters
ad1eae2b4b Set python version for mypy
This lets you catch compatibility issues regardless of the current python version used for development.
2023-05-11 12:57:27 +01:00
Martijn Pieters
eda2a437a4 More type hint compatibility updates 2023-05-11 12:57:27 +01:00
Martijn Pieters
a892a21b92 Run mypy during CI checks 2023-05-11 12:34:08 +01:00
Martijn Pieters
941cd044c7 Full mypy --strict type checking pass 2023-05-11 12:34:08 +01:00
Martijn Pieters
e92604802e Remove non-existing argument to unicorn.run 2023-05-11 12:31:07 +01:00
Martijn Pieters
94a02733c8 Run tests on all supported Python versions. 2023-05-11 12:31:07 +01:00
Martijn Pieters
013be85f97 Typing cleanup
- 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()`.
2023-05-11 12:31:05 +01:00
long2ice
564026e189 Merge pull request #135 from mjpieters/refactor_prefix
Make decorator responsibe for applying the prefix
2023-05-11 09:25:57 +08:00
long2ice
fba7726280 Merge pull request #134 from mjpieters/backend_coder_bytes
Make backends store bytes instead of strings
2023-05-11 09:24:38 +08:00
Martijn Pieters
d9965a45e5 Make decorator responsibe for applying the prefix
The key builder should not have to fetch the prefix separately, as this
makes creating custom key builders that much harder.
2023-05-10 18:59:59 +01:00
Martijn Pieters
23d439f83a Make backends store bytes instead of strings
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.
2023-05-10 17:35:15 +01:00
long2ice
5f2fcf3581 Merge pull request #132 from mjpieters/cache_pydantic_fields
Cache pydantic model fields for faster decoding
2023-05-10 21:21:43 +08:00
Martijn Pieters
7c30402907 Cache pydantic model fields for faster decoding
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.
2023-05-09 17:25:32 +01:00
long2ice
4d67e0c464 Merge pull request #124 from mjpieters/type_refinement
key_builder type; args and kwargs are always given
2023-05-09 18:16:37 +08:00
long2ice
276c7c725f Merge pull request #125 from mjpieters/update_lock
Refresh poetry lock
2023-05-09 18:16:13 +08:00
long2ice
da9a03ede8 Merge pull request #127 from mjpieters/exported_names
Explicitly list what names are exported
2023-05-09 18:15:57 +08:00
long2ice
eece971f0a Merge pull request #128 from mjpieters/namespaced_injection
Inject dependencies using a namespace
2023-05-09 18:15:32 +08:00
Martijn Pieters
e09ede2e4c Inject dependencies using a namespace
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.
2023-05-09 11:09:29 +01:00
long2ice
2788006b8c Merge pull request #130 from mjpieters/non_get_requests
Fix handling non-GET requests
2023-05-09 18:03:24 +08:00
Martijn Pieters
4cc946eb00 Fix handling non-GET requests
The `request` parameter being passed in was just a hold-over from an
earlier refactoring. Added tests to ensure that this edge case keeps
working.
2023-05-09 10:58:37 +01:00
long2ice
1ded0ed50e Merge pull request #131 from mjpieters/json_decoder_pydantic
Decode cache data to the correct endpoint type
2023-05-09 10:16:17 +08:00
Martijn Pieters
f78a599bbc Decode cache data to the correct endpoint type
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.
2023-05-08 16:55:05 +01:00
Martijn Pieters
b1dc05a89a key_builder type; args and kwargs are always given
These arguments are never set to None so don't need to be optional. They
are always a tuple and a dict but can be empty.
2023-04-28 16:50:30 +01:00
Martijn Pieters
416a4ec850 Explicitly list what names are exported
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"`.
2023-04-28 14:40:31 +01:00
Martijn Pieters
3a3964db1b Refresh poetry lock
The older releases in the lock can cause the CI build to fail with
more recent Poetry versions, see python-poetry/poetry#7611. Updating
the lock should remedy this.
2023-04-28 12:27:52 +01:00
long2ice
550ba76df4 Merge pull request #123 from mjpieters/method_signature
Attach updated endpoint signature to inner
2023-04-28 14:29:06 +08:00
long2ice
b26059b654 Merge pull request #122 from mjpieters/type-hinting
Complete type hints
2023-04-28 14:28:49 +08:00
Martijn Pieters
832650347b Attach updated endpoint signature to inner
Not all endpoints accept a __signature__ attribute, nor should the
cache decorator modify the decorated endpoint. Attach the signature
to the returned inner function instead.

While here, refactor the signature updating code, and extract it to
a separate function.
2023-04-27 18:14:59 +01:00
Martijn Pieters
72c42325ab The backend needs an async redis client with a pipeline method
The Abstract* classes lack the pipeline method so are not sufficient.
2023-04-27 16:33:43 +01:00
Martijn Pieters
6af14be049 Provide annotation for the session attribute 2023-04-27 16:32:07 +01:00
Martijn Pieters
9c966286b4 Use complete type hints with all generic parameters filled
This makes the core fastapi_cache pass all strict type checker tests.
2023-04-27 16:31:42 +01:00
Martijn Pieters
a52f6b1406 Simplify key_builder calling
The keybuilder is either returning a string, or a coroutine or other
awaitable. If the latter, await on the return value to get the string.
2023-04-27 16:29:10 +01:00
Martijn Pieters
255f40117b Define keybuilder protocol
This lets others create key builders that are type checked fully.
2023-04-27 16:26:41 +01:00
Martijn Pieters
d4cd787527 JSONResponse.body is UTF-8 bytes and must be decoded 2023-04-27 16:20:12 +01:00
Martijn Pieters
059793d585 Remove a type: ignore comment
GIve the type checker more information about the converters instead.
2023-04-27 16:19:02 +01:00
Martijn Pieters
0d0fe1f0d0 Mark up the class variables as such
There is never an instance of this class, so these are not instance attributes.
2023-04-27 16:15:06 +01:00
Martijn Pieters
32acafa5e0 Correct type hint: namespace is not optional
The namespace argument is positional and will never be `None` so should
not be marked as Optional. It is always a string, and the default is
to pass in an empty string.
2023-04-27 16:11:59 +01:00
long2ice
ee58f979d4 ci: fix workflows 2023-02-15 10:53:24 +08:00
long2ice
38ddd063c3 test: add httpx for test 2023-02-15 10:49:35 +08:00
long2ice
27acce3160 ci: fix poetry 2023-02-15 10:45:19 +08:00
long2ice
d04be274e9 feat: upgrade deps 2023-02-15 10:43:01 +08:00
long2ice
80563fd6e7 Merge pull request #118 from naoki-jarvisml/var_keyword
Support functions with VAR_KEYWORD parameter
2023-02-15 10:30:12 +08:00
Naoki Shima
98cf8a78a1 adding test coverage 2023-02-15 10:35:41 +09:00
Naoki Shima
01c895dbbb Support functions with VAR_KEYWORD parameter
decorating function with **kwargs parameter with @cache causes ValueError.

ValueError: wrong parameter order: variadic keyword parameter before keyword-only parameter

We need to inject request / response parameters before VAR_KEYWORD parameter.
2023-02-09 15:14:20 +09:00
long2ice
e3b08dda2c Merge pull request #114 from hackjammer/feature/redisCluster
Add RedisCluster Support
2023-02-01 15:33:11 +08:00
long2ice
552a7695e8 Update fastapi_cache/decorator.py
Co-authored-by: mkdir700 <56359329+mkdir700@users.noreply.github.com>
2023-02-01 15:33:04 +08:00
hackjammer
ea1ffcd7b4 Add logging to decorator.py on backend failures 2023-01-17 12:15:53 +00:00
hackjammer
e8193b5c22 enabled redis in cluster mode 2023-01-15 21:54:16 +00:00
hackjammer
ab26fad604 passthrough for any type of backend exception 2023-01-15 17:07:37 +00:00
long2ice
7a89f28b54 Merge pull request #112 from schmocker/main
add cache-control and etag to header of fist response
2023-01-15 12:27:56 +08:00
Tobias Schmocker
334b829a80 Merge branch 'master'
# Conflicts:
#	fastapi_cache/decorator.py
2023-01-14 19:11:42 +01:00
long2ice
62ef8bed37 Merge pull request #109 from Mrreadiness/fix/piclke-coder
Fix Piclke Coder
2023-01-11 21:31:02 +08:00
Ivan Moiseev
9a39db7a73 Merge branch 'long2ice:main' into fix/piclke-coder 2023-01-11 16:26:05 +03:00
long2ice
59a47b7fae chore: set version 0.2.0 2023-01-11 21:20:41 +08:00
long2ice
09361a7d4f Merge pull request #98 from vvanglro/feat/cache_response_obj
Feat/cache response obj
2023-01-11 17:23:47 +08:00
vvanglro
ed101595f7 fix: merge master 2023-01-11 16:43:36 +08:00
long2ice
0c73777930 Merge pull request #108 from hackjammer/master
Transparent passthrough in the event of cache backend connection issues
2023-01-11 10:45:11 +08:00
vvanglro
0d964fcf9f fix: remove unused 2023-01-07 13:55:41 +08:00
vvanglro
614ee25d0d feat: merge master 2023-01-07 13:46:48 +08:00
hackjammer
b420f26e9b transparent passthrough in the event of backend connection issues 2023-01-05 18:44:40 +00:00
Ivan Moiseev
e23289fcbf Merge branch 'main' into fix/piclke-coder 2022-12-08 00:23:39 +04:00
long2ice
8f0920d0d7 ci: fix 2022-11-07 16:39:17 +08:00
long2ice
91e6e51ec7 Merge pull request #101 from mkdir700/fix-ci-errors-on-python3.11
Fix ci errors on python3.11
2022-11-07 16:33:45 +08:00
mkdir700
5c776d20db build: update version of aiohttp 2022-11-05 22:21:35 +08:00
mkdir700
c4ae7154fd ci: update version of actions 2022-11-05 22:21:35 +08:00
Ivan Moiseev
cb9fe5c065 fix: PickleCoder and add tests for it. 2022-11-05 13:45:16 +04:00
vvanglro
c1484a46fd feat: CHANGELOG.md 2022-11-04 17:44:41 +08:00
vvanglro
2710129c4e feat: cache response obj add test case 2022-11-04 17:34:20 +08:00
vvanglro
4cb4afeff0 feat: support cache JSONResponse 2022-11-04 17:31:37 +08:00
vvanglro
a8fbf2b340 fix: request / router KeyError 2022-11-04 16:56:43 +08:00
long2ice
73f000a565 Merge pull request #93 from Mrreadiness/feat/type-hints-covering
Feat/type hints covering
2022-11-04 08:51:21 +08:00
long2ice
cda720f534 Merge pull request #74 from Genius-Voice/feature/support-async-keybuilder
Add ability to use async function for key_builder
2022-11-03 20:25:27 +08:00
Ivan Moiseev
5881bb9122 Merge branch 'main' into feat/type-hints-covering
# Conflicts:
#	fastapi_cache/coder.py
#	fastapi_cache/decorator.py
2022-11-03 15:53:22 +04:00
Ivan Moiseev
10f819483c fix: replace pipe for Optional 2022-11-03 15:49:58 +04:00
long2ice
566d30b790 Merge pull request #88 from vvanglro/feat/cache_html
feat: support cache jinja2 template response
2022-11-03 19:42:47 +08:00
long2ice
671af52aea Merge pull request #33 from DevLucca/master
add `no-cache` to cache exclusion
2022-10-31 21:51:35 +08:00
Ivan Moiseev
71a77f6b39 fix: request and response type hints 2022-10-30 11:03:16 +04:00
Ivan Moiseev
e555d5e9be Merge remote-tracking branch 'main/master' into feat/type-hints-covering
# Conflicts:
#	fastapi_cache/decorator.py
2022-10-30 10:58:02 +04:00
long2ice
d88b9eaab0 Merge pull request #94 from squaresmile/cache-type
Added typing info to the decorator
2022-10-25 10:27:27 +08:00
squaresmile
c1a0e97f73 Updated 0.2.0 changelog 2022-10-25 09:26:24 +07:00
squaresmile
f3f134a318 Added typing to the decorator 2022-10-25 08:58:44 +07:00
squaresmile
5781593829 Added py.typed 2022-10-25 08:50:56 +07:00
Ivan Moiseev
c6bd8483a4 feat: fix tests and add FastAPICache init in tests. 2022-10-22 21:12:04 +04:00
Ivan Moiseev
e842d6408e feat: make PickleCoder compatible with backends 2022-10-22 21:06:38 +04:00
Ivan Moiseev
68ef94f2db feat: add more asserts for FastAPICache init 2022-10-22 21:05:43 +04:00
Ivan Moiseev
4c6abcf786 feat: add more type hints 2022-10-22 20:59:37 +04:00
long2ice
1ef80ff457 Merge pull request #92 from cpbotha/fix-sync-for-cache-disabled-path-2
Add extra required await and more tests
2022-10-15 11:30:46 +08:00
Charl P. Botha
6ba06bb10f Fix sync example 2022-10-14 21:59:57 +02:00
Charl P. Botha
d0c0885eae Add coverage 2022-10-14 21:59:51 +02:00
Charl P. Botha
630b175766 Add tests for sync and disabled cache 2022-10-14 21:59:33 +02:00
Charl P. Botha
ceb70426f3 Factor out support for optional request / response 2022-10-14 21:58:34 +02:00
Charl P. Botha
d123ec4bfa Add extra required await 2022-10-14 14:09:31 +02:00
long2ice
eeea884bb4 Merge pull request #91 from cpbotha/fix-sync-for-cache-disabled-path
Fix sync for cache disabled path
2022-10-14 19:52:03 +08:00
Charl P. Botha
af9c4d4c56 Restore demo of sync handling 2022-10-14 13:44:59 +02:00
Charl P. Botha
2822ab5d71 Factor out sync handling and use everywhere 2022-10-14 13:44:49 +02:00
vvanglro
7e64cd6490 feat: support cache jinja2 template response 2022-09-28 17:37:05 +08:00
long2ice
34415ad50a Merge pull request #86 from vvanglro/fix/example
fix: example coroutine object is not iterable
2022-09-15 20:40:58 +08:00
vvanglro
3dd4887b37 fix: example coroutine object is not iterable 2022-09-15 17:30:59 +08:00
long2ice
3041af2216 Merge pull request #85 from vvanglro/docs/quick_start
docs: quick start redis
2022-09-14 17:09:15 +08:00
wanghao
5c6f819636 docs: quick start redis 2022-09-14 16:24:10 +08:00
long2ice
3a481a36ed fix: test 2022-09-11 12:33:10 +08:00
long2ice
cb9259807e feat: make request and response optional 2022-09-10 20:06:37 +08:00
long2ice
a4b3386bf0 Merge pull request #82 from uriyyo/feature/run_in_threadpool
Use `run_in_threadpool` instead of `asyncio.run_in_executor`
2022-09-10 18:43:40 +08:00
Yurii Karabas
f310ef5b2d Use run_in_threadpool instead of asyncio run_in_executor 2022-09-09 19:35:48 +03:00
Jegor Kitskerkin
6cc1e65abb Add support for async key_builder 2022-08-11 15:16:12 +02:00
long2ice
820689ce9a Merge pull request #72 from thentgesMindee/clear-no-namespace
fix: 🐛 make FastAPICache.clear clear whole prefix when namespace is None
2022-08-11 08:44:13 +08:00
thentgesMindee
309d9ce7d1 fix: 🐛 FastAPICache.clear clear whole prefix when namespace is None 2022-08-10 19:31:29 +02:00
Lucca Marques
e62f0117e0 feat: changelog changes 2022-08-09 13:45:53 -03:00
Lucca Marques
6dc449b830 Merge branch 'master' into master 2022-08-08 12:40:33 -03:00
long2ice
36e0812c19 Merge pull request #55 from RyanTruran/documentation
added InMemory example
2022-08-08 21:22:02 +08:00
rtruran
70f5eed50b Merge branch 'master' into documentation 2022-08-08 08:16:24 -05:00
Jinlong Peng
15576b482a Merge remote-tracking branch 'origin/master' 2022-08-07 21:11:55 +08:00
Jinlong Peng
f80bfdb18d upgrade deps 2022-08-07 21:11:47 +08:00
long2ice
aaed438d8f Merge pull request #69 from jegork/feature/support-cache-for-normal-functions
Add support for normal def functions
2022-08-01 15:05:16 +08:00
Jegor Kitskerkin
d6c52408d2 Add support for normal def functions 2022-08-01 00:06:39 +02:00
long2ice
8c92cc59ae Fix test 2022-06-26 19:18:34 +08:00
long2ice
824e2e145f Replace aioredis with redis-py 2022-06-17 11:01:47 +08:00
long2ice
7fa54d311f Merge remote-tracking branch 'origin/master' 2022-06-10 08:43:42 +08:00
long2ice
9582e04d43 update deps 2022-06-10 08:43:35 +08:00
long2ice
fd8cf2da11 Merge pull request #60 from cnkailyn/master
bugfix: '+' is more prior than 'or'
2022-04-24 11:44:25 +08:00
kailyn
2f1b1409b9 bugfix: '+' is more prior than 'or' 2022-04-24 11:19:20 +08:00
long2ice
269c1ca616 update deps 2022-03-30 14:19:53 +08:00
Ryan Truran
89826b0a3b added InMemory example 2022-02-24 10:07:33 -06:00
Tobias Schmocker
e5250c7f58 remove private from cache-control 2022-02-04 16:41:42 +01:00
Tobias Schmocker
1795c048d1 add cache-control to response after setting the cache 2022-02-04 16:37:18 +01:00
long2ice
81d2bf2cc6 update version and changelog 2021-11-12 09:37:02 +08:00
long2ice
70f53566aa Merge pull request #40 from jimtheyounger/feature/add-dynamodb-backend
Add dynamodb backend support
2021-10-29 10:45:28 +08:00
long2ice
9928f4cda0 Add enable param to init 2021-10-28 15:52:21 +08:00
long2ice
4faa5b7101 update workflows 2021-10-19 11:56:51 +08:00
long2ice
c3be2eca19 Fix default json coder for datetime. 2021-10-09 16:51:05 +08:00
Jimmy
11f01a21f5 Remove unused variable assignment 2021-10-07 17:34:06 +02:00
Jimmy
cdcfdc6ae6 Apply styling 2021-10-06 10:10:22 +02:00
Jimmy
a40c54e9e7 Update README & add usage 2021-09-29 16:22:04 +02:00
Jimmy
d67797a1d5 Add DynamoDB backend 2021-09-29 16:14:28 +02:00
long2ice
8a8eb395ec Merge pull request #38 from joeflack4/docs-minorFix
Fix: README.md: Corrected some syntactically incorrect commands
2021-09-24 10:23:25 +08:00
Joe Flack
e397dcb16b Updated readme.md
Corrected some syntactically incorrect commands
2021-09-23 16:10:31 -04:00
long2ice
37a2fa85db update CONVERTERS 2021-09-17 10:19:56 +08:00
long2ice
6888c10d6c update deps 2021-08-29 22:33:50 +08:00
long2ice
943935870d Update FUNDING.yml 2021-08-26 20:34:50 +08:00
Lucca Leme Marques
dacd7e1b0f add no-cache to cache exclusion 2021-08-23 11:51:28 -03:00
long2ice
46c7ada364 Fix ci 2021-07-26 16:37:19 +08:00
40 changed files with 4436 additions and 904 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,2 +1,2 @@
custom: ["https://sponsor.long2ice.cn"] custom: ["https://sponsor.long2ice.io"]

14
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
target-branch: main
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
target-branch: main

118
.github/workflows/ci-cd.yml vendored Normal file
View File

@@ -0,0 +1,118 @@
name: CI/CD
on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
jobs:
lint:
name: Linter
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Poetry
run: pipx install poetry
- name: Setup Python
id: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.x'
cache: poetry
- name: Cache mypy cache
uses: actions/cache@v3
with:
path: .mypy_cache
key: ${{ runner.os }}-mypy-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock') }}
restore-keys: |
${{ runner.os }}-mypy-${{ steps.setup-python.outputs.python-version }}-
${{ runner.os }}-mypy-
- name: Install linting requirements
run: poetry install --no-root
- name: Execute linters
run: make lint
test:
needs:
- lint
strategy:
matrix:
python: ["3.8", "3.9", "3.10", "3.11"]
fail-fast: false
name: "Test on Python ${{ matrix.python }}"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Poetry
run: pipx install poetry
- uses: actions/setup-python@v5
with:
python-version: "${{ matrix.python }}"
cache: poetry
- name: Install testing requirements
run: |
poetry install --no-root
poetry run pip install tox-gh-actions
- name: Execute tests
run: poetry run tox
test-summary:
name: Test matrix status
runs-on: ubuntu-latest
needs: [test]
if: always()
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
build:
name: Build distributions
runs-on: ubuntu-latest
needs: [test-summary]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Poetry
run: pipx install poetry
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
cache: poetry
- name: Build distributions
run:
make build
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: dist
path: dist
publish:
name: Build and publish Python 🐍 distributions 📦 to PyPI
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: [build]
runs-on: ubuntu-latest
environment:
name: pypi
# The URL is used in the 'successfully deployed' message as the link
# for the 'View deployment' button.
url: https://pypi.org/p/fastapi-cache2
permissions:
id-token: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: dist
path: dist
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

View File

@@ -1,16 +0,0 @@
name: ci
on: [ push, pull_request ]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.x"
- name: Install and configure Poetry
uses: snok/install-poetry@v1.1.1
with:
virtualenvs-create: false
- name: CI
run: make ci

34
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '29 15 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:python"

View File

@@ -0,0 +1,35 @@
name: Dependabot auto-merge
on: pull_request_target
permissions:
pull-requests: write
contents: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: github.event.pull_request.user.login == 'dependabot[bot]'
steps:
- name: Dependabot metadata
id: dependabot-metadata
uses: dependabot/fetch-metadata@v1
- uses: actions/checkout@v3
- name: Approve PR
# only auto-approve direct deps that are minor or patch updates
# dependency type is indirect, direct:development or direct:production
# version-update is semver-major, semver-minor or semver-patch
if: |
steps.dependabot-metadata.outputs.dependency-type != 'indirect'
&& steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major'
run: |
if [ "$(gh pr view "$PR_URL" --json reviewDecision -q .reviewDecision)" != "APPROVED" ]; then
gh pr review --approve "$PR_URL"
else
echo "PR already approved, skipping additional approvals to minimize emails/notification noise."
fi
gh pr merge --auto --squash "$PR_URL"
gh pr edit "$PR_URL" --add-label "auto-merge"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

30
.github/workflows/labeller.yaml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Labeller
on:
pull_request_target:
types:
- auto_merge_disabled
- auto_merge_enabled
permissions: {}
jobs:
add_remove_labels:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Add auto-merge label
if: github.event.action == 'auto_merge_enabled'
run: gh pr edit "$PR_URL" --add-label "auto-merge"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Remove auto-merge label
if: github.event.action == 'auto_merge_disabled'
run: gh pr edit "$PR_URL" --remove-label "auto-merge"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

View File

@@ -1,24 +0,0 @@
name: pypi
on:
release:
types:
- created
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Install and configure Poetry
uses: snok/install-poetry@v1.1.1
with:
virtualenvs-create: false
- name: Build dists
run: make build
- name: Pypi Publish
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_password }}

54
.github/workflows/towncrier.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Changelog
on:
pull_request:
types:
- opened
- reopened
- synchronize
- labeled
- unlabeled
branches:
- main
jobs:
towncrier:
name: Towncrier
runs-on: ubuntu-latest
# skip if this is a bot or the PR has the skip-changelog label
# note that the towncrier check command can recognize a release PR, by looking
# for changes to the CHANGELOG.md file.
if: |
!(
github.event.pull_request.user.login == 'dependabot[bot]'
|| contains(github.event.pull_request.labels.*.name, 'skip-changelog')
)
steps:
- uses: actions/checkout@v2
- name: Install Poetry
run: pipx install poetry
- name: Setup Python
id: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.x'
cache: poetry
- name: Install development tools
run: poetry install --no-root --only=dev
- name: Check for a towncrier fragment
run: |
# fetch enough commits from this merge commit to the base sha to ensure
# towncrier can inspect what changed
while [[ -z $(git merge-base $PR_BASE_SHA HEAD 2> /dev/null) ]]; do
git fetch --quiet --deepen=50 --no-tags --no-recurse-submodules origin $PR_BASE_SHA HEAD
done
if poetry run towncrier check --compare-with $PR_BASE_SHA; then
gh pr edit "$PR_URL" --add-label "changelog-provided"
else
gh pr edit "$PR_URL" --remove-label "changelog-provided"
exit 1
fi
env:
PR_BASE_SHA: ${{github.event.pull_request.base.sha}}
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

3
.gitignore vendored
View File

@@ -28,6 +28,9 @@ share/python-wheels/
*.egg *.egg
MANIFEST MANIFEST
# Ruff
.ruff_cache/
# PyInstaller # PyInstaller
# Usually these files are written by a python script from a template # Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it. # before PyInstaller builds the exe, so as to inject date/other infos into it.

View File

@@ -1,7 +1,51 @@
# ChangeLog # ChangeLog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the changes for the upcoming release can be found in <https://github.com/long2ice/fastapi-cache/tree/main/changelog.d/>.
<!-- towncrier release notes start -->
## 0.2
### 0.2.1
- Fix picklecoder
- Fix connection failure transparency and add logging
- Add Cache-Control and ETag on first response
- Support Async RedisCluster client from redis-py
### 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
- Add TestCase
- Fix cache decorate sync function
- Transparently handle backend connection failures.
## 0.1 ## 0.1
### 0.1.10
- Add `Cache-Control:no-cache` support.
### 0.1.9
- Replace `aioredis` with `redis-py`.
### 0.1.8
- Support `dynamodb` backend.
### 0.1.7
- Fix default json coder for datetime.
- Add `enable` param to `init`.
### 0.1.6 ### 0.1.6
- Fix redis cache. - Fix redis cache.

View File

@@ -1,41 +1,28 @@
checkfiles = fastapi_cache/ examples/ tests/
black_opts = -l 100 -t py38
py_warn = PYTHONDEVMODE=1
help:
@echo "FastAPI-Cache development makefile"
@echo
@echo "usage: make <target>"
@echo "Targets:"
@echo " up Ensure dev/test dependencies are updated"
@echo " deps Ensure dev/test dependencies are installed"
@echo " check Checks that build is sane"
@echo " test Runs all tests"
@echo " style Auto-formats the code"
@echo " build Build package"
up: up:
@poetry update @poetry update
deps: deps:
@poetry install --no-root -E all @poetry install --no-root --with=linting --all-extras
style: deps format: deps
@isort -src $(checkfiles) @poetry run tox run -e format
@black $(black_opts) $(checkfiles)
check: deps lint: deps
@black --check $(black_opts) $(checkfiles) || (echo "Please run 'make style' to auto-fix style issues" && false) @poetry run tox run -e lint
@flake8 $(checkfiles)
@bandit -r $(checkfiles)
test: deps test: deps
$(py_warn) pytest @poetry run tox
test-parallel: deps
@poetry run tox run-parallel
build: clean deps build: clean deps
@poetry build @poetry build
@poetry run tox run -e lint_distributions
clean: clean:
@rm -rf ./dist @rm -rf ./dist
ci: check test # aliases
check: lint
style: format

189
README.md
View File

@@ -1,25 +1,26 @@
# fastapi-cache # fastapi-cache
![pypi](https://img.shields.io/pypi/v/fastapi-cache2.svg?style=flat) [![pypi](https://img.shields.io/pypi/v/fastapi-cache2.svg?style=flat)](https://pypi.org/p/fastapi-cache2)
![license](https://img.shields.io/github/license/long2ice/fastapi-cache) [![license](https://img.shields.io/github/license/long2ice/fastapi-cache)](https://github.com/long2ice/fastapi-cache/blob/main/LICENSE)
![workflows](https://github.com/long2ice/fastapi-cache/workflows/pypi/badge.svg) [![CI/CD](https://github.com/long2ice/fastapi-cache/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/long2ice/fastapi-cache/actions/workflows/ci-cd.yml)
![workflows](https://github.com/long2ice/fastapi-cache/workflows/ci/badge.svg)
## Introduction ## Introduction
`fastapi-cache` is a tool to cache fastapi response and function result, with backends support `redis` and `memcache`. `fastapi-cache` is a tool to cache FastAPI endpoint and function results, with
backends supporting Redis, Memcached, and Amazon DynamoDB.
## Features ## Features
- Support `redis` and `memcache` and `in-memory` backends. - Supports `redis`, `memcache`, `dynamodb`, and `in-memory` backends.
- Easily integration with `fastapi`. - Easy integration with [FastAPI](https://fastapi.tiangolo.com/).
- Support http cache like `ETag` and `Cache-Control`. - Support for HTTP cache headers like `ETag` and `Cache-Control`, as well as conditional `If-Match-None` requests.
## Requirements ## Requirements
- `asyncio` environment. - FastAPI
- `redis` if use `RedisBackend`. - `redis` when using `RedisBackend`.
- `memcache` if use `MemcacheBackend`. - `memcache` when using `MemcacheBackend`.
- `aiobotocore` when using `DynamoBackend`.
## Install ## Install
@@ -30,13 +31,19 @@
or or
```shell ```shell
> pip install fastapi-cache2[redis] > pip install "fastapi-cache2[redis]"
``` ```
or or
```shell ```shell
> pip install fastapi-cache2[memcache] > pip install "fastapi-cache2[memcache]"
```
or
```shell
> pip install "fastapi-cache2[dynamodb]"
``` ```
## Usage ## Usage
@@ -44,7 +51,9 @@ or
### Quick Start ### Quick Start
```python ```python
import aioredis from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
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 Response
@@ -53,7 +62,17 @@ from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache from fastapi_cache.decorator import cache
app = FastAPI() from redis import asyncio as aioredis
@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
redis = aioredis.from_url("redis://localhost")
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
yield
app = FastAPI(lifespan=lifespan)
@cache() @cache()
@@ -63,74 +82,152 @@ async def get_cache():
@app.get("/") @app.get("/")
@cache(expire=60) @cache(expire=60)
async def index(request: Request, response: Response): async def index():
return dict(hello="world") return dict(hello="world")
@app.on_event("startup")
async def startup():
redis = aioredis.from_url("redis://localhost", encoding="utf8", decode_responses=True)
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
``` ```
### Initialization ### Initialization
Firstly you must call `FastAPICache.init` on startup event of `fastapi`, there are some global config you can pass in. First you must call `FastAPICache.init` during startup FastAPI startup; this is where you set global configuration.
### Use `cache` decorator ### Use the `@cache` decorator
If you want cache `fastapi` response transparently, you can use `cache` as decorator between router decorator and view function and must pass `request` as param of view function. If you want cache a FastAPI response transparently, you can use the `@cache`
decorator between the router decorator and the view function.
Parameter | type, description Parameter | type | default | description
------------ | ------------- ------------ | ----| --------- | --------
expire | int, states a caching time in seconds `expire` | `int` | | sets the caching time in seconds
namespace | str, namespace to use to store certain cache items `namespace` | `str` | `""` | namespace to use to store certain cache items
coder | which coder to use, e.g. JsonCoder `coder` | `Coder` | `JsonCoder` | which coder to use, e.g. `JsonCoder`
key_builder | which key builder to use, default to builtin `key_builder` | `KeyBuilder` callable | `default_key_builder` | which key builder to use
`injected_dependency_namespace` | `str` | `__fastapi_cache` | prefix for injected dependency keywords.
`cache_status_header` | `str` | `X-FastAPI-Cache` | Name for the header on the response indicating if the request was served from cache; either `HIT` or `MISS`.
You can also use the `@cache` decorator on regular functions to cache their result.
### Injected Request and Response dependencies
The `cache` decorator injects dependencies for the `Request` and `Response`
objects, so that it can add cache control headers to the outgoing response, and
return a 304 Not Modified response when the incoming request has a matching
`If-Non-Match` header. This only happens if the decorated endpoint doesn't already
list these dependencies already.
The keyword arguments for these extra dependencies are named
`__fastapi_cache_request` and `__fastapi_cache_response` to minimize collisions.
Use the `injected_dependency_namespace` argument to `@cache` to change the
prefix used if those names would clash anyway.
And if you want use `ETag` and `Cache-Control` features, you must pass `response` param also. ### Supported data types
You can also use `cache` as decorator like other cache tools to cache common function result. When using the (default) `JsonCoder`, the cache can store any data type that FastAPI can convert to JSON, including Pydantic models and dataclasses,
_provided_ that your endpoint has a correct return type annotation. An
annotation is not needed if the return type is a standard JSON-supported Python
type such as a dictionary or a list.
E.g. for an endpoint that returns a Pydantic model named `SomeModel`, the return annotation is used to ensure that the cached result is converted back to the correct class:
```python
from .models import SomeModel, create_some_model
@app.get("/foo")
@cache(expire=60)
async def foo() -> SomeModel:
return create_some_model()
```
It is not sufficient to configure a response model in the route decorator; the cache needs to know what the method itself returns. If no return type decorator is given, the primitive JSON type is returned instead.
For broader type support, use the `fastapi_cache.coder.PickleCoder` or implement a custom coder (see below).
### Custom coder ### Custom coder
By default use `JsonCoder`, you can write custom coder to encode and decode cache result, just need inherit `fastapi_cache.coder.Coder`. By default use `JsonCoder`, you can write custom coder to encode and decode cache result, just need
inherit `fastapi_cache.coder.Coder`.
```python ```python
from typing import Any
import orjson
from fastapi.encoders import jsonable_encoder
from fastapi_cache import Coder
class ORJsonCoder(Coder):
@classmethod
def encode(cls, value: Any) -> bytes:
return orjson.dumps(
value,
default=jsonable_encoder,
option=orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY,
)
@classmethod
def decode(cls, value: bytes) -> Any:
return orjson.loads(value)
@app.get("/") @app.get("/")
@cache(expire=60,coder=JsonCoder) @cache(expire=60, coder=ORJsonCoder)
async def index(request: Request, response: Response): async def index():
return dict(hello="world") return dict(hello="world")
``` ```
### Custom key builder ### Custom key builder
By default use builtin key builder, if you need, you can override this and pass in `cache` or `FastAPICache.init` to take effect globally. By default the `default_key_builder` builtin key builder is used; this creates a
cache key from the function module and name, and the positional and keyword
arguments converted to their `repr()` representations, encoded as a MD5 hash.
You can provide your own by passing a key builder in to `@cache()`, or to
`FastAPICache.init()` to apply globally.
For example, if you wanted to use the request method, URL and query string as a cache key instead of the function identifier you could use:
```python ```python
def my_key_builder( def request_key_builder(
func, func,
namespace: Optional[str] = "", namespace: str = "",
*,
request: Request = None, request: Request = None,
response: Response = None, response: Response = None,
*args, *args,
**kwargs, **kwargs,
): ):
prefix = FastAPICache.get_prefix() return ":".join([
cache_key = f"{prefix}:{namespace}:{func.__module__}:{func.__name__}:{args}:{kwargs}" namespace,
return cache_key request.method.lower(),
request.url.path,
repr(sorted(request.query_params.items()))
])
@app.get("/") @app.get("/")
@cache(expire=60,coder=JsonCoder,key_builder=my_key_builder) @cache(expire=60, key_builder=request_key_builder)
async def index(request: Request, response: Response): async def index():
return dict(hello="world") return dict(hello="world")
``` ```
## Backend notes
### InMemoryBackend ### InMemoryBackend
`InMemoryBackend` store cache data in memory and use lazy delete, which mean if you don't access it after cached, it will not delete automatically. The `InMemoryBackend` stores cache data in memory and only deletes when an
expired key is accessed. This means that if you don't access a function after
data has been cached, the data will not be removed automatically.
### RedisBackend
When using the Redis backend, please make sure you pass in a redis client that does [_not_ decode responses][redis-decode] (`decode_responses` **must** be `False`, which is the default). Cached data is stored as `bytes` (binary), decoding these in the Redis client would break caching.
[redis-decode]: https://redis-py.readthedocs.io/en/latest/examples/connection_examples.html#by-default-Redis-return-binary-responses,-to-decode-them-use-decode_responses=True
## Tests and coverage
```shell
coverage run -m pytest
coverage html
xdg-open htmlcov/index.html
```
## License ## License

View File

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

View File

@@ -0,0 +1,15 @@
{% if sections[""] -%}
{% for category, val in definitions.items() if category in sections[""] -%}
### {{ definitions[category]['name'] }}
{% for text, values in sections[""][category].items() %}
- {{ text }} {{ values|join(', ') }}
{% endfor %}
{% endfor -%}
{% else -%}
No significant changes.
{% endif -%}

View File

140
examples/in_memory/main.py Normal file
View File

@@ -0,0 +1,140 @@
# pyright: reportGeneralTypeIssues=false
from contextlib import asynccontextmanager
from typing import AsyncIterator, Dict, Optional
import pendulum
import uvicorn
from fastapi import FastAPI
from fastapi_cache import FastAPICache
from fastapi_cache.backends.inmemory import InMemoryBackend
from fastapi_cache.decorator import cache
from pydantic import BaseModel
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
FastAPICache.init(InMemoryBackend())
yield
app = FastAPI(lifespan=lifespan)
ret = 0
@cache(namespace="test", expire=1)
async def get_ret():
global ret
ret = ret + 1
return ret
@app.get("/")
@cache(namespace="test", expire=10)
async def index():
return {"ret": await get_ret()}
@app.get("/clear")
async def clear():
return await FastAPICache.clear(namespace="test")
@app.get("/date")
@cache(namespace="test", expire=10)
async def get_date():
return pendulum.today()
@app.get("/datetime")
@cache(namespace="test", expire=2)
async def get_datetime(request: Request, response: Response):
return {"now": pendulum.now()}
@cache(namespace="test")
async def func_kwargs(*unused_args, **kwargs):
return kwargs
@app.get("/kwargs")
async def get_kwargs(name: str):
return await func_kwargs(name, name=name)
@app.get("/sync-me")
@cache(namespace="test") # pyright: ignore[reportArgumentType]
def sync_me():
# as per the fastapi docs, this sync function is wrapped in a thread,
# thereby converted to async. fastapi-cache does the same.
return 42
@app.get("/cache_response_obj")
@cache(namespace="test", expire=5)
async def cache_response_obj():
return JSONResponse({"a": 1})
class SomeClass:
def __init__(self, value):
self.value = value
async def handler_method(self):
return self.value
# register an instance method as a handler
instance = SomeClass(17)
app.get("/method")(cache(namespace="test")(instance.handler_method))
# cache a Pydantic model instance; the return type annotation is required in this case
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
@app.get("/pydantic_instance")
@cache(namespace="test", expire=5)
async def pydantic_instance() -> Item:
return Item(name="Something", description="An instance of a Pydantic model", price=10.5)
put_ret = 0
@app.put("/uncached_put")
@cache(namespace="test", expire=5)
async def uncached_put():
global put_ret
put_ret = put_ret + 1
return {"value": put_ret}
put_ret2 = 0
@app.get("/cached_put")
@cache(namespace="test", expire=5)
async def cached_put():
global put_ret2
put_ret2 = put_ret2 + 1
return {"value": put_ret2}
@app.get("/namespaced_injection")
@cache(namespace="test", expire=5, injected_dependency_namespace="monty_python") # pyright: ignore[reportArgumentType]
def namespaced_injection(
__fastapi_cache_request: int = 42, __fastapi_cache_response: int = 17
) -> Dict[str, int]:
return {
"__fastapi_cache_request": __fastapi_cache_request,
"__fastapi_cache_response": __fastapi_cache_response,
}
if __name__ == "__main__":
uvicorn.run("main:app", reload=True)

View File

@@ -1,42 +0,0 @@
import aioredis
import uvicorn
from fastapi import FastAPI
from starlette.requests import Request
from starlette.responses import Response
from fastapi_cache import FastAPICache
from fastapi_cache.backends.inmemory import InMemoryBackend
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
app = FastAPI()
ret = 0
@cache(namespace="test", expire=1)
async def get_ret():
global ret
ret = ret + 1
return ret
@app.get("/")
@cache(namespace="test", expire=20)
async def index(request: Request, response: Response):
return dict(ret=await get_ret())
@app.get("/clear")
async def clear():
return await FastAPICache.clear(namespace="test")
@app.on_event("startup")
async def startup():
redis = aioredis.from_url(url="redis://localhost")
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
if __name__ == "__main__":
uvicorn.run("main:app", debug=True, reload=True)

3
examples/pyproject.toml Normal file
View File

@@ -0,0 +1,3 @@
[tool.ruff]
extend = "../pyproject.toml"

View File

10
examples/redis/index.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Cache HTML! {{ ret }} </h1>
</body>
</html>

95
examples/redis/main.py Normal file
View File

@@ -0,0 +1,95 @@
# pyright: reportGeneralTypeIssues=false
import time
from contextlib import asynccontextmanager
from typing import AsyncIterator
import pendulum
import uvicorn
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.coder import PickleCoder
from fastapi_cache.decorator import cache
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
import redis.asyncio as redis
from redis.asyncio.connection import ConnectionPool
@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
pool = ConnectionPool.from_url(url="redis://redis")
r = redis.Redis(connection_pool=pool)
FastAPICache.init(RedisBackend(r), prefix="fastapi-cache")
yield
app = FastAPI(lifespan=lifespan)
app.mount(
path="/static",
app=StaticFiles(directory="./"),
name="static",
)
templates = Jinja2Templates(directory="./")
ret = 0
@cache(namespace="test", expire=1)
async def get_ret():
global ret
ret = ret + 1
return ret
@app.get("/")
@cache(namespace="test", expire=10)
async def index():
return {"ret": await get_ret()}
@app.get("/clear")
async def clear():
return await FastAPICache.clear(namespace="test")
@app.get("/date")
@cache(namespace="test", expire=10)
async def get_data(request: Request, response: Response):
return pendulum.today()
# Note: This function MUST be sync to demonstrate fastapi-cache's correct handling,
# i.e. running cached sync functions in threadpool just like FastAPI itself!
@app.get("/blocking")
@cache(namespace="test", expire=10) # pyright: ignore[reportArgumentType]
def blocking():
time.sleep(2)
return {"ret": 42}
@app.get("/datetime")
@cache(namespace="test", expire=2)
async def get_datetime(request: Request, response: Response):
print(request, response)
return pendulum.now()
@app.get("/html", response_class=HTMLResponse)
@cache(expire=60, namespace="html", coder=PickleCoder)
async def cache_html(request: Request):
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})
if __name__ == "__main__":
uvicorn.run("main:app", reload=True)

View File

@@ -1,26 +1,42 @@
from typing import Callable from importlib.metadata import version
from typing import ClassVar, Optional, Type
from fastapi_cache.coder import Coder, JsonCoder from fastapi_cache.coder import Coder, JsonCoder
from fastapi_cache.key_builder import default_key_builder from fastapi_cache.key_builder import default_key_builder
from fastapi_cache.types import Backend, KeyBuilder
__version__ = version("fastapi-cache2") # pyright: ignore[reportUnknownVariableType]
__all__ = [
"Backend",
"Coder",
"FastAPICache",
"JsonCoder",
"KeyBuilder",
"default_key_builder",
]
class FastAPICache: class FastAPICache:
_backend = None _backend: ClassVar[Optional[Backend]] = None
_prefix = None _prefix: ClassVar[Optional[str]] = None
_expire = None _expire: ClassVar[Optional[int]] = None
_init = False _init: ClassVar[bool] = False
_coder = None _coder: ClassVar[Optional[Type[Coder]]] = None
_key_builder = None _key_builder: ClassVar[Optional[KeyBuilder]] = None
_cache_status_header: ClassVar[Optional[str]] = None
_enable: ClassVar[bool] = True
@classmethod @classmethod
def init( def init(
cls, cls,
backend, backend: Backend,
prefix: str = "", prefix: str = "",
expire: int = None, expire: Optional[int] = None,
coder: Coder = JsonCoder, coder: Type[Coder] = JsonCoder,
key_builder: Callable = default_key_builder, key_builder: KeyBuilder = default_key_builder,
): cache_status_header: str = "X-FastAPI-Cache",
enable: bool = True,
) -> None:
if cls._init: if cls._init:
return return
cls._init = True cls._init = True
@@ -29,29 +45,59 @@ class FastAPICache:
cls._expire = expire cls._expire = expire
cls._coder = coder cls._coder = coder
cls._key_builder = key_builder cls._key_builder = key_builder
cls._cache_status_header = cache_status_header
cls._enable = enable
@classmethod @classmethod
def get_backend(cls): def reset(cls) -> None:
assert cls._backend, "You must call init first!" # nosec: B101 cls._init = False
cls._backend = None
cls._prefix = None
cls._expire = None
cls._coder = None
cls._key_builder = None
cls._cache_status_header = None
cls._enable = True
@classmethod
def get_backend(cls) -> Backend:
assert cls._backend, "You must call init first!" # noqa: S101
return cls._backend return cls._backend
@classmethod @classmethod
def get_prefix(cls): def get_prefix(cls) -> str:
assert cls._prefix is not None, "You must call init first!" # noqa: S101
return cls._prefix return cls._prefix
@classmethod @classmethod
def get_expire(cls): def get_expire(cls) -> Optional[int]:
return cls._expire return cls._expire
@classmethod @classmethod
def get_coder(cls): def get_coder(cls) -> Type[Coder]:
assert cls._coder, "You must call init first!" # noqa: S101
return cls._coder return cls._coder
@classmethod @classmethod
def get_key_builder(cls): def get_key_builder(cls) -> KeyBuilder:
assert cls._key_builder, "You must call init first!" # noqa: S101
return cls._key_builder return cls._key_builder
@classmethod @classmethod
async def clear(cls, namespace: str = None, key: str = None): def get_cache_status_header(cls) -> str:
namespace = cls._prefix + ":" + namespace if namespace else None assert cls._cache_status_header, "You must call init first!" # noqa: S101
return cls._cache_status_header
@classmethod
def get_enable(cls) -> bool:
return cls._enable
@classmethod
async def clear(
cls, namespace: Optional[str] = None, key: Optional[str] = None
) -> int:
assert ( # noqa: S101
cls._backend and cls._prefix is not None
), "You must call init first!"
namespace = cls._prefix + (":" + namespace if namespace else "")
return await cls._backend.clear(namespace, key) return await cls._backend.clear(namespace, key)

View File

@@ -1,20 +1,28 @@
import abc from fastapi_cache.backends import inmemory
from typing import Tuple from fastapi_cache.types import Backend
__all__ = ["Backend", "inmemory"]
class Backend: # import each backend in turn and add to __all__. This syntax
@abc.abstractmethod # is explicitly supported by type checkers, while more dynamic
async def get_with_ttl(self, key: str) -> Tuple[int, str]: # syntax would not be recognised.
raise NotImplementedError try:
from fastapi_cache.backends import dynamodb
except ImportError:
pass
else:
__all__ += ["dynamodb"]
@abc.abstractmethod try:
async def get(self, key: str) -> str: from fastapi_cache.backends import memcached
raise NotImplementedError except ImportError:
pass
else:
__all__ += ["memcached"]
@abc.abstractmethod try:
async def set(self, key: str, value: str, expire: int = None): from fastapi_cache.backends import redis
raise NotImplementedError except ImportError:
pass
@abc.abstractmethod else:
async def clear(self, namespace: str = None, key: str = None) -> int: __all__ += ["redis"]
raise NotImplementedError

View File

@@ -0,0 +1,103 @@
import datetime
from typing import TYPE_CHECKING, Optional, Tuple
from aiobotocore.client import AioBaseClient
from aiobotocore.session import AioSession, get_session
from fastapi_cache.types import Backend
if TYPE_CHECKING:
from types_aiobotocore_dynamodb import DynamoDBClient
else:
DynamoDBClient = AioBaseClient
class DynamoBackend(Backend):
"""
Amazon DynamoDB backend provider
This backend requires an existing table within your AWS environment to be passed during
backend init. If ttl is going to be used, this needs to be manually enabled on the table
using the `ttl` key. Dynamo will take care of deleting outdated objects, but this is not
instant so don't be alarmed when they linger around for a bit.
As with all AWS clients, credentials will be taken from the environment. Check the AWS SDK
for more information.
Usage:
>> dynamodb = DynamoBackend(table_name="your-cache", region="eu-west-1")
>> await dynamodb.init()
>> FastAPICache.init(dynamodb)
"""
client: DynamoDBClient
session: AioSession
table_name: str
region: Optional[str]
def __init__(self, table_name: str, region: Optional[str] = None) -> None:
self.session: AioSession = get_session()
self.table_name = table_name
self.region = region
async def init(self) -> None:
self.client = await self.session.create_client( # pyright: ignore[reportUnknownMemberType]
"dynamodb", region_name=self.region
).__aenter__()
async def close(self) -> None:
self.client = await self.client.__aexit__(None, None, None)
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 "Item" in response:
value = response["Item"].get("value", {}).get("B")
ttl = response["Item"].get("ttl", {}).get("N")
if not ttl:
return -1, value
# It's only eventually consistent so we need to check ourselves
expire = int(ttl) - int(datetime.datetime.now().timestamp())
if expire > 0:
return expire, value
return 0, None
async def get(self, key: str) -> Optional[bytes]:
response = await self.client.get_item(TableName=self.table_name, Key={"key": {"S": key}})
if "Item" in response:
return response["Item"].get("value", {}).get("B")
return None
async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> None:
ttl = (
{
"ttl": {
"N": str(
int(
(
datetime.datetime.now() + datetime.timedelta(seconds=expire)
).timestamp()
)
)
}
}
if expire
else {}
)
await self.client.put_item(
TableName=self.table_name,
Item={
**{
"key": {"S": key},
"value": {"B": value},
},
**ttl,
},
)
async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
raise NotImplementedError

View File

@@ -3,12 +3,12 @@ from asyncio import Lock
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
from fastapi_cache.backends import Backend from fastapi_cache.types import Backend
@dataclass @dataclass
class Value: class Value:
data: str data: bytes
ttl_ts: int ttl_ts: int
@@ -20,32 +20,34 @@ class InMemoryBackend(Backend):
def _now(self) -> int: def _now(self) -> int:
return int(time.time()) return int(time.time())
def _get(self, key: str): def _get(self, key: str) -> Optional[Value]:
v = self._store.get(key) v = self._store.get(key)
if v: if v:
if v.ttl_ts < self._now: if v.ttl_ts < self._now:
del self._store[key] del self._store[key]
else: else:
return v return v
return None
async def get_with_ttl(self, key: str) -> Tuple[int, Optional[str]]: async def get_with_ttl(self, key: str) -> Tuple[int, Optional[bytes]]:
async with self._lock: async with self._lock:
v = self._get(key) v = self._get(key)
if v: if v:
return v.ttl_ts - self._now, v.data return v.ttl_ts - self._now, v.data
return 0, None return 0, None
async def get(self, key: str) -> str: async def get(self, key: str) -> Optional[bytes]:
async with self._lock: async with self._lock:
v = self._get(key) v = self._get(key)
if v: if v:
return v.data return v.data
return None
async def set(self, key: str, value: str, expire: int = None): async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> None:
async with self._lock: async with self._lock:
self._store[key] = Value(value, self._now + expire) self._store[key] = Value(value, self._now + (expire or 0))
async def clear(self, namespace: str = None, key: str = None) -> int: async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
count = 0 count = 0
if namespace: if namespace:
keys = list(self._store.keys()) keys = list(self._store.keys())

View File

@@ -1,22 +1,22 @@
from typing import Tuple from typing import Optional, Tuple
from aiomcache import Client from aiomcache import Client
from fastapi_cache.backends import Backend from fastapi_cache.types import Backend
class MemcachedBackend(Backend): class MemcachedBackend(Backend):
def __init__(self, mcache: Client): def __init__(self, mcache: Client):
self.mcache = mcache self.mcache = mcache
async def get_with_ttl(self, key: str) -> Tuple[int, str]: async def get_with_ttl(self, key: str) -> Tuple[int, Optional[bytes]]:
return 3600, await self.mcache.get(key.encode()) return 3600, await self.get(key)
async def get(self, key: str): async def get(self, key: str) -> Optional[bytes]:
return await self.mcache.get(key, key.encode()) return await self.mcache.get(key.encode())
async def set(self, key: str, value: str, expire: int = None): async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> None:
return await self.mcache.set(key.encode(), value.encode(), exptime=expire or 0) await self.mcache.set(key.encode(), value, exptime=expire or 0)
async def clear(self, namespace: str = None, key: str = None): async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
raise NotImplementedError raise NotImplementedError

View File

@@ -1,27 +1,30 @@
from typing import Tuple from typing import Optional, Tuple, Union
from aioredis import Redis from redis.asyncio.client import Redis
from redis.asyncio.cluster import RedisCluster
from fastapi_cache.backends import Backend from fastapi_cache.types import Backend
class RedisBackend(Backend): class RedisBackend(Backend):
def __init__(self, redis: Redis): def __init__(self, redis: Union["Redis[bytes]", "RedisCluster[bytes]"]):
self.redis = redis self.redis = redis
self.is_cluster: bool = isinstance(redis, RedisCluster)
async def get_with_ttl(self, key: str) -> Tuple[int, str]: async def get_with_ttl(self, key: str) -> Tuple[int, Optional[bytes]]:
async with self.redis.pipeline(transaction=True) as pipe: async with self.redis.pipeline(transaction=not self.is_cluster) as pipe:
return await (pipe.ttl(key).get(key).execute()) return await pipe.ttl(key).get(key).execute() # type: ignore[union-attr,no-any-return]
async def get(self, key) -> str: async def get(self, key: str) -> Optional[bytes]:
return await self.redis.get(key) return await self.redis.get(key) # type: ignore[union-attr]
async def set(self, key: str, value: str, expire: int = None): async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> None:
return await self.redis.set(key, value, ex=expire) await self.redis.set(key, value, ex=expire) # type: ignore[union-attr]
async def clear(self, namespace: str = None, key: str = None) -> int: async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
if namespace: if namespace:
lua = f"for i, name in ipairs(redis.call('KEYS', '{namespace}:*')) do redis.call('DEL', name); end" lua = f"for i, name in ipairs(redis.call('KEYS', '{namespace}:*')) do redis.call('DEL', name); end"
return await self.redis.eval(lua, numkeys=0) return await self.redis.eval(lua, numkeys=0) # type: ignore[union-attr,no-any-return]
elif key: elif key:
return await self.redis.delete(key) return await self.redis.delete(key) # type: ignore[union-attr]
return 0

View File

@@ -2,34 +2,52 @@ import datetime
import json import json
import pickle # nosec:B403 import pickle # nosec:B403
from decimal import Decimal from decimal import Decimal
from typing import Any from typing import (
Any,
Callable,
ClassVar,
Dict,
Optional,
TypeVar,
Union,
overload,
)
import dateutil.parser 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, # pyright: ignore[reportPrivateUsage]
)
CONVERTERS = {
"date": dateutil.parser.parse, class ModelField:
"datetime": dateutil.parser.parse, pass
_T = TypeVar("_T", bound=type)
CONVERTERS: Dict[str, Callable[[str], Any]] = {
# Pendulum 3.0.0 adds parse to __all__, at which point these ignores can be removed
"date": lambda x: pendulum.parse(x, exact=True),
"datetime": lambda x: pendulum.parse(x, exact=True),
"decimal": Decimal, "decimal": Decimal,
} }
class JsonEncoder(json.JSONEncoder): class JsonEncoder(json.JSONEncoder):
def default(self, obj): def default(self, o: Any) -> Any:
if isinstance(obj, datetime.datetime): if isinstance(o, datetime.datetime):
if obj.tzinfo: return {"val": str(o), "_spec_type": "datetime"}
return {"val": obj.strftime("%Y-%m-%d %H:%M:%S%z"), "_spec_type": "datetime"} elif isinstance(o, datetime.date):
return {"val": str(o), "_spec_type": "date"}
elif isinstance(o, Decimal):
return {"val": str(o), "_spec_type": "decimal"}
else: else:
return {"val": obj.strftime("%Y-%m-%d %H:%M:%S"), "_spec_type": "datetime"} return jsonable_encoder(o)
elif isinstance(obj, datetime.date):
return {"val": obj.strftime("%Y-%m-%d"), "_spec_type": "date"}
elif isinstance(obj, Decimal):
return {"val": str(obj), "_spec_type": "decimal"}
else:
return jsonable_encoder(obj)
def object_hook(obj): def object_hook(obj: Any) -> Any:
_spec_type = obj.get("_spec_type") _spec_type = obj.get("_spec_type")
if not _spec_type: if not _spec_type:
return obj return obj
@@ -37,34 +55,75 @@ def object_hook(obj):
if _spec_type in CONVERTERS: if _spec_type in CONVERTERS:
return CONVERTERS[_spec_type](obj["val"]) return CONVERTERS[_spec_type](obj["val"])
else: else:
raise TypeError("Unknown {}".format(_spec_type)) raise TypeError(f"Unknown {_spec_type}")
class Coder: class Coder:
@classmethod @classmethod
def encode(cls, value: Any): def encode(cls, value: Any) -> bytes:
raise NotImplementedError raise NotImplementedError
@classmethod @classmethod
def decode(cls, value: Any): def decode(cls, value: bytes) -> Any:
raise NotImplementedError raise NotImplementedError
# (Shared) cache for endpoint return types to Pydantic model fields.
# Note that subclasses share this cache! If a subclass overrides the
# decode_as_type method and then stores a different kind of field for a
# given type, do make sure that the subclass provides its own class
# attribute for this cache.
_type_field_cache: ClassVar[Dict[Any, ModelField]] = {}
@overload
@classmethod
def decode_as_type(cls, value: bytes, *, type_: _T) -> _T:
...
@overload
@classmethod
def decode_as_type(cls, value: bytes, *, type_: None) -> Any:
...
@classmethod
def decode_as_type(cls, value: bytes, *, type_: Optional[_T]) -> Union[_T, Any]:
"""Decode value to the specific given type
The default implementation uses the Pydantic model system to convert the value.
"""
result = cls.decode(value)
return result
class JsonCoder(Coder): class JsonCoder(Coder):
@classmethod @classmethod
def encode(cls, value: Any): def encode(cls, value: Any) -> Any:
return json.dumps(value, cls=JsonEncoder) if isinstance(value, JSONResponse):
return value.body
return json.dumps(value, cls=JsonEncoder).encode()
@classmethod @classmethod
def decode(cls, value: Any): def decode(cls, value: bytes) -> Any:
return json.loads(value, object_hook=object_hook) # explicitly decode from UTF-8 bytes first, as otherwise
# json.loads() will first have to detect the correct UTF-
# encoding used.
return json.loads(value.decode(), object_hook=object_hook)
class PickleCoder(Coder): class PickleCoder(Coder):
@classmethod @classmethod
def encode(cls, value: Any): def encode(cls, value: Any) -> bytes:
if isinstance(value, TemplateResponse):
value = value.body
return pickle.dumps(value) return pickle.dumps(value)
@classmethod @classmethod
def decode(cls, value: Any): def decode(cls, value: bytes) -> Any:
return pickle.loads(value) # nosec:B403 return pickle.loads(value) # noqa: S301
@classmethod
def decode_as_type(cls, value: bytes, *, type_: Optional[_T]) -> Any:
# Pickle already produces the correct type on decoding, no point
# in paying an extra performance penalty for pydantic to discover
# the same.
return cls.decode(value)

View File

@@ -1,69 +1,230 @@
import logging
import sys
from functools import wraps from functools import wraps
from typing import Callable, Optional, Type from inspect import Parameter, Signature, isawaitable, iscoroutinefunction
from typing import (
Awaitable,
Callable,
List,
Optional,
Type,
TypeVar,
Union,
cast,
)
if sys.version_info >= (3, 10):
from typing import ParamSpec
else:
from typing_extensions import ParamSpec
from fastapi.concurrency import run_in_threadpool
from fastapi.dependencies.utils import (
get_typed_return_annotation,
get_typed_signature,
)
from starlette.requests import Request
from starlette.responses import Response
from starlette.status import HTTP_304_NOT_MODIFIED
from fastapi_cache import FastAPICache from fastapi_cache import FastAPICache
from fastapi_cache.coder import Coder from fastapi_cache.coder import Coder
from fastapi_cache.types import KeyBuilder
logger: logging.Logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
P = ParamSpec("P")
R = TypeVar("R")
def _augment_signature(signature: Signature, *extra: Parameter) -> Signature:
if not extra:
return signature
parameters = list(signature.parameters.values())
variadic_keyword_params: List[Parameter] = []
while parameters and parameters[-1].kind is Parameter.VAR_KEYWORD:
variadic_keyword_params.append(parameters.pop())
return signature.replace(parameters=[*parameters, *extra, *variadic_keyword_params])
def _locate_param(
sig: Signature, dep: Parameter, to_inject: List[Parameter]
) -> Parameter:
"""Locate an existing parameter in the decorated endpoint
If not found, returns the injectable parameter, and adds it to the to_inject list.
"""
param = next(
(p for p in sig.parameters.values() if p.annotation is dep.annotation), None
)
if param is None:
to_inject.append(dep)
param = dep
return param
def _uncacheable(request: Optional[Request]) -> bool:
"""Determine if this request should not be cached
Returns true if:
- Caching has been disabled globally
- This is not a GET request
- The request has a Cache-Control header with a value of "no-store"
"""
if not FastAPICache.get_enable():
return True
if request is None:
return False
if request.method != "GET":
return True
return request.headers.get("Cache-Control") == "no-store"
def cache( def cache(
expire: int = None, expire: Optional[int] = None,
coder: Type[Coder] = None, coder: Optional[Type[Coder]] = None,
key_builder: Callable = None, key_builder: Optional[KeyBuilder] = None,
namespace: Optional[str] = "", namespace: str = "",
): injected_dependency_namespace: str = "__fastapi_cache",
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[Union[R, Response]]]]:
""" """
cache all function cache all function
:param injected_dependency_namespace:
:param namespace: :param namespace:
:param expire: :param expire:
:param coder: :param coder:
:param key_builder: :param key_builder:
:return: :return:
""" """
def wrapper(func): injected_request = Parameter(
name=f"{injected_dependency_namespace}_request",
annotation=Request,
kind=Parameter.KEYWORD_ONLY,
)
injected_response = Parameter(
name=f"{injected_dependency_namespace}_response",
annotation=Response,
kind=Parameter.KEYWORD_ONLY,
)
def wrapper(
func: Callable[P, Awaitable[R]]
) -> Callable[P, Awaitable[Union[R, Response]]]:
# get_typed_signature ensures that any forward references are resolved first
wrapped_signature = get_typed_signature(func)
to_inject: List[Parameter] = []
request_param = _locate_param(wrapped_signature, injected_request, to_inject)
response_param = _locate_param(wrapped_signature, injected_response, to_inject)
return_type = get_typed_return_annotation(func)
@wraps(func) @wraps(func)
async def inner(*args, **kwargs): async def inner(*args: P.args, **kwargs: P.kwargs) -> Union[R, Response]:
nonlocal coder nonlocal coder
nonlocal expire nonlocal expire
nonlocal key_builder nonlocal key_builder
copy_kwargs = kwargs.copy()
request = copy_kwargs.pop("request", None)
response = copy_kwargs.pop("response", None)
if request and request.headers.get("Cache-Control") == "no-store":
return await func(*args, **kwargs)
async def ensure_async_func(*args: P.args, **kwargs: P.kwargs) -> R:
"""Run cached sync functions in thread pool just like FastAPI."""
# 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
kwargs.pop(injected_request.name, None)
kwargs.pop(injected_response.name, None)
if iscoroutinefunction(func):
# async, return as is.
# unintuitively, we have to await once here, so that caller
# does not have to await twice. See
# https://stackoverflow.com/a/59268198/532513
return await func(*args, **kwargs)
else:
# sync, wrap in thread and return async
# see above why we have to await even although caller also awaits.
return await run_in_threadpool(func, *args, **kwargs) # type: ignore[arg-type]
copy_kwargs = kwargs.copy()
request: Optional[Request] = copy_kwargs.pop(request_param.name, None) # type: ignore[assignment]
response: Optional[Response] = copy_kwargs.pop(response_param.name, None) # type: ignore[assignment]
if _uncacheable(request):
return await ensure_async_func(*args, **kwargs)
prefix = FastAPICache.get_prefix()
coder = coder or FastAPICache.get_coder() coder = coder or FastAPICache.get_coder()
expire = expire or FastAPICache.get_expire() expire = expire or FastAPICache.get_expire()
key_builder = key_builder or FastAPICache.get_key_builder() key_builder = key_builder or FastAPICache.get_key_builder()
backend = FastAPICache.get_backend() backend = FastAPICache.get_backend()
cache_status_header = FastAPICache.get_cache_status_header()
cache_key = key_builder( cache_key = key_builder(
func, namespace, request=request, response=response, args=args, kwargs=copy_kwargs func,
f"{prefix}:{namespace}",
request=request,
response=response,
args=args,
kwargs=copy_kwargs,
)
if isawaitable(cache_key):
cache_key = await cache_key
assert isinstance(cache_key, str) # noqa: S101 # assertion is a type guard
try:
ttl, cached = await backend.get_with_ttl(cache_key)
except Exception:
logger.warning(
f"Error retrieving cache key '{cache_key}' from backend:",
exc_info=True,
)
ttl, cached = 0, None
if cached is None or (request is not None and request.headers.get("Cache-Control") == "no-cache") : # cache miss
result = await ensure_async_func(*args, **kwargs)
to_cache = coder.encode(result)
try:
await backend.set(cache_key, to_cache, expire)
except Exception:
logger.warning(
f"Error setting cache key '{cache_key}' in backend:",
exc_info=True,
) )
ttl, ret = await backend.get_with_ttl(cache_key)
if not request:
if ret is not None:
return coder.decode(ret)
ret = await func(*args, **kwargs)
await backend.set(cache_key, coder.encode(ret), expire or FastAPICache.get_expire())
return ret
if request.method != "GET":
return await func(request, *args, **kwargs)
if_none_match = request.headers.get("if-none-match")
if ret is not None:
if response: if response:
response.headers["Cache-Control"] = f"max-age={ttl}" response.headers.update(
etag = f"W/{hash(ret)}" {
if if_none_match == etag: "Cache-Control": f"max-age={expire}",
response.status_code = 304 "ETag": f"W/{hash(to_cache)}",
return response cache_status_header: "MISS",
response.headers["ETag"] = etag }
return coder.decode(ret) )
ret = await func(*args, **kwargs) else: # cache hit
await backend.set(cache_key, coder.encode(ret), expire or FastAPICache.get_expire()) if response:
return ret etag = f"W/{hash(cached)}"
response.headers.update(
{
"Cache-Control": f"max-age={ttl}",
"ETag": etag,
cache_status_header: "HIT",
}
)
if_none_match = request and request.headers.get("if-none-match")
if if_none_match == etag:
response.status_code = HTTP_304_NOT_MODIFIED
return response
result = cast(R, coder.decode_as_type(cached, type_=return_type))
return result
inner.__signature__ = _augment_signature(wrapped_signature, *to_inject) # type: ignore[attr-defined]
return inner return inner

View File

@@ -1,25 +1,20 @@
import hashlib import hashlib
from typing import Optional from typing import Any, Callable, Dict, Optional, Tuple
from starlette.requests import Request from starlette.requests import Request
from starlette.responses import Response from starlette.responses import Response
def default_key_builder( def default_key_builder(
func, func: Callable[..., Any],
namespace: Optional[str] = "", namespace: str = "",
*,
request: Optional[Request] = None, request: Optional[Request] = None,
response: Optional[Response] = None, response: Optional[Response] = None,
args: Optional[tuple] = None, args: Tuple[Any, ...],
kwargs: Optional[dict] = None, kwargs: Dict[str, Any],
): ) -> str:
from fastapi_cache import FastAPICache cache_key = hashlib.md5( # noqa: S324
prefix = f"{FastAPICache.get_prefix()}:{namespace}:"
cache_key = (
prefix
+ hashlib.md5( # nosec:B303
f"{func.__module__}:{func.__name__}:{args}:{kwargs}".encode() f"{func.__module__}:{func.__name__}:{args}:{kwargs}".encode()
).hexdigest() ).hexdigest()
) return f"{namespace}:{cache_key}"
return cache_key

0
fastapi_cache/py.typed Normal file
View File

40
fastapi_cache/types.py Normal file
View File

@@ -0,0 +1,40 @@
import abc
from typing import Any, Awaitable, Callable, Dict, Optional, Tuple, Union
from starlette.requests import Request
from starlette.responses import Response
from typing_extensions import Protocol
_Func = Callable[..., Any]
class KeyBuilder(Protocol):
def __call__(
self,
__function: _Func,
__namespace: str = ...,
*,
request: Optional[Request] = ...,
response: Optional[Response] = ...,
args: Tuple[Any, ...],
kwargs: Dict[str, Any],
) -> Union[Awaitable[str], str]:
...
class Backend(abc.ABC):
@abc.abstractmethod
async def get_with_ttl(self, key: str) -> Tuple[int, Optional[bytes]]:
raise NotImplementedError
@abc.abstractmethod
async def get(self, key: str) -> Optional[bytes]:
raise NotImplementedError
@abc.abstractmethod
async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> None:
raise NotImplementedError
@abc.abstractmethod
async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
raise NotImplementedError

3348
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.1.6" version = "0.2.2"
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"
@@ -15,25 +15,101 @@ packages = [
include = ["LICENSE", "README.md"] include = ["LICENSE", "README.md"]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.7" python = "^3.8"
fastapi = "*" fastapi = "*"
uvicorn = "*" uvicorn = "*"
aioredis = {version = ">=2.0.0b1", optional = true} typing-extensions = { version = ">=4.1.0" }
aiomcache = {version = "*", optional = true} importlib-metadata = { version = ">=6.6,<9.0", python = "<3.8" }
python-dateutil = "*" pendulum = "^3.0.0"
aiomcache = { version = "^0.8.2", optional = true }
aiobotocore = {version = "^2.13.1", optional = true}
redis = {version = "^5.0.8", extras = ["redis"]}
[tool.poetry.dev-dependencies] [tool.poetry.group.linting]
flake8 = "*" optional = true
isort = "*"
black = "^19.10b0" [tool.poetry.group.linting.dependencies]
mypy = { version = "^1.2.0", python = "^3.10" }
pyright = { version = "^1.1.373", 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" }
ruff = { version = ">=0.0.267,<0.1.2", python = "^3.10" }
[tool.poetry.group.dev.dependencies]
pytest = "*" pytest = "*"
bandit = "*" requests = "*"
coverage = ">=6.5,<8.0"
httpx = "*"
tox = "^4.5.1"
towncrier = "^22.12.0"
[build-system] [tool.poetry.group.distributing]
requires = ["poetry>=0.12"] optional = true
build-backend = "poetry.masonry.api"
[tool.poetry.group.distributing.dependencies]
twine = { version = "^4.0.2", python = "^3.10" }
[tool.poetry.extras] [tool.poetry.extras]
redis = ["aioredis"] redis = ["redis"]
memcache = ["aiomcache"] memcache = ["aiomcache"]
all = ["aioredis","aiomcache"] dynamodb = ["aiobotocore"]
all = ["redis", "aiomcache", "aiobotocore"]
[tool.mypy]
files = ["."]
python_version = "3.8"
# equivalent of --strict
warn_unused_configs = true
disallow_any_generics = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
no_implicit_reexport = true
strict_equality = true
extra_checks = true
[[tool.mypy.overrides]]
module = "examples.*.main"
ignore_errors = true
[tool.towncrier]
directory = "changelog.d"
filename = "CHANGELOG.md"
package = "fastapi_cache"
start_string = "<!-- towncrier release notes start -->\n"
underlines = ["", "", ""]
template = "changelog.d/changelog_template.jinja"
title_format = "## [{version}](https://github.com/long2ice/fastapi-cache/tree/{version}) - {project_date}"
issue_format = "[#{issue}](https://github.com/long2ice/fastapi-cache/issues/{issue})"
[tool.pyright]
strict = ["fastapi_cache", "tests"]
pythonVersion = "3.8"
[tool.pytest.ini_options]
addopts = "-p no:warnings"
[tool.ruff]
ignore = ["E501"]
line-length = 80
select = [
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"E", # pycodestyle errors
"F", # pyflakes
"I", # isort
"S", # flake8-bandit
"W", # pycodestyle warnings
"UP", # pyupgrade
]
target-version = "py38"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@@ -1,5 +0,0 @@
[flake8]
ignore = E501,W503
[tool:pytest]
addopts = -p no:warnings

10
tests/pyproject.toml Normal file
View File

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

66
tests/test_codecs.py Normal file
View File

@@ -0,0 +1,66 @@
from dataclasses import dataclass
from typing import Any, Optional, Tuple, Type
import pytest
from pydantic import BaseModel, ValidationError
from fastapi_cache.coder import JsonCoder, PickleCoder
@dataclass
class DCItem:
name: str
price: float
description: Optional[str] = None
tax: Optional[float] = None
class PDItem(BaseModel):
name: str
price: float
description: Optional[str] = None
tax: Optional[float] = None
@pytest.mark.parametrize(
"value",
[
1,
"some_string",
(1, 2),
[1, 2, 3],
{"some_key": 1, "other_key": 2},
DCItem(name="foo", price=42.0, description="some dataclass item", tax=0.2),
PDItem(name="foo", price=42.0, description="some pydantic item", tax=0.2),
],
)
def test_pickle_coder(value: Any) -> None:
encoded_value = PickleCoder.encode(value)
assert isinstance(encoded_value, bytes)
decoded_value = PickleCoder.decode(encoded_value)
assert decoded_value == value
@pytest.mark.parametrize(
("value", "return_type"),
[
(1, None),
("some_string", None),
((1, 2), Tuple[int, int]),
([1, 2, 3], None),
({"some_key": 1, "other_key": 2}, None),
(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),
],
)
def test_json_coder(value: Any, return_type: Type[Any]) -> None:
encoded_value = JsonCoder.encode(value)
assert isinstance(encoded_value, bytes)
decoded_value = JsonCoder.decode_as_type(encoded_value, type_=return_type)
assert decoded_value == value
def test_json_coder_validation_error() -> None:
invalid = b'{"name": "incomplete"}'
with pytest.raises(ValidationError):
JsonCoder.decode_as_type(invalid, type_=PDItem)

View File

@@ -1,2 +1,137 @@
def test_default_key_builder(): import time
return 1 from typing import Any, Generator
import pendulum
import pytest
from starlette.testclient import TestClient
from examples.in_memory.main import app
from fastapi_cache import FastAPICache
from fastapi_cache.backends.inmemory import InMemoryBackend
@pytest.fixture(autouse=True)
def _init_cache() -> Generator[Any, Any, None]: # pyright: ignore[reportUnusedFunction]
FastAPICache.init(InMemoryBackend())
yield
FastAPICache.reset()
def test_datetime() -> None:
with TestClient(app) as client:
response = client.get("/datetime")
assert response.headers.get("X-FastAPI-Cache") == "MISS"
now = response.json().get("now")
now_ = pendulum.now()
assert pendulum.parse(now) == now_
response = client.get("/datetime")
assert response.headers.get("X-FastAPI-Cache") == "HIT"
now = response.json().get("now")
assert pendulum.parse(now) == now_
time.sleep(3)
response = client.get("/datetime")
now = response.json().get("now")
assert response.headers.get("X-FastAPI-Cache") == "MISS"
now = pendulum.parse(now)
assert now != now_
assert now == pendulum.now()
def test_date() -> None:
"""Test path function without request or response arguments."""
with TestClient(app) as client:
response = client.get("/date")
assert response.headers.get("X-FastAPI-Cache") == "MISS"
assert pendulum.parse(response.json()) == pendulum.today()
# do it again to test cache
response = client.get("/date")
assert response.headers.get("X-FastAPI-Cache") == "HIT"
assert pendulum.parse(response.json()) == pendulum.today()
# now test with cache disabled, as that's a separate code path
FastAPICache._enable = False # pyright: ignore[reportPrivateUsage]
response = client.get("/date")
assert "X-FastAPI-Cache" not in response.headers
assert pendulum.parse(response.json()) == pendulum.today()
FastAPICache._enable = True # pyright: ignore[reportPrivateUsage]
def test_sync() -> None:
"""Ensure that sync function support works."""
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")
def test_kwargs() -> None:
with TestClient(app) as client:
name = "Jon"
response = client.get("/kwargs", params={"name": name})
assert "X-FastAPI-Cache" not in response.headers
assert response.json() == {"name": name}
def test_method() -> None:
with TestClient(app) as client:
response = client.get("/method")
assert response.json() == 17
def test_pydantic_model() -> None:
with TestClient(app) as client:
r1 = client.get("/pydantic_instance")
assert r1.headers.get("X-FastAPI-Cache") == "MISS"
r2 = client.get("/pydantic_instance")
assert r2.headers.get("X-FastAPI-Cache") == "HIT"
assert r1.json() == r2.json()
def test_non_get() -> None:
with TestClient(app) as client:
response = client.put("/cached_put")
assert "X-FastAPI-Cache" not in response.headers
assert response.json() == {"value": 1}
response = client.put("/cached_put")
assert "X-FastAPI-Cache" not in response.headers
assert response.json() == {"value": 2}
def test_alternate_injected_namespace() -> None:
with TestClient(app) as client:
response = client.get("/namespaced_injection")
assert response.headers.get("X-FastAPI-Cache") == "MISS"
assert response.json() == {"__fastapi_cache_request": 42, "__fastapi_cache_response": 17}
def test_cache_control() -> None:
with TestClient(app) as client:
response = client.get("/cached_put")
assert response.json() == {"value": 1}
# HIT
response = client.get("/cached_put")
assert response.json() == {"value": 1}
# no-cache
response = client.get("/cached_put", headers={"Cache-Control": "no-cache"})
assert response.json() == {"value": 2}
response = client.get("/cached_put")
assert response.json() == {"value": 2}
# no-store
response = client.get("/cached_put", headers={"Cache-Control": "no-store"})
assert response.json() == {"value": 3}
response = client.get("/cached_put")
assert response.json() == {"value": 2}

51
tox.ini Normal file
View File

@@ -0,0 +1,51 @@
[tox]
env_list = py38,py39,py310,py311
minversion = 4.5.1
[gh-actions]
# Map Github Actions Python version to environment factors
# Requires tox-gh-actions 3.x is installed in the GitHub action
python =
3.8: py38
3.9: py39
3.10: py310
3.11: py311
[testenv]
description = Run the tests with pytest
package = wheel
extras = all
set_env =
# trick poetry into adopting the tox virtualenv
POETRY_VIRTUALENVS_PATH = {[tox]work_dir}
allowlist_externals = poetry
commands_pre =
poetry install --no-root --sync --all-extras
commands =
python -X dev -m pytest {tty:--color=yes} {posargs}
[testenv:lint]
description = Run the linters
skip_install = true
commands_pre =
poetry install --no-root --with=linting --sync --all-extras
commands =
ruff check --show-source .
mypy
pyright
[testenv:format]
description = Format the code
skip_install = true
commands_pre =
poetry install --no-root --sync --with=linting
commands =
ruff check --fix .
[testenv:lint_distributions]
description = Lint distribution files with Twine
skip_install = true
commands_pre =
poetry install --no-root --sync --only=distributing
commands =
twine check dist/*