fastapi-singleton 0.1.0__tar.gz → 0.2.0__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_singleton-0.1.0 → fastapi_singleton-0.2.0}/PKG-INFO +7 -22
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/README.md +5 -20
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/pyproject.toml +2 -2
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/_function.py +12 -13
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/_signature.py +9 -5
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/__init__.py +0 -0
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/_class.py +0 -0
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/_hooks.py +0 -0
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/_lifespan.py +0 -0
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/_provider.py +0 -0
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/_registry.py +0 -0
- {fastapi_singleton-0.1.0 → fastapi_singleton-0.2.0}/src/fastapi_singleton/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-singleton
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Application-scoped dependencies for FastAPI
|
|
5
5
|
Author: Alex Ward
|
|
6
6
|
Author-email: Alex Ward <alxwrd@googlemail.com>
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
15
|
Classifier: Framework :: FastAPI
|
|
16
16
|
Requires-Dist: fastapi>=0.115
|
|
17
|
-
Requires-Python: >=3.
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
18
|
Project-URL: Repository, https://github.com/alxwrd/fastapi-singleton
|
|
19
19
|
Project-URL: Releases, https://github.com/alxwrd/fastapi-singleton/releases
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
@@ -24,23 +24,16 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
<p align="center"><i>
|
|
25
25
|
Application-scoped dependencies for <code>fastapi</code>
|
|
26
26
|
</i></p>
|
|
27
|
-
<img width="256px" src=".github/assets/three-card-trickster-768.png">
|
|
27
|
+
<img width="256px" src="https://github.com/alxwrd/fastapi-singleton/raw/main/.github/assets/three-card-trickster-768.png">
|
|
28
28
|
<div align="center">
|
|
29
29
|
<a href="https://github.com/alxwrd/fastapi-singleton/actions/workflows/test.yml"><img src="https://img.shields.io/github/actions/workflow/status/alxwrd/fastapi-singleton/test.yml?branch=main&label=main"></a>
|
|
30
30
|
<a href="https://pypi.python.org/pypi/fastapi-singleton"><img src="https://img.shields.io/pypi/v/fastapi-singleton.svg"></a>
|
|
31
31
|
<a href="https://github.com/alxwrd/fastapi-singleton/blob/main/LICENCE"><img src="https://img.shields.io/pypi/l/fastapi-singleton.svg?"></a>
|
|
32
32
|
</div>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
pools, HTTP clients, and anything else that's expensive to create and safe to
|
|
38
|
-
share.
|
|
39
|
-
|
|
40
|
-
`fastapi-singleton` gives you a `@singleton` decorator that turns any
|
|
41
|
-
dependency, function or class, into one shared instance per process, with
|
|
42
|
-
proper startup and shutdown hooks wired into FastAPI's `lifespan`, instead of
|
|
43
|
-
leaving it to whatever a `SIGTERM` does to a `@lru_cache`d object.
|
|
33
|
+
Gives you a `@singleton` decorator that turns any
|
|
34
|
+
dependency, function or class, into one shared instance per process, with
|
|
35
|
+
proper startup and shutdown hooks wired into FastAPI's `lifespan`, instead of
|
|
36
|
+
leaving it to whatever a `SIGTERM` does to a `@lru_cache`d object.
|
|
44
37
|
</div>
|
|
45
38
|
|
|
46
39
|
## Example
|
|
@@ -125,14 +118,6 @@ class Connection:
|
|
|
125
118
|
self.other = other
|
|
126
119
|
```
|
|
127
120
|
|
|
128
|
-
A class singleton's `__init__` is the constructor, plain and simple -
|
|
129
|
-
`Depends(Connection)` calls it exactly once, the same way any FastAPI
|
|
130
|
-
class-based dependency works. `__init__` can never be `async def` in
|
|
131
|
-
Python, so a class singleton can't do real async setup itself - if you need
|
|
132
|
-
that (an async connection pool, an `await`-based client, anything with
|
|
133
|
-
teardown), write it as a function singleton instead and have your class
|
|
134
|
-
depend on it, the same way `Connection` depends on `get_other` above.
|
|
135
|
-
|
|
136
121
|
A singleton can't depend on a regular, request-scoped dependency - there's
|
|
137
122
|
no request to resolve it from when the singleton is constructed eagerly at
|
|
138
123
|
startup, or directly in plain Python. `@singleton`-ing something that
|
|
@@ -3,23 +3,16 @@
|
|
|
3
3
|
<p align="center"><i>
|
|
4
4
|
Application-scoped dependencies for <code>fastapi</code>
|
|
5
5
|
</i></p>
|
|
6
|
-
<img width="256px" src=".github/assets/three-card-trickster-768.png">
|
|
6
|
+
<img width="256px" src="https://github.com/alxwrd/fastapi-singleton/raw/main/.github/assets/three-card-trickster-768.png">
|
|
7
7
|
<div align="center">
|
|
8
8
|
<a href="https://github.com/alxwrd/fastapi-singleton/actions/workflows/test.yml"><img src="https://img.shields.io/github/actions/workflow/status/alxwrd/fastapi-singleton/test.yml?branch=main&label=main"></a>
|
|
9
9
|
<a href="https://pypi.python.org/pypi/fastapi-singleton"><img src="https://img.shields.io/pypi/v/fastapi-singleton.svg"></a>
|
|
10
10
|
<a href="https://github.com/alxwrd/fastapi-singleton/blob/main/LICENCE"><img src="https://img.shields.io/pypi/l/fastapi-singleton.svg?"></a>
|
|
11
11
|
</div>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
pools, HTTP clients, and anything else that's expensive to create and safe to
|
|
17
|
-
share.
|
|
18
|
-
|
|
19
|
-
`fastapi-singleton` gives you a `@singleton` decorator that turns any
|
|
20
|
-
dependency, function or class, into one shared instance per process, with
|
|
21
|
-
proper startup and shutdown hooks wired into FastAPI's `lifespan`, instead of
|
|
22
|
-
leaving it to whatever a `SIGTERM` does to a `@lru_cache`d object.
|
|
12
|
+
Gives you a `@singleton` decorator that turns any
|
|
13
|
+
dependency, function or class, into one shared instance per process, with
|
|
14
|
+
proper startup and shutdown hooks wired into FastAPI's `lifespan`, instead of
|
|
15
|
+
leaving it to whatever a `SIGTERM` does to a `@lru_cache`d object.
|
|
23
16
|
</div>
|
|
24
17
|
|
|
25
18
|
## Example
|
|
@@ -104,14 +97,6 @@ class Connection:
|
|
|
104
97
|
self.other = other
|
|
105
98
|
```
|
|
106
99
|
|
|
107
|
-
A class singleton's `__init__` is the constructor, plain and simple -
|
|
108
|
-
`Depends(Connection)` calls it exactly once, the same way any FastAPI
|
|
109
|
-
class-based dependency works. `__init__` can never be `async def` in
|
|
110
|
-
Python, so a class singleton can't do real async setup itself - if you need
|
|
111
|
-
that (an async connection pool, an `await`-based client, anything with
|
|
112
|
-
teardown), write it as a function singleton instead and have your class
|
|
113
|
-
depend on it, the same way `Connection` depends on `get_other` above.
|
|
114
|
-
|
|
115
100
|
A singleton can't depend on a regular, request-scoped dependency - there's
|
|
116
101
|
no request to resolve it from when the singleton is constructed eagerly at
|
|
117
102
|
startup, or directly in plain Python. `@singleton`-ing something that
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fastapi-singleton"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "Application-scoped dependencies for FastAPI"
|
|
5
5
|
license = "MIT"
|
|
6
6
|
readme = "README.md"
|
|
@@ -17,7 +17,7 @@ classifiers = [
|
|
|
17
17
|
"Programming Language :: Python :: 3 :: Only",
|
|
18
18
|
"Framework :: FastAPI",
|
|
19
19
|
]
|
|
20
|
-
requires-python = ">=3.
|
|
20
|
+
requires-python = ">=3.10"
|
|
21
21
|
dependencies = [
|
|
22
22
|
"fastapi>=0.115",
|
|
23
23
|
]
|
|
@@ -61,7 +61,6 @@ class _BaseFunctionSingleton(Generic[_LockT]):
|
|
|
61
61
|
self._provider = Provider(self._fn)
|
|
62
62
|
self._created = None
|
|
63
63
|
self._torn_down = False
|
|
64
|
-
self._before_end_done = False
|
|
65
64
|
self._value = _UNSET
|
|
66
65
|
self._construction_kwargs = {}
|
|
67
66
|
self._lock = self.LOCK_METHOD()
|
|
@@ -114,12 +113,12 @@ class SyncFunctionSingleton(_BaseFunctionSingleton[threading.Lock]):
|
|
|
114
113
|
def teardown(self) -> None:
|
|
115
114
|
if not self._should_teardown():
|
|
116
115
|
return
|
|
117
|
-
if not self._before_end_done:
|
|
118
|
-
self._before_end_done = True
|
|
119
|
-
_hooks.run_sync(self._hooks.before_end)
|
|
120
|
-
self._provider.teardown()
|
|
121
|
-
_hooks.run_sync(self._hooks.after_end)
|
|
122
116
|
self._torn_down = True
|
|
117
|
+
try:
|
|
118
|
+
_hooks.run_sync(self._hooks.before_end)
|
|
119
|
+
finally:
|
|
120
|
+
self._provider.teardown()
|
|
121
|
+
_hooks.run_sync(self._hooks.after_end)
|
|
123
122
|
|
|
124
123
|
|
|
125
124
|
class AsyncFunctionSingleton(_BaseFunctionSingleton[asyncio.Lock]):
|
|
@@ -143,14 +142,14 @@ class AsyncFunctionSingleton(_BaseFunctionSingleton[asyncio.Lock]):
|
|
|
143
142
|
async def teardown(self) -> None:
|
|
144
143
|
if not self._should_teardown():
|
|
145
144
|
return
|
|
146
|
-
if not self._before_end_done:
|
|
147
|
-
self._before_end_done = True
|
|
148
|
-
await _hooks.run_async(self._hooks.before_end)
|
|
149
|
-
result = self._provider.teardown()
|
|
150
|
-
if inspect.isawaitable(result):
|
|
151
|
-
await result
|
|
152
|
-
await _hooks.run_async(self._hooks.after_end)
|
|
153
145
|
self._torn_down = True
|
|
146
|
+
try:
|
|
147
|
+
await _hooks.run_async(self._hooks.before_end)
|
|
148
|
+
finally:
|
|
149
|
+
result = self._provider.teardown()
|
|
150
|
+
if inspect.isawaitable(result):
|
|
151
|
+
await result
|
|
152
|
+
await _hooks.run_async(self._hooks.after_end)
|
|
154
153
|
|
|
155
154
|
|
|
156
155
|
def make_function_singleton(
|
|
@@ -55,11 +55,15 @@ def guard_against_cycles(singleton: Any) -> Iterator[None]:
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def _values_equal(a: Any, b: Any) -> bool:
|
|
58
|
-
"""Like `==`, but
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
"""Like `==`, but recurses into dicts/lists/tuples and treats two NaN
|
|
59
|
+
floats as equal to each other.
|
|
60
|
+
|
|
61
|
+
Covers the ways `==` alone is unreliable for argument values: NaN
|
|
62
|
+
follows IEEE 754, where it's never equal to anything (including
|
|
63
|
+
another NaN), and unhashable containers need their elements compared
|
|
64
|
+
individually to catch a NaN nested inside them. Without this, a repeat
|
|
65
|
+
call with semantically-unchanged arguments could look like a
|
|
66
|
+
conflicting one."""
|
|
63
67
|
if (
|
|
64
68
|
isinstance(a, float)
|
|
65
69
|
and isinstance(b, float)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|