fastapi-deprecation 0.2.1__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.
- fastapi_deprecation-0.2.1/.github/workflows/publish.yml +28 -0
- fastapi_deprecation-0.2.1/.github/workflows/test.yml +34 -0
- fastapi_deprecation-0.2.1/.gitignore +12 -0
- fastapi_deprecation-0.2.1/.pre-commit-config.yaml +22 -0
- fastapi_deprecation-0.2.1/.python-version +1 -0
- fastapi_deprecation-0.2.1/LICENSE +21 -0
- fastapi_deprecation-0.2.1/PKG-INFO +184 -0
- fastapi_deprecation-0.2.1/README.md +151 -0
- fastapi_deprecation-0.2.1/docs/index.md +138 -0
- fastapi_deprecation-0.2.1/docs/reference/core.md +30 -0
- fastapi_deprecation-0.2.1/docs/reference/dependencies.md +41 -0
- fastapi_deprecation-0.2.1/docs/reference/openapi.md +34 -0
- fastapi_deprecation-0.2.1/docs/reference/utils.md +20 -0
- fastapi_deprecation-0.2.1/mkdocs.yml +56 -0
- fastapi_deprecation-0.2.1/pyproject.toml +35 -0
- fastapi_deprecation-0.2.1/src/fastapi_deprecation/__init__.py +15 -0
- fastapi_deprecation-0.2.1/src/fastapi_deprecation/_version.py +34 -0
- fastapi_deprecation-0.2.1/src/fastapi_deprecation/core.py +109 -0
- fastapi_deprecation-0.2.1/src/fastapi_deprecation/dependencies.py +111 -0
- fastapi_deprecation-0.2.1/src/fastapi_deprecation/openapi.py +47 -0
- fastapi_deprecation-0.2.1/src/fastapi_deprecation/py.typed +0 -0
- fastapi_deprecation-0.2.1/src/fastapi_deprecation/utils.py +59 -0
- fastapi_deprecation-0.2.1/tests/test_brownouts.py +71 -0
- fastapi_deprecation-0.2.1/tests/test_core.py +92 -0
- fastapi_deprecation-0.2.1/tests/test_extended.py +56 -0
- fastapi_deprecation-0.2.1/tests/test_openapi.py +48 -0
- fastapi_deprecation-0.2.1/tests/test_recursive_openapi.py +48 -0
- fastapi_deprecation-0.2.1/tests/test_utils.py +46 -0
- fastapi_deprecation-0.2.1/uv.lock +1135 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-n-publish:
|
|
10
|
+
name: Build and Publish
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment:
|
|
13
|
+
name: pypi
|
|
14
|
+
url: https://pypi.org/p/fastapi-deprecation
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
|
|
24
|
+
- name: Build package
|
|
25
|
+
run: uv build
|
|
26
|
+
|
|
27
|
+
- name: Publish distribution 📦 to PyPI
|
|
28
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
8
|
+
types: [opener, synchronize, reopened]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
name: Test
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
|
|
24
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
25
|
+
run: uv python install ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install the project
|
|
28
|
+
run: uv sync --all-extras --dev
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: uv run pytest tests
|
|
32
|
+
|
|
33
|
+
- name: Lint with Ruff
|
|
34
|
+
run: uv run ruff check .
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.5.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
|
|
10
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
11
|
+
rev: v0.1.6
|
|
12
|
+
hooks:
|
|
13
|
+
- id: ruff
|
|
14
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
15
|
+
- id: ruff-format
|
|
16
|
+
|
|
17
|
+
- repo: https://github.com/compilerla/conventional-pre-commit
|
|
18
|
+
rev: v3.2.0
|
|
19
|
+
hooks:
|
|
20
|
+
- id: conventional-pre-commit
|
|
21
|
+
stages: [commit-msg]
|
|
22
|
+
args: []
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fractal Vision
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-deprecation
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Endpoint deprecation management for FastAPI made easy
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 Fractal Vision
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Requires-Dist: fastapi>=0.129.0
|
|
29
|
+
Requires-Dist: pydantic>=2.12.5
|
|
30
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
31
|
+
Requires-Dist: typing-extensions>=4.15.0
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# FastAPI Deprecation
|
|
35
|
+
|
|
36
|
+
<div align="center">
|
|
37
|
+
<p align="center">
|
|
38
|
+
<strong>RFC 9745 compliant API deprecation for FastAPI.</strong>
|
|
39
|
+
</p>
|
|
40
|
+
<p align="center">
|
|
41
|
+
<a href="https://github.com/fractalvision/fastapi-deprecation/actions/workflows/test.yml">
|
|
42
|
+
<img src="https://github.com/fractalvision/fastapi-deprecation/actions/workflows/test.yml/badge.svg" alt="Test Status"/>
|
|
43
|
+
</a>
|
|
44
|
+
</p>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
**FastAPI Deprecation** helps you manage the lifecycle of your API endpoints using standard HTTP headers (`Deprecation`, `Sunset`, `Link`) and automated blocking logic. It allows you to gracefully warn clients about upcoming deprecations and automatically shut down endpoints when they reach their sunset date.
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- **Standard Compliance**: Fully implements [RFC 9745](https://datatracker.ietf.org/doc/rfc9745/) and [RFC 8594](https://datatracker.ietf.org/doc/rfc8594/).
|
|
54
|
+
- **Decorator-based**: Simple `@deprecated` decorator for your path operations.
|
|
55
|
+
- **Automated Blocking**: Automatically returns `410 Gone` or `301 Moved Permanently` after the `sunset_date`.
|
|
56
|
+
- **OpenAPI Integration**: Automatically marks endpoints as deprecated in Swagger UI and ReDoc.
|
|
57
|
+
- **Router Support**: Deprecate entire routers or sub-applications with `DeprecationDependency`.
|
|
58
|
+
- **Extended Features**:
|
|
59
|
+
- **Telemetry**: Track usage of deprecated endpoints.
|
|
60
|
+
- **Brownouts**: Schedule temporary shutdowns to simulate future removal.
|
|
61
|
+
- **Future Deprecation**: Announce upcoming deprecations before they become active.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install fastapi-deprecation
|
|
67
|
+
# or with uv
|
|
68
|
+
uv add fastapi-deprecation
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
To run the documentation locally:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
uv run zensical serve
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from fastapi import FastAPI
|
|
83
|
+
from fastapi_deprecation import deprecated, auto_deprecate_openapi
|
|
84
|
+
|
|
85
|
+
app = FastAPI()
|
|
86
|
+
|
|
87
|
+
@app.get("/old-endpoint")
|
|
88
|
+
@deprecated(
|
|
89
|
+
deprecation_date="2024-01-01",
|
|
90
|
+
sunset_date="2025-01-01",
|
|
91
|
+
alternative="/new-endpoint",
|
|
92
|
+
detail="This endpoint is old and tired."
|
|
93
|
+
)
|
|
94
|
+
async def old():
|
|
95
|
+
return {"message": "Enjoy it while it lasts!"}
|
|
96
|
+
|
|
97
|
+
# Don't forget to update the schema at the end!
|
|
98
|
+
auto_deprecate_openapi(app)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## How It Works
|
|
102
|
+
|
|
103
|
+
1. **Warning Phase** (Before Sunset):
|
|
104
|
+
* Requests return `200 OK`.
|
|
105
|
+
* Response headers include:
|
|
106
|
+
* `Deprecation: @1704067200` (Unix timestamp of `deprecation_date`)
|
|
107
|
+
* `Sunset: Wed, 01 Jan 2025 00:00:00 GMT`
|
|
108
|
+
* `Link: </new-endpoint>; rel="alternative"`
|
|
109
|
+
|
|
110
|
+
2. **Blocking Phase** (After Sunset):
|
|
111
|
+
* Requests return `410 Gone` (or `301 Moved Permanently` if `alternative` is set).
|
|
112
|
+
* The `detail` message is returned in the response body.
|
|
113
|
+
|
|
114
|
+
## Advanced Usage
|
|
115
|
+
|
|
116
|
+
### 1. Brownouts (Scheduled Unavailability)
|
|
117
|
+
You can simulate future shutdowns by scheduling "brownouts" — temporary periods where the endpoint returns `410 Gone` (or `301` if alternative is set). This forces clients to notice the deprecation before the final sunset.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
@deprecated(
|
|
121
|
+
sunset_date="2025-12-31",
|
|
122
|
+
brownouts=[
|
|
123
|
+
# 1-hour brownout
|
|
124
|
+
("2025-11-01T09:00:00Z", "2025-11-01T10:00:00Z"),
|
|
125
|
+
# 1-day brownout
|
|
126
|
+
("2025-12-01T00:00:00Z", "2025-12-02T00:00:00Z"),
|
|
127
|
+
],
|
|
128
|
+
detail="Service is temporarily unavailable due to scheduled brownout."
|
|
129
|
+
)
|
|
130
|
+
async def my_endpoint(): ...
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 2. Telemetry & Logging
|
|
134
|
+
Track usage of deprecated endpoints using a global callback. This is useful for monitoring which clients are still using old APIs.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
import logging
|
|
138
|
+
from fastapi import Request, Response
|
|
139
|
+
from fastapi_deprecation import set_deprecation_callback, DeprecationDependency
|
|
140
|
+
|
|
141
|
+
logger = logging.getLogger("deprecation")
|
|
142
|
+
|
|
143
|
+
def log_usage(request: Request, response: Response, dep: DeprecationDependency):
|
|
144
|
+
logger.warning(
|
|
145
|
+
f"Deprecated endpoint {request.url} accessed. "
|
|
146
|
+
f"Deprecation date: {dep.deprecation_date}"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
set_deprecation_callback(log_usage)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 3. Deprecating Entire Routers
|
|
153
|
+
To deprecate a whole group of endpoints, use `DeprecationDependency` on the `APIRouter`.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from fastapi import APIRouter, Depends
|
|
157
|
+
from fastapi_deprecation import DeprecationDependency
|
|
158
|
+
|
|
159
|
+
router = APIRouter(
|
|
160
|
+
dependencies=[Depends(DeprecationDependency(deprecation_date="2024-01-01"))]
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
@router.get("/sub-route")
|
|
164
|
+
async def sub(): ...
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 4. Recursive OpenAPI Support
|
|
168
|
+
When using `auto_deprecate_openapi(app)`, it automatically traverses potentially mounted sub-applications (`app.mount(...)`) and marks their routes as deprecated if configured.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
root_app.mount("/v1", v1_app)
|
|
172
|
+
# This will update OpenAPI for both root_app AND v1_app
|
|
173
|
+
auto_deprecate_openapi(root_app)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 5. Future Deprecation
|
|
177
|
+
You can announce a *future* deprecation date. The `Deprecation` header will still be sent (as per RFC 9745), allowing clients to prepare in advance.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
@deprecated(deprecation_date="2030-01-01")
|
|
181
|
+
async def future_proof(): ...
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
See the [Documentation](https://example.com/docs) for full details on API reference and advanced configuration.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# FastAPI Deprecation
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<p align="center">
|
|
5
|
+
<strong>RFC 9745 compliant API deprecation for FastAPI.</strong>
|
|
6
|
+
</p>
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/fractalvision/fastapi-deprecation/actions/workflows/test.yml">
|
|
9
|
+
<img src="https://github.com/fractalvision/fastapi-deprecation/actions/workflows/test.yml/badge.svg" alt="Test Status"/>
|
|
10
|
+
</a>
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
**FastAPI Deprecation** helps you manage the lifecycle of your API endpoints using standard HTTP headers (`Deprecation`, `Sunset`, `Link`) and automated blocking logic. It allows you to gracefully warn clients about upcoming deprecations and automatically shut down endpoints when they reach their sunset date.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Standard Compliance**: Fully implements [RFC 9745](https://datatracker.ietf.org/doc/rfc9745/) and [RFC 8594](https://datatracker.ietf.org/doc/rfc8594/).
|
|
21
|
+
- **Decorator-based**: Simple `@deprecated` decorator for your path operations.
|
|
22
|
+
- **Automated Blocking**: Automatically returns `410 Gone` or `301 Moved Permanently` after the `sunset_date`.
|
|
23
|
+
- **OpenAPI Integration**: Automatically marks endpoints as deprecated in Swagger UI and ReDoc.
|
|
24
|
+
- **Router Support**: Deprecate entire routers or sub-applications with `DeprecationDependency`.
|
|
25
|
+
- **Extended Features**:
|
|
26
|
+
- **Telemetry**: Track usage of deprecated endpoints.
|
|
27
|
+
- **Brownouts**: Schedule temporary shutdowns to simulate future removal.
|
|
28
|
+
- **Future Deprecation**: Announce upcoming deprecations before they become active.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install fastapi-deprecation
|
|
34
|
+
# or with uv
|
|
35
|
+
uv add fastapi-deprecation
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Documentation
|
|
39
|
+
|
|
40
|
+
To run the documentation locally:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv run zensical serve
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from fastapi import FastAPI
|
|
50
|
+
from fastapi_deprecation import deprecated, auto_deprecate_openapi
|
|
51
|
+
|
|
52
|
+
app = FastAPI()
|
|
53
|
+
|
|
54
|
+
@app.get("/old-endpoint")
|
|
55
|
+
@deprecated(
|
|
56
|
+
deprecation_date="2024-01-01",
|
|
57
|
+
sunset_date="2025-01-01",
|
|
58
|
+
alternative="/new-endpoint",
|
|
59
|
+
detail="This endpoint is old and tired."
|
|
60
|
+
)
|
|
61
|
+
async def old():
|
|
62
|
+
return {"message": "Enjoy it while it lasts!"}
|
|
63
|
+
|
|
64
|
+
# Don't forget to update the schema at the end!
|
|
65
|
+
auto_deprecate_openapi(app)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## How It Works
|
|
69
|
+
|
|
70
|
+
1. **Warning Phase** (Before Sunset):
|
|
71
|
+
* Requests return `200 OK`.
|
|
72
|
+
* Response headers include:
|
|
73
|
+
* `Deprecation: @1704067200` (Unix timestamp of `deprecation_date`)
|
|
74
|
+
* `Sunset: Wed, 01 Jan 2025 00:00:00 GMT`
|
|
75
|
+
* `Link: </new-endpoint>; rel="alternative"`
|
|
76
|
+
|
|
77
|
+
2. **Blocking Phase** (After Sunset):
|
|
78
|
+
* Requests return `410 Gone` (or `301 Moved Permanently` if `alternative` is set).
|
|
79
|
+
* The `detail` message is returned in the response body.
|
|
80
|
+
|
|
81
|
+
## Advanced Usage
|
|
82
|
+
|
|
83
|
+
### 1. Brownouts (Scheduled Unavailability)
|
|
84
|
+
You can simulate future shutdowns by scheduling "brownouts" — temporary periods where the endpoint returns `410 Gone` (or `301` if alternative is set). This forces clients to notice the deprecation before the final sunset.
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
@deprecated(
|
|
88
|
+
sunset_date="2025-12-31",
|
|
89
|
+
brownouts=[
|
|
90
|
+
# 1-hour brownout
|
|
91
|
+
("2025-11-01T09:00:00Z", "2025-11-01T10:00:00Z"),
|
|
92
|
+
# 1-day brownout
|
|
93
|
+
("2025-12-01T00:00:00Z", "2025-12-02T00:00:00Z"),
|
|
94
|
+
],
|
|
95
|
+
detail="Service is temporarily unavailable due to scheduled brownout."
|
|
96
|
+
)
|
|
97
|
+
async def my_endpoint(): ...
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 2. Telemetry & Logging
|
|
101
|
+
Track usage of deprecated endpoints using a global callback. This is useful for monitoring which clients are still using old APIs.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import logging
|
|
105
|
+
from fastapi import Request, Response
|
|
106
|
+
from fastapi_deprecation import set_deprecation_callback, DeprecationDependency
|
|
107
|
+
|
|
108
|
+
logger = logging.getLogger("deprecation")
|
|
109
|
+
|
|
110
|
+
def log_usage(request: Request, response: Response, dep: DeprecationDependency):
|
|
111
|
+
logger.warning(
|
|
112
|
+
f"Deprecated endpoint {request.url} accessed. "
|
|
113
|
+
f"Deprecation date: {dep.deprecation_date}"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
set_deprecation_callback(log_usage)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3. Deprecating Entire Routers
|
|
120
|
+
To deprecate a whole group of endpoints, use `DeprecationDependency` on the `APIRouter`.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from fastapi import APIRouter, Depends
|
|
124
|
+
from fastapi_deprecation import DeprecationDependency
|
|
125
|
+
|
|
126
|
+
router = APIRouter(
|
|
127
|
+
dependencies=[Depends(DeprecationDependency(deprecation_date="2024-01-01"))]
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
@router.get("/sub-route")
|
|
131
|
+
async def sub(): ...
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 4. Recursive OpenAPI Support
|
|
135
|
+
When using `auto_deprecate_openapi(app)`, it automatically traverses potentially mounted sub-applications (`app.mount(...)`) and marks their routes as deprecated if configured.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
root_app.mount("/v1", v1_app)
|
|
139
|
+
# This will update OpenAPI for both root_app AND v1_app
|
|
140
|
+
auto_deprecate_openapi(root_app)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 5. Future Deprecation
|
|
144
|
+
You can announce a *future* deprecation date. The `Deprecation` header will still be sent (as per RFC 9745), allowing clients to prepare in advance.
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
@deprecated(deprecation_date="2030-01-01")
|
|
148
|
+
async def future_proof(): ...
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
See the [Documentation](https://example.com/docs) for full details on API reference and advanced configuration.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# FastAPI Deprecation
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<p align="center">
|
|
5
|
+
<strong>RFC 9745 compliant API deprecation for FastAPI.</strong>
|
|
6
|
+
</p>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
**FastAPI Deprecation** helps you manage the lifecycle of your API endpoints using standard HTTP headers (`Deprecation`, `Sunset`, `Link`) and automated blocking logic. It allows you to gracefully warn clients about upcoming deprecations and automatically shut down endpoints when they reach their sunset date.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Standard Compliance**: Fully implements [RFC 9745](https://datatracker.ietf.org/doc/rfc9745/) and [RFC 8594](https://datatracker.ietf.org/doc/rfc8594/).
|
|
16
|
+
- **Decorator-based**: Simple `@deprecated` decorator for your path operations.
|
|
17
|
+
- **Automated Blocking**: Automatically returns `410 Gone` or `301 Moved Permanently` after the `sunset_date`.
|
|
18
|
+
- **OpenAPI Integration**: Automatically marks endpoints as deprecated in Swagger UI and ReDoc.
|
|
19
|
+
- **Router Support**: Deprecate entire routers or sub-applications with `DeprecationDependency`.
|
|
20
|
+
- **Extended Features**:
|
|
21
|
+
- **Telemetry**: Track usage of deprecated endpoints.
|
|
22
|
+
- **Brownouts**: Schedule temporary shutdowns to simulate future removal.
|
|
23
|
+
- **Future Deprecation**: Announce upcoming deprecations before they become active.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install fastapi-deprecation
|
|
29
|
+
# or with uv
|
|
30
|
+
uv add fastapi-deprecation
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from fastapi import FastAPI
|
|
37
|
+
from fastapi_deprecation import deprecated, auto_deprecate_openapi
|
|
38
|
+
|
|
39
|
+
app = FastAPI()
|
|
40
|
+
|
|
41
|
+
@app.get("/old-endpoint")
|
|
42
|
+
@deprecated(
|
|
43
|
+
deprecation_date="2024-01-01",
|
|
44
|
+
sunset_date="2025-01-01",
|
|
45
|
+
alternative="/new-endpoint",
|
|
46
|
+
detail="This endpoint is old and tired."
|
|
47
|
+
)
|
|
48
|
+
async def old():
|
|
49
|
+
return {"message": "Enjoy it while it lasts!"}
|
|
50
|
+
|
|
51
|
+
# Don't forget to update the schema at the end!
|
|
52
|
+
auto_deprecate_openapi(app)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## How It Works
|
|
56
|
+
|
|
57
|
+
1. **Warning Phase** (Before Sunset):
|
|
58
|
+
* Requests return `200 OK`.
|
|
59
|
+
* Response headers include:
|
|
60
|
+
* `Deprecation: @1704067200` (Unix timestamp of `deprecation_date`)
|
|
61
|
+
* `Sunset: Wed, 01 Jan 2025 00:00:00 GMT`
|
|
62
|
+
* `Link: </new-endpoint>; rel="alternative"`
|
|
63
|
+
|
|
64
|
+
2. **Blocking Phase** (After Sunset):
|
|
65
|
+
* Requests return `410 Gone` (or `301 Moved Permanently` if `alternative` is set).
|
|
66
|
+
* The `detail` message is returned in the response body.
|
|
67
|
+
|
|
68
|
+
## Advanced Usage
|
|
69
|
+
|
|
70
|
+
### 1. Brownouts (Scheduled Unavailability)
|
|
71
|
+
You can simulate future shutdowns by scheduling "brownouts" — temporary periods where the endpoint returns `410 Gone` (or `301` if alternative is set). This forces clients to notice the deprecation before the final sunset.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
@deprecated(
|
|
75
|
+
sunset_date="2025-12-31",
|
|
76
|
+
brownouts=[
|
|
77
|
+
# 1-hour brownout
|
|
78
|
+
("2025-11-01T09:00:00Z", "2025-11-01T10:00:00Z"),
|
|
79
|
+
# 1-day brownout
|
|
80
|
+
("2025-12-01T00:00:00Z", "2025-12-02T00:00:00Z"),
|
|
81
|
+
],
|
|
82
|
+
detail="Service is temporarily unavailable due to scheduled brownout."
|
|
83
|
+
)
|
|
84
|
+
async def my_endpoint(): ...
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Telemetry & Logging
|
|
88
|
+
Track usage of deprecated endpoints using a global callback. This is useful for monitoring which clients are still using old APIs.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import logging
|
|
92
|
+
from fastapi import Request, Response
|
|
93
|
+
from fastapi_deprecation import set_deprecation_callback, DeprecationDependency
|
|
94
|
+
|
|
95
|
+
logger = logging.getLogger("deprecation")
|
|
96
|
+
|
|
97
|
+
def log_usage(request: Request, response: Response, dep: DeprecationDependency):
|
|
98
|
+
logger.warning(
|
|
99
|
+
f"Deprecated endpoint {request.url} accessed. "
|
|
100
|
+
f"Deprecation date: {dep.deprecation_date}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
set_deprecation_callback(log_usage)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 3. Deprecating Entire Routers
|
|
107
|
+
To deprecate a whole group of endpoints, use `DeprecationDependency` on the `APIRouter`.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from fastapi import APIRouter, Depends
|
|
111
|
+
from fastapi_deprecation import DeprecationDependency
|
|
112
|
+
|
|
113
|
+
router = APIRouter(
|
|
114
|
+
dependencies=[Depends(DeprecationDependency(deprecation_date="2024-01-01"))]
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
@router.get("/sub-route")
|
|
118
|
+
async def sub(): ...
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 4. Recursive OpenAPI Support
|
|
122
|
+
When using `auto_deprecate_openapi(app)`, it automatically traverses potentially mounted sub-applications (`app.mount(...)`) and marks their routes as deprecated if configured.
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
root_app.mount("/v1", v1_app)
|
|
126
|
+
# This will update OpenAPI for both root_app AND v1_app
|
|
127
|
+
auto_deprecate_openapi(root_app)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 5. Future Deprecation
|
|
131
|
+
You can announce a *future* deprecation date. The `Deprecation` header will still be sent (as per RFC 9745), allowing clients to prepare in advance.
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
@deprecated(deprecation_date="2030-01-01")
|
|
135
|
+
async def future_proof(): ...
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Check the [API Reference](reference/core.md) for full details.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Core API
|
|
2
|
+
|
|
3
|
+
The `core` module provides the primary interface for deprecating endpoints in FastAPI: the `@deprecated` decorator.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
The `@deprecated` decorator should be placed *above* your path operation function but *below* the FastAPI router decorator (though order usually doesn't matter for functionality, it keeps the code clean).
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from fastapi import FastAPI
|
|
11
|
+
from fastapi_deprecation import deprecated
|
|
12
|
+
|
|
13
|
+
app = FastAPI()
|
|
14
|
+
|
|
15
|
+
@app.get("/items")
|
|
16
|
+
@deprecated(
|
|
17
|
+
deprecation_date="2024-01-01",
|
|
18
|
+
sunset_date="2025-01-01",
|
|
19
|
+
alternative="/new_items"
|
|
20
|
+
)
|
|
21
|
+
async def read_items():
|
|
22
|
+
return [{"item_id": "Foo"}]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Reference
|
|
26
|
+
|
|
27
|
+
::: fastapi_deprecation.core
|
|
28
|
+
options:
|
|
29
|
+
show_root_heading: true
|
|
30
|
+
show_source: true
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Dependencies API
|
|
2
|
+
|
|
3
|
+
The `dependencies` module contains the underlying logic for handling deprecation lifecycles. It allows for more advanced usage patterns, such as applying deprecation to entire routers or registering global callbacks.
|
|
4
|
+
|
|
5
|
+
## Router Deprecation
|
|
6
|
+
|
|
7
|
+
You can deprecate an entire `APIRouter` by adding `DeprecationDependency` to its dependencies list.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from fastapi import APIRouter, Depends
|
|
11
|
+
from fastapi_deprecation import DeprecationDependency
|
|
12
|
+
|
|
13
|
+
router = APIRouter(
|
|
14
|
+
dependencies=[
|
|
15
|
+
Depends(DeprecationDependency(deprecation_date="2024-06-01"))
|
|
16
|
+
]
|
|
17
|
+
)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Telemetry Callbacks
|
|
21
|
+
|
|
22
|
+
You can register a callback to be notified whenever a deprecated endpoint is accessed. This is useful for logging, analytics, or alerting.
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import logging
|
|
26
|
+
from fastapi_deprecation import set_deprecation_callback
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("deprecation")
|
|
29
|
+
|
|
30
|
+
def on_deprecation(request, response, dep):
|
|
31
|
+
logger.warning(f"Deprecated usage: {request.url}")
|
|
32
|
+
|
|
33
|
+
set_deprecation_callback(on_deprecation)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Reference
|
|
37
|
+
|
|
38
|
+
::: fastapi_deprecation.dependencies
|
|
39
|
+
options:
|
|
40
|
+
show_root_heading: true
|
|
41
|
+
show_source: true
|