ttl-cache-decorator 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ttl_cache_decorator-0.1.2/.gitattributes +2 -0
- ttl_cache_decorator-0.1.2/.github/workflows/docs.yml +38 -0
- ttl_cache_decorator-0.1.2/.github/workflows/publish.yml +33 -0
- ttl_cache_decorator-0.1.2/.github/workflows/release.yml +43 -0
- ttl_cache_decorator-0.1.2/.github/workflows/test.yml +44 -0
- ttl_cache_decorator-0.1.2/.gitignore +40 -0
- ttl_cache_decorator-0.1.2/.python-version +1 -0
- ttl_cache_decorator-0.1.2/.vscode/extensions.json +15 -0
- ttl_cache_decorator-0.1.2/.vscode/launch.json +14 -0
- ttl_cache_decorator-0.1.2/.vscode/settings.json +49 -0
- ttl_cache_decorator-0.1.2/CHANGELOG.md +13 -0
- ttl_cache_decorator-0.1.2/LICENSE.txt +18 -0
- ttl_cache_decorator-0.1.2/PKG-INFO +58 -0
- ttl_cache_decorator-0.1.2/README.md +33 -0
- ttl_cache_decorator-0.1.2/cliff.toml +37 -0
- ttl_cache_decorator-0.1.2/docs/index.md +138 -0
- ttl_cache_decorator-0.1.2/pyproject.toml +129 -0
- ttl_cache_decorator-0.1.2/src/ttl_cache/__init__.py +1 -0
- ttl_cache_decorator-0.1.2/src/ttl_cache/_ttl_lru.py +83 -0
- ttl_cache_decorator-0.1.2/tests/__init__.py +3 -0
- ttl_cache_decorator-0.1.2/tests/test_ttl_cache.py +35 -0
- ttl_cache_decorator-0.1.2/uv.lock +676 -0
- ttl_cache_decorator-0.1.2/zensical.toml +122 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
tags:
|
|
7
|
+
- "v*"
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
concurrency:
|
|
11
|
+
group: docs-publish
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
jobs:
|
|
14
|
+
publish-docs:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
- uses: astral-sh/setup-uv@v6
|
|
21
|
+
with:
|
|
22
|
+
enable-cache: true
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: |
|
|
25
|
+
uv sync --group docs --no-install-project
|
|
26
|
+
uv pip install git+https://github.com/squidfunk/mike.git
|
|
27
|
+
- name: Configure git
|
|
28
|
+
run: |
|
|
29
|
+
git config user.name "${{ github.actor }}"
|
|
30
|
+
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
|
|
31
|
+
- name: Publish dev docs
|
|
32
|
+
if: github.ref == 'refs/heads/main'
|
|
33
|
+
run: uv run mike deploy --push dev
|
|
34
|
+
- name: Publish release docs
|
|
35
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
36
|
+
run: |
|
|
37
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
38
|
+
uv run mike deploy --push --update-aliases "$VERSION" latest
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags:
|
|
5
|
+
- "v*"
|
|
6
|
+
workflow_run:
|
|
7
|
+
workflows:
|
|
8
|
+
- Release
|
|
9
|
+
types:
|
|
10
|
+
- completed
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
jobs:
|
|
14
|
+
publish:
|
|
15
|
+
if: >-
|
|
16
|
+
github.event_name == 'push' ||
|
|
17
|
+
github.event.workflow_run.conclusion == 'success'
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
environment:
|
|
20
|
+
name: pypi
|
|
21
|
+
url: https://pypi.org/p/ttl-cache-decorator
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v7
|
|
24
|
+
with:
|
|
25
|
+
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.ref }}
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
- uses: astral-sh/setup-uv@v6
|
|
28
|
+
with:
|
|
29
|
+
enable-cache: true
|
|
30
|
+
- name: Build distributions
|
|
31
|
+
run: uv build
|
|
32
|
+
- name: Publish to PyPI
|
|
33
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
on:
|
|
3
|
+
workflow_dispatch:
|
|
4
|
+
inputs:
|
|
5
|
+
bump:
|
|
6
|
+
description: Version bump
|
|
7
|
+
type: choice
|
|
8
|
+
options:
|
|
9
|
+
- patch
|
|
10
|
+
- minor
|
|
11
|
+
- major
|
|
12
|
+
default: patch
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write
|
|
15
|
+
jobs:
|
|
16
|
+
release:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
# A personal access token is required so the pushed tag triggers the
|
|
20
|
+
# test/docs/publish workflows — pushes made with the default
|
|
21
|
+
# GITHUB_TOKEN never start new workflow runs.
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
with:
|
|
24
|
+
token: ${{ secrets.RELEASE_PAT || github.token }}
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
- uses: astral-sh/setup-uv@v6
|
|
27
|
+
- uses: taiki-e/install-action@v2
|
|
28
|
+
with:
|
|
29
|
+
tool: git-cliff
|
|
30
|
+
- name: Bump version
|
|
31
|
+
run: uv version --bump ${{ inputs.bump }}
|
|
32
|
+
- name: Generate changelog
|
|
33
|
+
run: git-cliff --config cliff.toml --tag "v$(uv version --short)" --output CHANGELOG.md
|
|
34
|
+
- name: Commit, tag and push
|
|
35
|
+
run: |
|
|
36
|
+
VERSION=$(uv version --short)
|
|
37
|
+
git config user.name "${{ github.actor }}"
|
|
38
|
+
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
|
|
39
|
+
git add pyproject.toml uv.lock CHANGELOG.md
|
|
40
|
+
git commit -m "chore: release v$VERSION"
|
|
41
|
+
git tag -a "v$VERSION" -m "Release v$VERSION"
|
|
42
|
+
git push origin main
|
|
43
|
+
git push origin "v$VERSION"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
tags:
|
|
7
|
+
- "v*"
|
|
8
|
+
pull_request:
|
|
9
|
+
workflow_run:
|
|
10
|
+
workflows:
|
|
11
|
+
- Release
|
|
12
|
+
types:
|
|
13
|
+
- completed
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
concurrency:
|
|
17
|
+
group: tests-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
jobs:
|
|
20
|
+
test:
|
|
21
|
+
runs-on: ${{ matrix.os }}
|
|
22
|
+
strategy:
|
|
23
|
+
fail-fast: false
|
|
24
|
+
matrix:
|
|
25
|
+
os:
|
|
26
|
+
- ubuntu-latest
|
|
27
|
+
- windows-latest
|
|
28
|
+
- macos-latest
|
|
29
|
+
python-version:
|
|
30
|
+
- "3.12"
|
|
31
|
+
- "3.13"
|
|
32
|
+
- "3.14"
|
|
33
|
+
- "3.15"
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v7
|
|
36
|
+
- uses: astral-sh/setup-uv@v6
|
|
37
|
+
with:
|
|
38
|
+
enable-cache: true
|
|
39
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
40
|
+
run: uv python install ${{ matrix.python-version }}
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: uv sync --group test --python ${{ matrix.python-version }}
|
|
43
|
+
- name: Run tests
|
|
44
|
+
run: uv run pytest
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
.env
|
|
12
|
+
*.env.*
|
|
13
|
+
!.env.example
|
|
14
|
+
|
|
15
|
+
# Streamlit secrets
|
|
16
|
+
**/.streamlit/secrets.toml
|
|
17
|
+
|
|
18
|
+
# Database
|
|
19
|
+
.sqlite/*.db
|
|
20
|
+
|
|
21
|
+
# Big json files
|
|
22
|
+
full_describe.json
|
|
23
|
+
|
|
24
|
+
# Gitkeep
|
|
25
|
+
|
|
26
|
+
# MkDocs
|
|
27
|
+
site/
|
|
28
|
+
.cache/
|
|
29
|
+
|
|
30
|
+
.vscode/**
|
|
31
|
+
!.vscode/extensions.json
|
|
32
|
+
!.vscode/settings.json
|
|
33
|
+
!.vscode/launch.json
|
|
34
|
+
!project.code-snippets
|
|
35
|
+
.coverage
|
|
36
|
+
|
|
37
|
+
tests/temp/**
|
|
38
|
+
*.db
|
|
39
|
+
|
|
40
|
+
!.gitkeep
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recommendations": [
|
|
3
|
+
"streetsidesoftware.code-spell-checker",
|
|
4
|
+
"streetsidesoftware.code-spell-checker-british-english",
|
|
5
|
+
"esbenp.prettier-vscode",
|
|
6
|
+
"dbaeumer.vscode-eslint",
|
|
7
|
+
"charliermarsh.ruff",
|
|
8
|
+
"ms-python.python",
|
|
9
|
+
"ms-python.vscode-pylance",
|
|
10
|
+
"ms-python.debugpy",
|
|
11
|
+
"njpwerner.autodocstring",
|
|
12
|
+
"usernamehw.errorlens"
|
|
13
|
+
],
|
|
14
|
+
"unwantedRecommendations": ["ms-python.flake8"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Git settings
|
|
3
|
+
"git.autofetch": true,
|
|
4
|
+
// Ruff settings - config lives in pyproject.toml
|
|
5
|
+
"ruff.configuration": "${workspaceFolder}/pyproject.toml",
|
|
6
|
+
// Mypy settings - auto-discovers mypy.ini in workspace root
|
|
7
|
+
"python.analysis.typeCheckingMode": "standard",
|
|
8
|
+
// Python testing settings
|
|
9
|
+
"python.testing.pytestEnabled": true,
|
|
10
|
+
"python.testing.unittestEnabled": false,
|
|
11
|
+
"python.testing.pytestArgs": [
|
|
12
|
+
"tests",
|
|
13
|
+
"--cov",
|
|
14
|
+
"--cov-config=pyproject.toml"
|
|
15
|
+
],
|
|
16
|
+
"[python]": {
|
|
17
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
|
18
|
+
"editor.formatOnSave": true,
|
|
19
|
+
"editor.codeActionsOnSave": {
|
|
20
|
+
"source.organizeImports.ruff": "explicit",
|
|
21
|
+
"source.fixAll.ruff": "explicit"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
// Code formatting
|
|
25
|
+
"editor.formatOnSave": true,
|
|
26
|
+
// Editor settings
|
|
27
|
+
"editor.rulers": [120],
|
|
28
|
+
"files.insertFinalNewline": true,
|
|
29
|
+
"files.trimTrailingWhitespace": true,
|
|
30
|
+
"files.eol": "\n",
|
|
31
|
+
// Codespell settings - explicit workspace paths
|
|
32
|
+
"cSpell.enabled": true,
|
|
33
|
+
"cSpell.language": "en-GB",
|
|
34
|
+
"cSpell.customDictionaries": {
|
|
35
|
+
"project-words": {
|
|
36
|
+
"name": "project-words",
|
|
37
|
+
"path": "${workspaceFolder}/.project-words.txt",
|
|
38
|
+
"addWords": true,
|
|
39
|
+
"scope": "workspace"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
// Error Lens settings
|
|
43
|
+
"errorLens.enabled": true,
|
|
44
|
+
"errorLens.enabledDiagnosticLevels": ["error", "warning", "info", "hint"],
|
|
45
|
+
"errorLens.onSave": true,
|
|
46
|
+
"errorLens.onSaveTimeout": 1000,
|
|
47
|
+
"errorLens.delay": 500,
|
|
48
|
+
"errorLens.followCursor": "allLines"
|
|
49
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
### Miscellaneous
|
|
6
|
+
|
|
7
|
+
- Initial commit ([c96fc0e](https://github.com/Johandielangman/ttl-cache-decorator/commit/c96fc0efa65c289c731b6b8d04358cbd9943eeeb))
|
|
8
|
+
- Add the wheel ([20e5b76](https://github.com/Johandielangman/ttl-cache-decorator/commit/20e5b761ea79e1553682a770e2cbfc51ace33ca0))
|
|
9
|
+
- Add docs dependencies ([23eacf4](https://github.com/Johandielangman/ttl-cache-decorator/commit/23eacf4ebc5f9bcd78e7aac6a727ba54f041bda3))
|
|
10
|
+
- Use a cliff uses ([3e1c78a](https://github.com/Johandielangman/ttl-cache-decorator/commit/3e1c78a6dd6a14053e3f0b0285cc1383fc253806))
|
|
11
|
+
- Release v0.1.1 ([91408a2](https://github.com/Johandielangman/ttl-cache-decorator/commit/91408a2770190351a4996c1f996a415e42d3f463))
|
|
12
|
+
- Better tagging ([4b45da8](https://github.com/Johandielangman/ttl-cache-decorator/commit/4b45da8adeb4545a7a3bd04f600f615198a4f7d9))
|
|
13
|
+
<!-- generated by git-cliff -->
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Johandielangman <jghanekom2@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
6
|
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
12
|
+
portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
15
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
16
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
18
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ttl-cache-decorator
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: LRU Cache Decorator with a Time to Live
|
|
5
|
+
Project-URL: Changelog, https://github.com/Johandielangman/ttl-cache-decorator/blob/main/CHANGELOG.md
|
|
6
|
+
Project-URL: Documentation, https://johandielangman.github.io/ttl-cache-decorator/
|
|
7
|
+
Project-URL: Issues, https://github.com/Johandielangman/ttl-cache-decorator/issues
|
|
8
|
+
Project-URL: Source, https://github.com/Johandielangman/ttl-cache-decorator
|
|
9
|
+
Author-email: Johandielangman <johan@datatreehouse.org>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE.txt
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# TTL Cache
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/ttl-cache-decorator)
|
|
29
|
+
[](https://pypi.org/project/ttl-cache-decorator)
|
|
30
|
+
|
|
31
|
+
An LRU cache decorator with a time to live — like `functools.lru_cache`, but entries expire after a configurable number of seconds.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```console
|
|
36
|
+
pip install ttl-cache-decorator
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from ttl_cache import ttl_lru_cache
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@ttl_lru_cache(ttl=30)
|
|
46
|
+
def fetch_user(user_id: int) -> dict:
|
|
47
|
+
return {"id": user_id, "name": "Ada"}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
fetch_user(1) # Calls the function
|
|
51
|
+
fetch_user(1) # Cache hit
|
|
52
|
+
# ... 30 seconds later ...
|
|
53
|
+
fetch_user(1) # Expired — calls the function again
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
`ttl-cache-decorator` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# TTL Cache
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/ttl-cache-decorator)
|
|
4
|
+
[](https://pypi.org/project/ttl-cache-decorator)
|
|
5
|
+
|
|
6
|
+
An LRU cache decorator with a time to live — like `functools.lru_cache`, but entries expire after a configurable number of seconds.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```console
|
|
11
|
+
pip install ttl-cache-decorator
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from ttl_cache import ttl_lru_cache
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@ttl_lru_cache(ttl=30)
|
|
21
|
+
def fetch_user(user_id: int) -> dict:
|
|
22
|
+
return {"id": user_id, "name": "Ada"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
fetch_user(1) # Calls the function
|
|
26
|
+
fetch_user(1) # Cache hit
|
|
27
|
+
# ... 30 seconds later ...
|
|
28
|
+
fetch_user(1) # Expired — calls the function again
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## License
|
|
32
|
+
|
|
33
|
+
`ttl-cache-decorator` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# git-cliff ~ changelog configuration
|
|
2
|
+
# https://git-cliff.org/docs/configuration
|
|
3
|
+
|
|
4
|
+
[changelog]
|
|
5
|
+
header = """
|
|
6
|
+
# Changelog
|
|
7
|
+
|
|
8
|
+
All notable changes to this project are documented in this file.
|
|
9
|
+
"""
|
|
10
|
+
body = """
|
|
11
|
+
{% for group, commits in commits | group_by(attribute="group") %}
|
|
12
|
+
### {{ group | upper_first }}
|
|
13
|
+
{% for commit in commits %}
|
|
14
|
+
- {{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}](https://github.com/Johandielangman/ttl-cache-decorator/commit/{{ commit.id }}))\
|
|
15
|
+
{% endfor %}
|
|
16
|
+
{% endfor %}
|
|
17
|
+
"""
|
|
18
|
+
footer = """
|
|
19
|
+
<!-- generated by git-cliff -->
|
|
20
|
+
"""
|
|
21
|
+
trim = true
|
|
22
|
+
|
|
23
|
+
[git]
|
|
24
|
+
conventional_commits = true
|
|
25
|
+
filter_unconventional = true
|
|
26
|
+
commit_parsers = [
|
|
27
|
+
{ message = "^feat", group = "Features" },
|
|
28
|
+
{ message = "^fix", group = "Bug Fixes" },
|
|
29
|
+
{ message = "^perf", group = "Performance" },
|
|
30
|
+
{ message = "^refactor", group = "Refactoring" },
|
|
31
|
+
{ message = "^docs", group = "Documentation" },
|
|
32
|
+
{ message = "^test", group = "Testing" },
|
|
33
|
+
{ message = "^chore\\(release\\)", skip = true },
|
|
34
|
+
{ message = "^chore", group = "Miscellaneous" },
|
|
35
|
+
]
|
|
36
|
+
filter_commits = false
|
|
37
|
+
tag_pattern = "^v[0-9]+\\.[0-9]+\\.[0-9]+$"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/timer
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# TTL Cache
|
|
6
|
+
|
|
7
|
+
An LRU cache decorator with a ==time to live==. `ttl_lru_cache` works exactly
|
|
8
|
+
like [`functools.lru_cache`][lru_cache], except entries automatically expire
|
|
9
|
+
after a configurable number of seconds.
|
|
10
|
+
|
|
11
|
+
[lru_cache]: https://docs.python.org/3/library/functools.html#functools.lru_cache
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
=== "pip"
|
|
16
|
+
|
|
17
|
+
``` console
|
|
18
|
+
pip install ttl-cache-decorator
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
=== "uv"
|
|
22
|
+
|
|
23
|
+
``` console
|
|
24
|
+
uv add ttl-cache-decorator
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Requires **Python 3.12+**.
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
``` python title="Basic usage" hl_lines="1 4"
|
|
32
|
+
from ttl_cache import ttl_lru_cache
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@ttl_lru_cache(ttl=30) # (1)!
|
|
36
|
+
def fetch_user(user_id: int) -> dict:
|
|
37
|
+
print("Fetching from the network...")
|
|
38
|
+
return {"id": user_id, "name": "Ada"}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
fetch_user(1) # Prints "Fetching from the network..."
|
|
42
|
+
fetch_user(1) # Cache hit, nothing printed
|
|
43
|
+
# ... 30 seconds later ...
|
|
44
|
+
fetch_user(1) # Expired — fetches again
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
1. Cache results for 30 seconds. While the TTL window is open, repeat calls
|
|
48
|
+
with the same arguments are served from the cache.
|
|
49
|
+
|
|
50
|
+
## How it works
|
|
51
|
+
|
|
52
|
+
Under the hood it still uses `lru_cache`, but the cached call receives an extra
|
|
53
|
+
positional-only *time bucket* argument derived from a monotonic clock:
|
|
54
|
+
|
|
55
|
+
``` python title="The time bucket"
|
|
56
|
+
time_bucket = int((time.monotonic() + offset) // ttl)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
An entry stays "alive" while the bucket value is unchanged and expires when the
|
|
60
|
+
bucket rolls over:
|
|
61
|
+
|
|
62
|
+
``` mermaid
|
|
63
|
+
graph LR
|
|
64
|
+
A[Call] --> B{Bucket changed?}
|
|
65
|
+
B -->|No| C[Serve cached value]
|
|
66
|
+
B -->|Yes| D[Recompute and cache]
|
|
67
|
+
C --> E[Return]
|
|
68
|
+
D --> E
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
!!! note
|
|
72
|
+
|
|
73
|
+
The bucket boundary is shared per TTL window, so an entry lives for *at
|
|
74
|
+
most* `ttl` seconds and *at least* the remainder of the current bucket.
|
|
75
|
+
|
|
76
|
+
## API reference
|
|
77
|
+
|
|
78
|
+
### `ttl_lru_cache`
|
|
79
|
+
|
|
80
|
+
``` python
|
|
81
|
+
ttl_lru_cache(
|
|
82
|
+
ttl: float = 60,
|
|
83
|
+
jitter: float = 0,
|
|
84
|
+
maxsize: int | None = 128,
|
|
85
|
+
typed: bool = False,
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Parameter | Type | Default | Description |
|
|
90
|
+
|---|---|---|---|
|
|
91
|
+
| `ttl` | `float` | `60` | Time to live in seconds. Must be positive. |
|
|
92
|
+
| `jitter` | `float` | `0` | Max random offset (seconds) applied to bucket boundaries. Must be non-negative. |
|
|
93
|
+
| `maxsize` | `int \| None` | `128` | Max size of the cache, or `None` for unbounded. |
|
|
94
|
+
| `typed` | `bool` | `False` | Whether argument types are part of the cache key. |
|
|
95
|
+
|
|
96
|
+
Returns a decorator whose wrapped function keeps its signature and exposes the
|
|
97
|
+
usual `lru_cache` helpers: `cache_info()` and `cache_clear()`.
|
|
98
|
+
|
|
99
|
+
!!! warning "Validation"
|
|
100
|
+
|
|
101
|
+
A `ValueError` is raised at decoration time if `ttl` is not positive or
|
|
102
|
+
`jitter` is negative.
|
|
103
|
+
|
|
104
|
+
### Jitter
|
|
105
|
+
|
|
106
|
+
`jitter` draws a random offset once per decorated function from
|
|
107
|
+
`random.uniform(0, jitter)`, so different functions (or processes) expire at
|
|
108
|
+
different moments instead of all at once. Use it to avoid a *thundering herd*
|
|
109
|
+
when many cached values would otherwise expire simultaneously:
|
|
110
|
+
|
|
111
|
+
``` python title="Adding jitter" hl_lines="1"
|
|
112
|
+
@ttl_lru_cache(ttl=60, jitter=10)
|
|
113
|
+
def fetch_metrics(service: str) -> dict:
|
|
114
|
+
...
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Cache helpers
|
|
118
|
+
|
|
119
|
+
``` python title="Inspecting and clearing the cache"
|
|
120
|
+
fetch_user.cache_info() # CacheInfo(hits=..., misses=..., maxsize=128, currsize=...)
|
|
121
|
+
fetch_user.cache_clear() # Drop all cached entries
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
??? info "Typing"
|
|
125
|
+
|
|
126
|
+
The decorator is fully generic via `ParamSpec` and returns a
|
|
127
|
+
`TTLCachedFunction` protocol, so type checkers preserve the wrapped
|
|
128
|
+
function's signature — including keyword arguments.
|
|
129
|
+
|
|
130
|
+
``` python
|
|
131
|
+
from ttl_cache import TTLCachedFunction
|
|
132
|
+
|
|
133
|
+
cached_fetch: TTLCachedFunction[[str], dict] = ttl_lru_cache(ttl=5)(fetch_user)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
`ttl-cache-decorator` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|