fastapi-injected 0.1.2__tar.gz → 0.1.4__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.
Files changed (29) hide show
  1. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/PKG-INFO +30 -8
  2. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/README.md +28 -7
  3. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/fastapi_injected/__init__.py +4 -2
  4. fastapi_injected-0.1.4/fastapi_injected/_fastapi_lifecycle.py +62 -0
  5. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/fastapi_injected/deps.py +10 -49
  6. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/fastapi_injected/inject.py +2 -2
  7. fastapi_injected-0.1.2/fastapi_injected/solve.py → fastapi_injected-0.1.4/fastapi_injected/resolve.py +4 -4
  8. fastapi_injected-0.1.4/fastapi_injected/scope.py +108 -0
  9. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/pyproject.toml +3 -1
  10. fastapi_injected-0.1.4/tests/test_fastapi.py +35 -0
  11. fastapi_injected-0.1.2/tests/test_solve.py → fastapi_injected-0.1.4/tests/test_resolve.py +6 -6
  12. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/tests/test_typing.py +3 -3
  13. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/uv.lock +47 -45
  14. fastapi_injected-0.1.2/fastapi_injected/scope.py +0 -75
  15. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/.github/dependabot.yml +0 -0
  16. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/.github/workflows/automerge.yml +0 -0
  17. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/.github/workflows/lint.yml +0 -0
  18. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/.github/workflows/publish.yml +0 -0
  19. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/.github/workflows/test.yml +0 -0
  20. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/.gitignore +0 -0
  21. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/.pre-commit-config.yaml +0 -0
  22. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/LICENSE +0 -0
  23. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/fastapi_injected/sign.py +0 -0
  24. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/fastapi_injected/types.py +0 -0
  25. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/tests/__init__.py +0 -0
  26. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/tests/deps.py +0 -0
  27. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/tests/ext/__init__.py +0 -0
  28. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/tests/ext/test_pydantic_ai.py +0 -0
  29. {fastapi_injected-0.1.2 → fastapi_injected-0.1.4}/tests/test_inject.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-injected
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Yet another library to reuse fastapi dependency injection
5
5
  Project-URL: Repository, https://github.com/uriyyo/fastapi-injected
6
6
  Author-email: Yurii Karabas <1998uriyyo@gmail.com>
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: Programming Language :: Python :: 3.14
16
16
  Requires-Python: >=3.12
17
17
  Requires-Dist: fastapi>=0.139.2
18
+ Requires-Dist: typing-extensions>=4.16.0
18
19
  Description-Content-Type: text/markdown
19
20
 
20
21
  # fastapi-injected
@@ -39,7 +40,7 @@ Declare dependencies as regular classes and annotate fields with `Dep[...]`:
39
40
  from dataclasses import dataclass
40
41
  from typing import AsyncIterator
41
42
 
42
- from fastapi_injected import Dep, DepFactory, Inejected, inject
43
+ from fastapi_injected import Dep, DepFactory, Injected, inject
43
44
 
44
45
 
45
46
  @dataclass
@@ -66,7 +67,7 @@ class Service:
66
67
 
67
68
 
68
69
  @inject
69
- async def handler(*, service: Dep[Service] = Inejected) -> None:
70
+ async def handler(*, service: Dep[Service] = Injected) -> None:
70
71
  ... # service is built and injected, session is closed on exit
71
72
 
72
73
 
@@ -75,29 +76,31 @@ await handler()
75
76
 
76
77
  - `Dep[T]` — resolve `T` by calling it, same as FastAPI's `Annotated[T, Depends()]`.
77
78
  - `DepFactory[T, factory]` — resolve `T` via a factory, same as `Annotated[T, Depends(factory)]`. Generator factories get proper teardown.
78
- - `Inejected` — a sentinel default that exists purely to make type checkers happy: without it they would complain about a missing argument at call sites. At runtime the parameter is always filled in by `@inject`.
79
+ - `Injected` — a sentinel default that exists purely to make type checkers happy: without it they would complain about a missing argument at call sites. At runtime the parameter is always filled in by `@inject`.
79
80
 
80
81
  Injected parameters mix freely with regular ones — pass your own arguments as usual and the rest is injected:
81
82
 
82
83
  ```python
83
84
  @inject
84
- async def add(a: int, b: int, *, service: Dep[Service] = Inejected) -> int:
85
+ async def add(a: int, b: int, *, service: Dep[Service] = Injected) -> int:
85
86
  ...
86
87
 
87
88
 
88
89
  result = await add(1, 2)
89
90
  ```
90
91
 
91
- ### Solving a type directly
92
+ ### Resolving a type directly
92
93
 
93
94
  No decorator needed — resolve a dependency graph on demand:
94
95
 
95
96
  ```python
96
- from fastapi_injected import solve
97
+ from fastapi_injected import resolve
97
98
 
98
- service = await solve(Service)
99
+ service = await resolve(Service)
99
100
  ```
100
101
 
102
+ Like `@inject`, `resolve` accepts `new_scope=True` to force a fresh scope instead of reusing the surrounding one.
103
+
101
104
  ### Scopes and caching
102
105
 
103
106
  By default every call to an injected function gets its own scope: dependencies are built, cached within the call, and torn down when it returns. Wrap several calls in `push_inject_scope()` to share one cache (and defer teardown to the end of the scope):
@@ -113,6 +116,25 @@ async with push_inject_scope():
113
116
 
114
117
  Use `@inject(new_scope=True)` to opt a function out of the surrounding scope and always get fresh dependencies.
115
118
 
119
+ ### FastAPI request integration
120
+
121
+ Inside a FastAPI app, `@inject`-ed functions and `resolve` can share the request's own dependency cache — the same instances FastAPI built for the handler. Register `init_inject_scope` as a dependency:
122
+
123
+ ```python
124
+ from fastapi import Depends, FastAPI
125
+ from fastapi_injected import Dep, init_inject_scope, resolve
126
+
127
+ app = FastAPI(dependencies=[Depends(init_inject_scope)])
128
+
129
+
130
+ @app.get("/")
131
+ async def route(service: Dep[Service]) -> str:
132
+ same = await resolve(Service) # same instance as `service`
133
+ ...
134
+ ```
135
+
136
+ Anything called from the handler — including `@inject`-ed helpers — resolves against the request's cache, so a per-request dependency like a DB session stays a single instance for the whole request.
137
+
116
138
  ## License
117
139
 
118
140
  MIT
@@ -20,7 +20,7 @@ Declare dependencies as regular classes and annotate fields with `Dep[...]`:
20
20
  from dataclasses import dataclass
21
21
  from typing import AsyncIterator
22
22
 
23
- from fastapi_injected import Dep, DepFactory, Inejected, inject
23
+ from fastapi_injected import Dep, DepFactory, Injected, inject
24
24
 
25
25
 
26
26
  @dataclass
@@ -47,7 +47,7 @@ class Service:
47
47
 
48
48
 
49
49
  @inject
50
- async def handler(*, service: Dep[Service] = Inejected) -> None:
50
+ async def handler(*, service: Dep[Service] = Injected) -> None:
51
51
  ... # service is built and injected, session is closed on exit
52
52
 
53
53
 
@@ -56,29 +56,31 @@ await handler()
56
56
 
57
57
  - `Dep[T]` — resolve `T` by calling it, same as FastAPI's `Annotated[T, Depends()]`.
58
58
  - `DepFactory[T, factory]` — resolve `T` via a factory, same as `Annotated[T, Depends(factory)]`. Generator factories get proper teardown.
59
- - `Inejected` — a sentinel default that exists purely to make type checkers happy: without it they would complain about a missing argument at call sites. At runtime the parameter is always filled in by `@inject`.
59
+ - `Injected` — a sentinel default that exists purely to make type checkers happy: without it they would complain about a missing argument at call sites. At runtime the parameter is always filled in by `@inject`.
60
60
 
61
61
  Injected parameters mix freely with regular ones — pass your own arguments as usual and the rest is injected:
62
62
 
63
63
  ```python
64
64
  @inject
65
- async def add(a: int, b: int, *, service: Dep[Service] = Inejected) -> int:
65
+ async def add(a: int, b: int, *, service: Dep[Service] = Injected) -> int:
66
66
  ...
67
67
 
68
68
 
69
69
  result = await add(1, 2)
70
70
  ```
71
71
 
72
- ### Solving a type directly
72
+ ### Resolving a type directly
73
73
 
74
74
  No decorator needed — resolve a dependency graph on demand:
75
75
 
76
76
  ```python
77
- from fastapi_injected import solve
77
+ from fastapi_injected import resolve
78
78
 
79
- service = await solve(Service)
79
+ service = await resolve(Service)
80
80
  ```
81
81
 
82
+ Like `@inject`, `resolve` accepts `new_scope=True` to force a fresh scope instead of reusing the surrounding one.
83
+
82
84
  ### Scopes and caching
83
85
 
84
86
  By default every call to an injected function gets its own scope: dependencies are built, cached within the call, and torn down when it returns. Wrap several calls in `push_inject_scope()` to share one cache (and defer teardown to the end of the scope):
@@ -94,6 +96,25 @@ async with push_inject_scope():
94
96
 
95
97
  Use `@inject(new_scope=True)` to opt a function out of the surrounding scope and always get fresh dependencies.
96
98
 
99
+ ### FastAPI request integration
100
+
101
+ Inside a FastAPI app, `@inject`-ed functions and `resolve` can share the request's own dependency cache — the same instances FastAPI built for the handler. Register `init_inject_scope` as a dependency:
102
+
103
+ ```python
104
+ from fastapi import Depends, FastAPI
105
+ from fastapi_injected import Dep, init_inject_scope, resolve
106
+
107
+ app = FastAPI(dependencies=[Depends(init_inject_scope)])
108
+
109
+
110
+ @app.get("/")
111
+ async def route(service: Dep[Service]) -> str:
112
+ same = await resolve(Service) # same instance as `service`
113
+ ...
114
+ ```
115
+
116
+ Anything called from the handler — including `@inject`-ed helpers — resolves against the request's cache, so a per-request dependency like a DB session stays a single instance for the whole request.
117
+
97
118
  ## License
98
119
 
99
120
  MIT
@@ -1,13 +1,15 @@
1
+ from ._fastapi_lifecycle import init_inject_scope
1
2
  from .inject import inject
3
+ from .resolve import resolve
2
4
  from .scope import push_inject_scope
3
- from .solve import solve
4
5
  from .types import Dep, DepFactory, Injected
5
6
 
6
7
  __all__ = [
7
8
  "Dep",
8
9
  "DepFactory",
9
10
  "Injected",
11
+ "init_inject_scope",
10
12
  "inject",
11
13
  "push_inject_scope",
12
- "solve",
14
+ "resolve",
13
15
  ]
@@ -0,0 +1,62 @@
1
+ from collections.abc import AsyncIterable
2
+ from functools import wraps
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from fastapi import Request, WebSocket, routing
6
+ from fastapi.dependencies import utils
7
+
8
+ from .scope import push_inject_scope
9
+ from .types import DependencyCache
10
+
11
+ _DEPENDENCY_CACHE_KEY = "__fastapi_injected_dependency_cache__"
12
+
13
+
14
+ class MissingDependencyCacheError(Exception):
15
+ pass
16
+
17
+
18
+ if not TYPE_CHECKING:
19
+ _base_solve_dependencies = utils.solve_dependencies
20
+
21
+ @wraps(_base_solve_dependencies)
22
+ async def _solve_dependencies(
23
+ *,
24
+ request: Request | WebSocket,
25
+ dependency_cache: DependencyCache | None = None,
26
+ **kwargs: Any,
27
+ ) -> utils.SolvedDependency:
28
+ if (current_cache := request.scope.get(_DEPENDENCY_CACHE_KEY)) is not None:
29
+ dependency_cache = current_cache
30
+ elif dependency_cache is None:
31
+ dependency_cache = {}
32
+
33
+ request.scope[_DEPENDENCY_CACHE_KEY] = dependency_cache
34
+
35
+ return await _base_solve_dependencies(
36
+ request=request,
37
+ dependency_cache=dependency_cache,
38
+ **kwargs,
39
+ )
40
+
41
+ utils.solve_dependencies = _solve_dependencies
42
+ routing.solve_dependencies = _solve_dependencies
43
+
44
+
45
+ def _get_dependency_cache(request: Request) -> DependencyCache:
46
+ try:
47
+ return request.scope[_DEPENDENCY_CACHE_KEY]
48
+ except KeyError:
49
+ raise MissingDependencyCacheError("Dependency cache not found") from None
50
+
51
+
52
+ async def init_inject_scope(request: Request) -> AsyncIterable[None]:
53
+ async with push_inject_scope(
54
+ dependency_cache=_get_dependency_cache(request),
55
+ request=request,
56
+ ):
57
+ yield
58
+
59
+
60
+ __all__ = [
61
+ "init_inject_scope",
62
+ ]
@@ -1,5 +1,6 @@
1
1
  import inspect
2
2
  from collections.abc import Callable
3
+ from contextlib import AsyncExitStack
3
4
  from contextvars import ContextVar
4
5
  from copy import copy
5
6
  from functools import lru_cache, wraps
@@ -7,49 +8,13 @@ from typing import Annotated, Any, Literal, cast, overload
7
8
 
8
9
  from fastapi import Depends
9
10
  from fastapi.dependencies.models import Dependant
10
- from fastapi.dependencies.utils import get_dependant, get_typed_signature
11
- from fastapi.dependencies.utils import solve_dependencies as _solve_dependencies
12
- from starlette.requests import Request
13
- from starlette.types import Message, Scope
11
+ from fastapi.dependencies.utils import get_dependant, get_typed_signature, solve_dependencies
14
12
 
15
13
  from .scope import InjectScope
16
14
  from .sign import prepare_sign, update_func_sign
17
15
  from .types import Coro, HasSignature
18
16
 
19
17
 
20
- def _dummy_scope() -> Scope:
21
- return {
22
- "type": "http",
23
- "http_version": "1.1",
24
- "query_string": b"",
25
- "headers": [],
26
- }
27
-
28
-
29
- def _dummy_request(
30
- *,
31
- extra_scope: Scope | None = None,
32
- ) -> Request:
33
- async def _dummy_receive() -> Message:
34
- return {
35
- "type": "http.request",
36
- "body": b"",
37
- }
38
-
39
- async def _dummy_send(_: Message, /) -> None:
40
- pass
41
-
42
- scope = _dummy_scope()
43
- if extra_scope:
44
- scope.update(extra_scope)
45
-
46
- return Request(
47
- scope,
48
- receive=_dummy_receive,
49
- send=_dummy_send,
50
- )
51
-
52
-
53
18
  @lru_cache(maxsize=1024)
54
19
  def create_dependant[**P, R](func: Callable[P, Coro[R]], /) -> Dependant:
55
20
  @wraps(func)
@@ -90,7 +55,7 @@ def create_single_dependant[**P, R](func: Callable[P, R], /) -> Dependant:
90
55
 
91
56
 
92
57
  @overload
93
- async def solve_dependencies(
58
+ async def resolve_dependencies(
94
59
  dependant: Dependant,
95
60
  scope: InjectScope,
96
61
  *,
@@ -99,7 +64,7 @@ async def solve_dependencies(
99
64
 
100
65
 
101
66
  @overload
102
- async def solve_dependencies(
67
+ async def resolve_dependencies(
103
68
  dependant: Dependant,
104
69
  scope: InjectScope,
105
70
  *,
@@ -107,23 +72,19 @@ async def solve_dependencies(
107
72
  ) -> Any: ...
108
73
 
109
74
 
110
- async def solve_dependencies(
75
+ async def resolve_dependencies(
111
76
  dependant: Dependant,
112
77
  scope: InjectScope,
113
78
  *,
114
79
  single: bool = False,
115
80
  ) -> dict[str, Any]:
116
- solved = await _solve_dependencies(
117
- request=_dummy_request(
118
- extra_scope={
119
- "fastapi_inner_astack": scope.request_astack,
120
- "fastapi_function_astack": scope.func_astack,
121
- },
122
- ),
81
+ solved = await solve_dependencies(
82
+ request=scope.request,
123
83
  dependant=dependant,
124
- async_exit_stack=scope.request_astack,
125
84
  dependency_cache=copy(scope.dependency_cache),
126
85
  dependency_overrides_provider=_dependency_override_provider.get(),
86
+ # this parameter is deprecated and not used
87
+ async_exit_stack=cast(AsyncExitStack, None),
127
88
  embed_body_fields=False,
128
89
  )
129
90
 
@@ -153,6 +114,6 @@ def set_inject_dependency_override_provider(provider: Any, /) -> None:
153
114
 
154
115
  __all__ = [
155
116
  "create_dependant",
117
+ "resolve_dependencies",
156
118
  "set_inject_dependency_override_provider",
157
- "solve_dependencies",
158
119
  ]
@@ -1,7 +1,7 @@
1
1
  from functools import partial, wraps
2
2
  from typing import overload
3
3
 
4
- from .deps import create_dependant, solve_dependencies
4
+ from .deps import create_dependant, resolve_dependencies
5
5
  from .scope import inside_inject_scope
6
6
  from .sign import strip_sign
7
7
  from .types import AsyncFunc, Decorator
@@ -40,7 +40,7 @@ def inject[**P, R](
40
40
  async with inside_inject_scope(
41
41
  new_scope=new_scope,
42
42
  ) as inject_scope:
43
- solved = await solve_dependencies(dependant, inject_scope)
43
+ solved = await resolve_dependencies(dependant, inject_scope)
44
44
 
45
45
  for key, value in solved.items():
46
46
  kwargs.setdefault(key, value)
@@ -1,10 +1,10 @@
1
1
  from typing_extensions import TypeForm
2
2
 
3
- from .deps import create_single_dependant, solve_dependencies
3
+ from .deps import create_single_dependant, resolve_dependencies
4
4
  from .scope import inside_inject_scope
5
5
 
6
6
 
7
- async def solve[R](
7
+ async def resolve[R](
8
8
  tp: TypeForm[R],
9
9
  *,
10
10
  new_scope: bool = False,
@@ -14,7 +14,7 @@ async def solve[R](
14
14
  async with inside_inject_scope(
15
15
  new_scope=new_scope,
16
16
  ) as inject_scope:
17
- return await solve_dependencies(
17
+ return await resolve_dependencies(
18
18
  dependant,
19
19
  inject_scope,
20
20
  single=True,
@@ -22,5 +22,5 @@ async def solve[R](
22
22
 
23
23
 
24
24
  __all__ = [
25
- "solve",
25
+ "resolve",
26
26
  ]
@@ -0,0 +1,108 @@
1
+ from collections.abc import AsyncIterator
2
+ from contextlib import (
3
+ AsyncExitStack,
4
+ asynccontextmanager,
5
+ )
6
+ from contextvars import ContextVar
7
+ from dataclasses import dataclass
8
+
9
+ from fastapi import Request
10
+ from starlette.types import Message, Scope
11
+
12
+ from .types import DependencyCache
13
+
14
+
15
+ def _dummy_scope() -> Scope:
16
+ return {
17
+ "type": "http",
18
+ "http_version": "1.1",
19
+ "query_string": b"",
20
+ "headers": [],
21
+ }
22
+
23
+
24
+ def _dummy_request(
25
+ *,
26
+ extra_scope: Scope | None = None,
27
+ ) -> Request:
28
+ async def _dummy_receive() -> Message:
29
+ return {
30
+ "type": "http.request",
31
+ "body": b"",
32
+ }
33
+
34
+ async def _dummy_send(_: Message, /) -> None:
35
+ pass
36
+
37
+ scope = _dummy_scope()
38
+ if extra_scope:
39
+ scope.update(extra_scope)
40
+
41
+ return Request(
42
+ scope,
43
+ receive=_dummy_receive,
44
+ send=_dummy_send,
45
+ )
46
+
47
+
48
+ @dataclass
49
+ class InjectScope:
50
+ dependency_cache: DependencyCache
51
+ request: Request
52
+
53
+
54
+ _inject_scope: ContextVar[InjectScope | None] = ContextVar(
55
+ "_inject_scope",
56
+ default=None,
57
+ )
58
+
59
+
60
+ @asynccontextmanager
61
+ async def push_inject_scope(
62
+ *,
63
+ dependency_cache: DependencyCache | None = None,
64
+ request: Request | None = None,
65
+ ) -> AsyncIterator[InjectScope]:
66
+ if dependency_cache is None:
67
+ dependency_cache = {}
68
+
69
+ async with AsyncExitStack() as stack:
70
+ request = request or _dummy_request(
71
+ extra_scope={
72
+ "fastapi_inner_astack": stack,
73
+ "fastapi_function_astack": stack,
74
+ },
75
+ )
76
+
77
+ scope = InjectScope(dependency_cache, request)
78
+ token = _inject_scope.set(scope)
79
+
80
+ try:
81
+ yield scope
82
+ finally:
83
+ _inject_scope.reset(token)
84
+
85
+
86
+ def current_inject_scope() -> InjectScope | None:
87
+ return _inject_scope.get()
88
+
89
+
90
+ @asynccontextmanager
91
+ async def inside_inject_scope(
92
+ *,
93
+ new_scope: bool = False,
94
+ ) -> AsyncIterator[InjectScope]:
95
+ scope = current_inject_scope()
96
+
97
+ async with AsyncExitStack() as stack:
98
+ if scope is None or new_scope:
99
+ scope = await stack.enter_async_context(push_inject_scope())
100
+
101
+ yield scope
102
+
103
+
104
+ __all__ = [
105
+ "InjectScope",
106
+ "current_inject_scope",
107
+ "inside_inject_scope",
108
+ ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fastapi-injected"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "Yet another library to reuse fastapi dependency injection"
5
5
  authors = [
6
6
  { name = "Yurii Karabas", email = "1998uriyyo@gmail.com" },
@@ -19,6 +19,7 @@ classifiers = [
19
19
  ]
20
20
  dependencies = [
21
21
  "fastapi>=0.139.2",
22
+ "typing-extensions>=4.16.0",
22
23
  ]
23
24
 
24
25
  [dependency-groups]
@@ -30,6 +31,7 @@ dev = [
30
31
  "ty>=0.0.62",
31
32
  "pre-commit>=4.6.1",
32
33
  "pydantic-ai-slim>=2.15.0",
34
+ "httpx>=0.28.1",
33
35
  ]
34
36
 
35
37
  [project.urls]
@@ -0,0 +1,35 @@
1
+ from fastapi import Depends, FastAPI, status
2
+ from fastapi.testclient import TestClient
3
+
4
+ from fastapi_injected import Dep, Injected, init_inject_scope, inject, resolve
5
+
6
+ from .deps import Container
7
+
8
+ app = FastAPI(
9
+ dependencies=[
10
+ Depends(init_inject_scope),
11
+ ],
12
+ )
13
+
14
+ client = TestClient(app)
15
+
16
+
17
+ @inject
18
+ async def _func(
19
+ *,
20
+ container: Dep[Container] = Injected,
21
+ ) -> Container:
22
+ return container
23
+
24
+
25
+ @app.get("/")
26
+ async def route(container: Dep[Container]) -> str:
27
+ assert await resolve(Container) is container
28
+ assert await _func() is container
29
+
30
+ return ""
31
+
32
+
33
+ def test_cache_is_working() -> None:
34
+ result = client.get("/")
35
+ assert result.status_code == status.HTTP_200_OK
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
 
3
3
  import pytest
4
4
 
5
- from fastapi_injected import Dep, push_inject_scope, solve
5
+ from fastapi_injected import Dep, push_inject_scope, resolve
6
6
 
7
7
  pytestmark = pytest.mark.asyncio
8
8
 
@@ -17,16 +17,16 @@ class Bar:
17
17
  foo: Dep[Foo]
18
18
 
19
19
 
20
- async def test_solve():
21
- bar = await solve(Bar)
20
+ async def test_resolve():
21
+ bar = await resolve(Bar)
22
22
 
23
23
  assert isinstance(bar, Bar)
24
24
  assert isinstance(bar.foo, Foo)
25
25
 
26
26
 
27
- async def test_solve_reuse_cache():
27
+ async def test_resolve_reuse_cache():
28
28
  async with push_inject_scope():
29
- b1 = await solve(Bar)
30
- b2 = await solve(Bar)
29
+ b1 = await resolve(Bar)
30
+ b2 = await resolve(Bar)
31
31
 
32
32
  assert b1 is b2
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- from fastapi_injected import Dep, DepFactory, Injected, inject, solve
3
+ from fastapi_injected import Dep, DepFactory, Injected, inject, resolve
4
4
 
5
5
  from .deps import Child, Container, ContextState, ctx_dep
6
6
 
@@ -22,8 +22,8 @@ async def func(
22
22
  return a + 10
23
23
 
24
24
 
25
- async def solve_test() -> None:
26
- container = await solve(Container)
25
+ async def resolve_test() -> None:
26
+ container = await resolve(Container)
27
27
 
28
28
  static_assert(is_equivalent_to(TypeOf[container], Container))
29
29
 
@@ -1,10 +1,6 @@
1
1
  version = 1
2
2
  revision = 3
3
3
  requires-python = ">=3.12"
4
- resolution-markers = [
5
- "python_full_version >= '3.14'",
6
- "python_full_version < '3.14'",
7
- ]
8
4
 
9
5
  [[package]]
10
6
  name = "annotated-doc"
@@ -160,14 +156,16 @@ wheels = [
160
156
 
161
157
  [[package]]
162
158
  name = "fastapi-injected"
163
- version = "0.1.2"
159
+ version = "0.1.4"
164
160
  source = { editable = "." }
165
161
  dependencies = [
166
162
  { name = "fastapi" },
163
+ { name = "typing-extensions" },
167
164
  ]
168
165
 
169
166
  [package.dev-dependencies]
170
167
  dev = [
168
+ { name = "httpx" },
171
169
  { name = "pre-commit" },
172
170
  { name = "pydantic-ai-slim" },
173
171
  { name = "pytest" },
@@ -178,10 +176,14 @@ dev = [
178
176
  ]
179
177
 
180
178
  [package.metadata]
181
- requires-dist = [{ name = "fastapi", specifier = ">=0.139.2" }]
179
+ requires-dist = [
180
+ { name = "fastapi", specifier = ">=0.139.2" },
181
+ { name = "typing-extensions", specifier = ">=4.16.0" },
182
+ ]
182
183
 
183
184
  [package.metadata.requires-dev]
184
185
  dev = [
186
+ { name = "httpx", specifier = ">=0.28.1" },
185
187
  { name = "pre-commit", specifier = ">=4.6.1" },
186
188
  { name = "pydantic-ai-slim", specifier = ">=2.15.0" },
187
189
  { name = "pytest", specifier = ">=9.1.1" },
@@ -202,15 +204,15 @@ wheels = [
202
204
 
203
205
  [[package]]
204
206
  name = "genai-prices"
205
- version = "0.0.71"
207
+ version = "0.0.72"
206
208
  source = { registry = "https://pypi.org/simple" }
207
209
  dependencies = [
208
210
  { name = "httpx2" },
209
211
  { name = "pydantic" },
210
212
  ]
211
- sdist = { url = "https://files.pythonhosted.org/packages/1d/e4/5072862613fba039da2b7c981a8649c6c6bbcb2863bd8bc81617c09ce5ee/genai_prices-0.0.71.tar.gz", hash = "sha256:de4db34ec38404f9ef383cb1ab29e204d16ccf27071af0b16d5747ee7affe36b", size = 82105, upload-time = "2026-07-10T00:38:30.491Z" }
213
+ sdist = { url = "https://files.pythonhosted.org/packages/6f/c8/2549fa8ceaaf0bd61114cf392250a107be657233723bc4e65cb91dba934f/genai_prices-0.0.72.tar.gz", hash = "sha256:a7e481d0ea85922fcf48df6864f2491fc81a4f03dcf0ecbd9aa8c2c6d9fcdbe8", size = 82753, upload-time = "2026-07-22T21:11:03.997Z" }
212
214
  wheels = [
213
- { url = "https://files.pythonhosted.org/packages/0d/98/c06c1318f6834a26268a2d4280e4183f60c5ea92152aea841feff29826ff/genai_prices-0.0.71-py3-none-any.whl", hash = "sha256:1d13111563af2b1ce43ccfacf77b7ac3216ad704c644408a56e11b181fe0d128", size = 84586, upload-time = "2026-07-10T00:38:29.252Z" },
215
+ { url = "https://files.pythonhosted.org/packages/22/2e/1e2c31666f7e4e2d0327a80f1c920fd8764ccd27672c4bc389d79219ece6/genai_prices-0.0.72-py3-none-any.whl", hash = "sha256:21281d8df34d9bfbb736e0763a60ceda02226938cd47379ae437a44fcf8ba23d", size = 85238, upload-time = "2026-07-22T21:11:02.911Z" },
214
216
  ]
215
217
 
216
218
  [[package]]
@@ -246,15 +248,15 @@ wheels = [
246
248
 
247
249
  [[package]]
248
250
  name = "httpcore2"
249
- version = "2.7.0"
251
+ version = "2.9.0"
250
252
  source = { registry = "https://pypi.org/simple" }
251
253
  dependencies = [
252
254
  { name = "h11" },
253
255
  { name = "truststore" },
254
256
  ]
255
- sdist = { url = "https://files.pythonhosted.org/packages/d5/fe/6a3f9f1a8bb8733326140737446aaf72fddb8b54b8f202302f5c84960613/httpcore2-2.7.0.tar.gz", hash = "sha256:6dc0fedf329a52a990930a5579edfebaea81118ea700ea0dd7de2b5e5be49efc", size = 65593, upload-time = "2026-07-14T20:40:01.111Z" }
257
+ sdist = { url = "https://files.pythonhosted.org/packages/d5/4e/fc534e5c8e9d6df4598cda50bd3df936ba9af495f932dc93376597b2f6db/httpcore2-2.9.0.tar.gz", hash = "sha256:03077a578e26d6166d831f68319326bf1d92cb851946292d2158a85e9da1896a", size = 67072, upload-time = "2026-07-23T14:09:49.949Z" }
256
258
  wheels = [
257
- { url = "https://files.pythonhosted.org/packages/6f/6c/62e2e279e63fc4f7a5ee841ef13175a8bbc613f258e9dcc186e9de803a42/httpcore2-2.7.0-py3-none-any.whl", hash = "sha256:1452f589fe23f55b44546cd884294c41a29330af902bc0b71a761fd52d18f92b", size = 81506, upload-time = "2026-07-14T20:39:58.053Z" },
259
+ { url = "https://files.pythonhosted.org/packages/d7/00/bf7357a92f1807f654c3bb303b3e158294ec6a1313c79511fc9f1a45de2b/httpcore2-2.9.0-py3-none-any.whl", hash = "sha256:e51e54521809628d2014da8d67eecd4de738d54f556d646069fa9cb63fc9a9dc", size = 82800, upload-time = "2026-07-23T14:09:46.868Z" },
258
260
  ]
259
261
 
260
262
  [[package]]
@@ -274,7 +276,7 @@ wheels = [
274
276
 
275
277
  [[package]]
276
278
  name = "httpx2"
277
- version = "2.7.0"
279
+ version = "2.9.0"
278
280
  source = { registry = "https://pypi.org/simple" }
279
281
  dependencies = [
280
282
  { name = "anyio" },
@@ -283,9 +285,9 @@ dependencies = [
283
285
  { name = "truststore" },
284
286
  { name = "typing-extensions", marker = "python_full_version < '3.13'" },
285
287
  ]
286
- sdist = { url = "https://files.pythonhosted.org/packages/a3/4a/129b2e21b90ac2985d3928d96792bccc39bc6dfe796c5eee2d8ec06d4105/httpx2-2.7.0.tar.gz", hash = "sha256:8b30709aed5c8465b0dd3b95c09ce301c8f79e7e7a2d00ab0af551e0d0375b07", size = 94487, upload-time = "2026-07-14T20:40:02.318Z" }
288
+ sdist = { url = "https://files.pythonhosted.org/packages/92/18/b79756b348664b13253a5811230daa1f33b3f0e86df4409b26199f5e9c3e/httpx2-2.9.0.tar.gz", hash = "sha256:85e33a04e7b0ad24044a62affa916140f363f19ba38e15fbc7e6aba1f1fb2775", size = 95340, upload-time = "2026-07-23T14:09:51.094Z" }
287
289
  wheels = [
288
- { url = "https://files.pythonhosted.org/packages/1d/b8/c341bba6411bdfda786020343c47a75ef472f6085caf82391b142b1a3ad9/httpx2-2.7.0-py3-none-any.whl", hash = "sha256:ed2a2719c696789e09493bd8e2bec3d8bd925cc6e26b68389ec25ade132f7bf4", size = 90234, upload-time = "2026-07-14T20:39:59.531Z" },
290
+ { url = "https://files.pythonhosted.org/packages/82/7d/354002b85409c6fe6f3f6b97624f1d11a4ddf701cd9a7ed2e161c73f0d86/httpx2-2.9.0-py3-none-any.whl", hash = "sha256:cc8e82fa3dc02fa91586dc6ccb3c9bd802a5a48adf4626d66553e793de67cbb4", size = 91107, upload-time = "2026-07-23T14:09:48.434Z" },
289
291
  ]
290
292
 
291
293
  [[package]]
@@ -335,14 +337,14 @@ wheels = [
335
337
 
336
338
  [[package]]
337
339
  name = "opentelemetry-api"
338
- version = "1.43.0"
340
+ version = "1.44.0"
339
341
  source = { registry = "https://pypi.org/simple" }
340
342
  dependencies = [
341
343
  { name = "typing-extensions" },
342
344
  ]
343
- sdist = { url = "https://files.pythonhosted.org/packages/ae/cc/e4c9584181f86494df0f6bdec1a4f3280c50db44704dc2a407e994fc87bb/opentelemetry_api-1.43.0.tar.gz", hash = "sha256:107d0d03857ea8fc7c5fcbbbd83f800c281f0d560553d61c1d675fccfd1761c1", size = 73476, upload-time = "2026-06-24T15:19:55.323Z" }
345
+ sdist = { url = "https://files.pythonhosted.org/packages/ee/8b/aa9e2d8b8dfa7c946f7dec5d1f8f6ba8eca062f43509a06bdb5ce93d26c0/opentelemetry_api-1.44.0.tar.gz", hash = "sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a", size = 72406, upload-time = "2026-07-16T15:25:32.678Z" }
344
346
  wheels = [
345
- { url = "https://files.pythonhosted.org/packages/17/83/6dba32b85f31868400440dc7ad2ca1eab94cbbf3a7b0459ed39f8311a9e2/opentelemetry_api-1.43.0-py3-none-any.whl", hash = "sha256:20acf45e9b21851926835292e4045d290acade1edd2ff3de86d2f069687ba1fd", size = 61912, upload-time = "2026-06-24T15:19:35.434Z" },
347
+ { url = "https://files.pythonhosted.org/packages/ca/6f/a04e900f465ff3221ccc395522503e2d10e79fa21f2723c8e177aae1e0d1/opentelemetry_api-1.44.0-py3-none-any.whl", hash = "sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef", size = 60018, upload-time = "2026-07-16T15:25:11.657Z" },
346
348
  ]
347
349
 
348
350
  [[package]]
@@ -405,7 +407,7 @@ wheels = [
405
407
 
406
408
  [[package]]
407
409
  name = "pydantic-ai-slim"
408
- version = "2.15.0"
410
+ version = "2.16.0"
409
411
  source = { registry = "https://pypi.org/simple" }
410
412
  dependencies = [
411
413
  { name = "genai-prices" },
@@ -416,9 +418,9 @@ dependencies = [
416
418
  { name = "pydantic-graph" },
417
419
  { name = "typing-inspection" },
418
420
  ]
419
- sdist = { url = "https://files.pythonhosted.org/packages/54/bc/65fabea030e6b40c04b25b7069b559ff5cabea6e7cadf640839ceeb08af6/pydantic_ai_slim-2.15.0.tar.gz", hash = "sha256:29d804b6caf582337f4e99f98ba4f4bf0b9e164761429daa87c392a176315e20", size = 879375, upload-time = "2026-07-22T03:59:19.447Z" }
421
+ sdist = { url = "https://files.pythonhosted.org/packages/76/6a/3048579c646f4cea7966889009a1d7eef1365f9126393945d39cefad9e83/pydantic_ai_slim-2.16.0.tar.gz", hash = "sha256:36d17cb12edd72ffc62f9e06cc49ac5f23cb77cf6b665c4b1fd11c00dbe6a852", size = 889343, upload-time = "2026-07-23T02:47:20.696Z" }
420
422
  wheels = [
421
- { url = "https://files.pythonhosted.org/packages/66/d9/28a6010b1d1937fc5c0fa1516d8a7e05d871920269e1dedd28db3c138195/pydantic_ai_slim-2.15.0-py3-none-any.whl", hash = "sha256:758f9cf5bf1c7e83bf4e7459e71006a3d5da46443416f71281fea202365ce7a7", size = 1066704, upload-time = "2026-07-22T03:59:12.047Z" },
423
+ { url = "https://files.pythonhosted.org/packages/2e/95/2c79b9f8e875562bae8141078af27428120e8415d493bb07ea36ca5905f9/pydantic_ai_slim-2.16.0-py3-none-any.whl", hash = "sha256:7cad27fb8f45ce4af4e8da83d7f206a3e1038dbc7535625c0ce5518fe378a55d", size = 1077057, upload-time = "2026-07-23T02:47:12.633Z" },
422
424
  ]
423
425
 
424
426
  [[package]]
@@ -498,7 +500,7 @@ wheels = [
498
500
 
499
501
  [[package]]
500
502
  name = "pydantic-graph"
501
- version = "2.15.0"
503
+ version = "2.16.0"
502
504
  source = { registry = "https://pypi.org/simple" }
503
505
  dependencies = [
504
506
  { name = "httpx" },
@@ -506,9 +508,9 @@ dependencies = [
506
508
  { name = "pydantic" },
507
509
  { name = "typing-inspection" },
508
510
  ]
509
- sdist = { url = "https://files.pythonhosted.org/packages/6a/39/f56cfeb84754b07470c4149bdc2f242308c057975d51ca1d1695a58f36c3/pydantic_graph-2.15.0.tar.gz", hash = "sha256:3cc82597ed9bfff4806e5c5105bc64e6e627924a21933962e4d8a3aebe25bc6d", size = 43934, upload-time = "2026-07-22T03:59:21.684Z" }
511
+ sdist = { url = "https://files.pythonhosted.org/packages/c3/22/6b6426e14607275f6b15f2a0c4530514c48ff01f86c53bbaac4041d09df9/pydantic_graph-2.16.0.tar.gz", hash = "sha256:f71e5c8e78a4ce56bc044861178e506ebd99881717ae0993924c457f5de6230e", size = 43979, upload-time = "2026-07-23T02:47:23.328Z" }
510
512
  wheels = [
511
- { url = "https://files.pythonhosted.org/packages/a0/b6/6d6360afbc91b9430abe7f50266f2f0b78fcfc55a88b5681618948d60ab1/pydantic_graph-2.15.0-py3-none-any.whl", hash = "sha256:ed5b3fa18dccea9cef95a09fbbf087e49d9e244f11376ee0454d8f89059a95e6", size = 51660, upload-time = "2026-07-22T03:59:15.108Z" },
513
+ { url = "https://files.pythonhosted.org/packages/80/c0/362a7fb50562d7b51e3d02d454826e0a7b5652f27e5be8e835ddfaf84da6/pydantic_graph-2.16.0-py3-none-any.whl", hash = "sha256:99c25852c436d4d510d1ecdcb53ce4a0b11aa4d1d9d81472727587f2b5a3bc13", size = 51661, upload-time = "2026-07-23T02:47:15.921Z" },
512
514
  ]
513
515
 
514
516
  [[package]]
@@ -671,27 +673,27 @@ wheels = [
671
673
 
672
674
  [[package]]
673
675
  name = "ty"
674
- version = "0.0.62"
675
- source = { registry = "https://pypi.org/simple" }
676
- sdist = { url = "https://files.pythonhosted.org/packages/ff/2c/ecf9d6a7456e64ea9114d21e68edaea77ecda9151cc89dfd3ed0b673e4a9/ty-0.0.62.tar.gz", hash = "sha256:145b6feba4d5f38b6d595eb41f7a8ec1c970a0a83b79a70680e9e3b787a3e381", size = 6271717, upload-time = "2026-07-22T01:02:43.732Z" }
677
- wheels = [
678
- { url = "https://files.pythonhosted.org/packages/e4/ab/cb629d65b4437b298214a095488a8d84ec349f225ed84da13763adfce08b/ty-0.0.62-py3-none-linux_armv6l.whl", hash = "sha256:3db167f0aef29e7c34d47414c9ce160fd045e9b2d6a0edd08d0331b4eaa3b9ad", size = 12045218, upload-time = "2026-07-22T01:02:04.497Z" },
679
- { url = "https://files.pythonhosted.org/packages/b7/c9/f6555aa3b0c46bf2a38891a2048c45616e836a6aab96dff847ab01cc83b3/ty-0.0.62-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aab3f1d6ca4c9ad1869e57164478694d9828ad1dfcadc192ece7215a20859197", size = 11688585, upload-time = "2026-07-22T01:02:06.795Z" },
680
- { url = "https://files.pythonhosted.org/packages/56/25/9f4a322c64ac8a4f2d1593749bb5932c5c964e557fe7788116fa029b2463/ty-0.0.62-py3-none-macosx_11_0_arm64.whl", hash = "sha256:69ce10b241f487b69ccdc36ca4ac4f4ba683172d24594b5d7296a66f7e26d15c", size = 11204179, upload-time = "2026-07-22T01:02:09.109Z" },
681
- { url = "https://files.pythonhosted.org/packages/ef/db/d9cf7d5343ff896c2d780caec3dcd0ee77493d8a4140b8aa49e15824abff/ty-0.0.62-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd736420acb009d00a2379e47be4085dd3e3cdbe5305bdf5acce0dd9082f1bbb", size = 11745874, upload-time = "2026-07-22T01:02:11.507Z" },
682
- { url = "https://files.pythonhosted.org/packages/4b/23/a575008c2ccdaacaf1c358be52087db449ea73a78c91c8afb30084d64cfc/ty-0.0.62-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bca9a80e2e5fb72e88e31cf662aae3bd8d7e9e0005ab517a89c368267c992387", size = 11875688, upload-time = "2026-07-22T01:02:14.072Z" },
683
- { url = "https://files.pythonhosted.org/packages/df/92/43a6250b52a704169b6d864665f286f71588d95ff645941cc0e4ca026ab7/ty-0.0.62-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f8d2a35940bbdea1d28c36c83d3eaba08bf47990486a066b9cb3ae4fbb8625e", size = 12484062, upload-time = "2026-07-22T01:02:16.081Z" },
684
- { url = "https://files.pythonhosted.org/packages/92/34/35c698baa067a2543a32cd7237ad9bdc63dec1ce8b5b62b4686b4485fec4/ty-0.0.62-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f8257481a517f05e0621ef3b4ef4089a38ac03f17aa800c9f672df19fd03b3d", size = 13007869, upload-time = "2026-07-22T01:02:18.145Z" },
685
- { url = "https://files.pythonhosted.org/packages/2a/2c/e9dd785d62bcfeb9285cc030495288640f1942bf3228a791a3a30d72f7b4/ty-0.0.62-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b907322a2cecd3e25902878997d08de57cf0efbb27833f8b3378fbd4f218590", size = 12675651, upload-time = "2026-07-22T01:02:20.299Z" },
686
- { url = "https://files.pythonhosted.org/packages/36/86/5650d8b70bd80946afdb3aaefedd280a698b82d9066f18f8a47f58642230/ty-0.0.62-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac8b70f0b5494feed0a3cf47af579141e4c2e88faffcaeebcb4176e914b54b6a", size = 12324208, upload-time = "2026-07-22T01:02:22.671Z" },
687
- { url = "https://files.pythonhosted.org/packages/38/2d/278a17a0f47a90804a744e7d7a598c47fbc15a357ad5aaff563fee3bc65a/ty-0.0.62-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:059790546f9304a750f28ded9b964a7a27dec88e725d329a4b9ab5d944e83dd6", size = 12616124, upload-time = "2026-07-22T01:02:24.8Z" },
688
- { url = "https://files.pythonhosted.org/packages/1a/51/62741847bd646f8702b9ce61de496804a8ac52055afab8dbb94aaf64f431/ty-0.0.62-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9e83bc6d59e2b37a1d8923ffb7e2455fd35ad35821a3dc69c8459ca9cd28724", size = 11695150, upload-time = "2026-07-22T01:02:27.17Z" },
689
- { url = "https://files.pythonhosted.org/packages/0c/0d/74bd23f94a527bdc32d8366f51db157f75a3074e320c567a2eae83bd6d21/ty-0.0.62-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f84ac4b606c3b4275a17f7b2f60cb0ca8216972bb7af2f515383ee6e4b1fb7f1", size = 11884562, upload-time = "2026-07-22T01:02:29.09Z" },
690
- { url = "https://files.pythonhosted.org/packages/dd/0e/2f42cc81436b5dc0070b4e353c74cbe94ce91ef5828753db921088dec500/ty-0.0.62-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1c05f8f9cd720fa42744574bf3bda9b1b676f7876e33324b03b21aad81f651df", size = 12071773, upload-time = "2026-07-22T01:02:31.292Z" },
691
- { url = "https://files.pythonhosted.org/packages/42/24/0bd3c0069524bf7d5e610d92dd0698414372015461507280c8787c9cd25b/ty-0.0.62-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:88bef93008228890e990fe9e87f0504b8dea9f9f24534eb3a5ee9d8ab9e5cd4f", size = 12425247, upload-time = "2026-07-22T01:02:34.474Z" },
692
- { url = "https://files.pythonhosted.org/packages/3f/27/08505fa0d2bef8fe4107753bb02ed2da4e323103ff5d423b9cc9eea4346e/ty-0.0.62-py3-none-win32.whl", hash = "sha256:254690c758e9d0ee2c8dfe07320d4e9d54709d5e95fa588ecf5a96391dc8d8d4", size = 11381070, upload-time = "2026-07-22T01:02:36.625Z" },
693
- { url = "https://files.pythonhosted.org/packages/aa/c1/609840f13d1c48e88e5b431fe789650da14cb25da0df2979fe08ce422e73/ty-0.0.62-py3-none-win_amd64.whl", hash = "sha256:d88c2594e6a33c5f859c1d9016ad6137ec304c5d51422ce48894d2b9393956a7", size = 12415015, upload-time = "2026-07-22T01:02:39.193Z" },
694
- { url = "https://files.pythonhosted.org/packages/0f/a8/43f838fa38922f175a223814120b5faeb1bb6e5ef1533e45f543c0f510b8/ty-0.0.62-py3-none-win_arm64.whl", hash = "sha256:11b5e7df21890bef3eda49ae15a0c4d12554917ec4ea90ea985caa33241af627", size = 11773787, upload-time = "2026-07-22T01:02:41.564Z" },
676
+ version = "0.0.63"
677
+ source = { registry = "https://pypi.org/simple" }
678
+ sdist = { url = "https://files.pythonhosted.org/packages/ba/ce/cbeaa5c7576fec643609dfbf200d59493523b1cc0481d4e7a5effcbf0630/ty-0.0.63.tar.gz", hash = "sha256:c2f66439393b3acac69306c117d4ae44638ce5fffa4a20c21046e85bd473359f", size = 6280695, upload-time = "2026-07-23T11:41:39.845Z" }
679
+ wheels = [
680
+ { url = "https://files.pythonhosted.org/packages/1a/e4/d17a8e113ab15c692fe6fb9c422112d4bee7e829d80ced35826b45d98d98/ty-0.0.63-py3-none-linux_armv6l.whl", hash = "sha256:9a4ef7782e3af314fb63d006bec5ac3025bd70fee774b4dcfb5e44e8564f1994", size = 12056302, upload-time = "2026-07-23T11:41:03.5Z" },
681
+ { url = "https://files.pythonhosted.org/packages/be/0b/357234c815dc4bfcc88c3f860aa0983fe4228c8aa2a5bb16c35ee08a94af/ty-0.0.63-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:01eab0ab70d51ad10298aa2d4b058b387a1fe93e5ee52d2a1ee23e9c69ba8354", size = 11737674, upload-time = "2026-07-23T11:41:05.862Z" },
682
+ { url = "https://files.pythonhosted.org/packages/1c/13/193d9aeeb6774690351cff9fafabd3ae9b54cc225d125e07fa004ce23bdc/ty-0.0.63-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a671b61eaad16178389e05b9c108c9cb75ae8d84968fe55906484f55d6268338", size = 11264191, upload-time = "2026-07-23T11:41:07.963Z" },
683
+ { url = "https://files.pythonhosted.org/packages/4f/b7/c5843e16759a2b25fc8a7197473474038e585ac9fdaa6fa16aeb6384bf6a/ty-0.0.63-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b255cc83d95c51bb9ee4931303fdbece8cb1d6d7c655eb77a3f2c8f349fb64d6", size = 11818890, upload-time = "2026-07-23T11:41:09.885Z" },
684
+ { url = "https://files.pythonhosted.org/packages/06/20/adf83ae1fc570d9bb449605e1eeeaa523ad2329ca838a636698b5c135d11/ty-0.0.63-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0da1e8183aa4f893421a173904478021498f542ee41273660538da6649a9631c", size = 11853141, upload-time = "2026-07-23T11:41:12.151Z" },
685
+ { url = "https://files.pythonhosted.org/packages/ea/90/f8effd846e3ee13486ea08257c13094d58b9f188c5641bf609d6a7d5c09f/ty-0.0.63-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128f38eb5a67199e3811426386f7ec96f41251ddf45e04ddc0bcbb29d4853245", size = 12545184, upload-time = "2026-07-23T11:41:14.4Z" },
686
+ { url = "https://files.pythonhosted.org/packages/b8/76/aac3a30d40431eb1d329acea016b248f5b6255e30ef3d28e19447e344be2/ty-0.0.63-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bedf34aff8b0557f2a7119b314a68a9c9e58c1d21554d0e2748f2c5fe6a1f638", size = 13062375, upload-time = "2026-07-23T11:41:16.441Z" },
687
+ { url = "https://files.pythonhosted.org/packages/62/3d/0158733932893e17f6008dadd74c8d7aed422fefc66e47fe77888014fe8c/ty-0.0.63-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7328d63c34587606dce02935a25404f54cd0161bfe413b8f7fc21d174aead612", size = 12619461, upload-time = "2026-07-23T11:41:18.538Z" },
688
+ { url = "https://files.pythonhosted.org/packages/f5/be/e280ad095b050778f16f493d49a073fa5f6d8f301d3e2e59be6a672ba05c/ty-0.0.63-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504c4457f3a62afe836c1f26a2c9a12549299095f9cc4146778558df51a7515c", size = 12376402, upload-time = "2026-07-23T11:41:20.482Z" },
689
+ { url = "https://files.pythonhosted.org/packages/57/57/619788bf335cb86b090470b557ae1eed33613dc24e12142505d32b462e58/ty-0.0.63-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7394ed39424b027c89d5f0c818871c791e33958b88f5bb13e14bd4a12a3ca631", size = 12671377, upload-time = "2026-07-23T11:41:22.489Z" },
690
+ { url = "https://files.pythonhosted.org/packages/07/a2/0aff2cdd3c98e2c4729337542b8da0ce1980790e1600e0c4a717a1f46293/ty-0.0.63-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:94c3f155230490f8fb505911655e940c630c25cc0c35a76690cb413254fb4437", size = 11768090, upload-time = "2026-07-23T11:41:24.558Z" },
691
+ { url = "https://files.pythonhosted.org/packages/43/4a/cdb5f1d26154144dfee08e1bd671daf827238170f7cee9dad43990bb2016/ty-0.0.63-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c16e1d8b4f0ae99106d5f13a894034a202baf6cdab61e2bb1a239de82904f839", size = 11867747, upload-time = "2026-07-23T11:41:26.794Z" },
692
+ { url = "https://files.pythonhosted.org/packages/7e/26/ecc09ecb70bc9fbfdf42fa57ff29568f173e8200df575a0d72dc2b8486f9/ty-0.0.63-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b66293dad89eaed4b9fbf3b661614dbeb85b59ba037178a3f9a0adedf264da5c", size = 12120000, upload-time = "2026-07-23T11:41:28.731Z" },
693
+ { url = "https://files.pythonhosted.org/packages/14/07/862822f9c2c397785b69b25cf79b4dfc3c0d55684b9adf11ac194450f8e1/ty-0.0.63-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8b29cc832e2ec73502c97dd7325d6ae0e085b34a33529a0f785192317d9862fc", size = 12477862, upload-time = "2026-07-23T11:41:30.847Z" },
694
+ { url = "https://files.pythonhosted.org/packages/d0/c2/50b08b641578d6f48e8aa28a91d0de32c91aa24dd926ed199e8afd2d37ea/ty-0.0.63-py3-none-win32.whl", hash = "sha256:f0a8fbfd1f990c0c5d85cce018d438969ac79e49247e2192c9745394bb7d17ab", size = 11433155, upload-time = "2026-07-23T11:41:33.28Z" },
695
+ { url = "https://files.pythonhosted.org/packages/92/2d/d422a5568f0d1f317186be22241288dc6e08b71dc24aca223f22038ac2d2/ty-0.0.63-py3-none-win_amd64.whl", hash = "sha256:2aa2370bdd6f42e9f37518c812379bef70b9162f9e5aad511495229b0b71cb93", size = 12450036, upload-time = "2026-07-23T11:41:35.559Z" },
696
+ { url = "https://files.pythonhosted.org/packages/35/97/2c9748e28ead0650c7ad3e5f74f178832ceabd7cb5c272a882f29eb32ee4/ty-0.0.63-py3-none-win_arm64.whl", hash = "sha256:95ac1a62162c3c7ac204731e95ebf766d62a46a7bfa238a6acbe923fcb772cb1", size = 11826243, upload-time = "2026-07-23T11:41:37.749Z" },
695
697
  ]
696
698
 
697
699
  [[package]]
@@ -1,75 +0,0 @@
1
- from collections.abc import AsyncIterator
2
- from contextlib import (
3
- AbstractAsyncContextManager,
4
- AsyncExitStack,
5
- asynccontextmanager,
6
- nullcontext,
7
- )
8
- from contextvars import ContextVar
9
- from dataclasses import dataclass
10
- from typing import Any
11
-
12
- from .types import DependencyCache
13
-
14
-
15
- @dataclass
16
- class InjectScope:
17
- dependency_cache: DependencyCache
18
- func_astack: AsyncExitStack
19
- request_astack: AsyncExitStack
20
-
21
-
22
- _inject_scope: ContextVar[InjectScope | None] = ContextVar(
23
- "_inject_scope",
24
- default=None,
25
- )
26
-
27
-
28
- @asynccontextmanager
29
- async def push_inject_scope(
30
- dependency_cache: DependencyCache | None = None,
31
- ) -> AsyncIterator[InjectScope]:
32
- async with AsyncExitStack() as stack:
33
- scope = InjectScope(
34
- dependency_cache or {},
35
- stack,
36
- stack,
37
- )
38
- token = _inject_scope.set(scope)
39
-
40
- try:
41
- yield scope
42
- finally:
43
- _inject_scope.reset(token)
44
-
45
-
46
- def current_inject_scope() -> InjectScope | None:
47
- return _inject_scope.get()
48
-
49
-
50
- @asynccontextmanager
51
- async def inside_inject_scope(
52
- *,
53
- new_scope: bool = False,
54
- ) -> AsyncIterator[InjectScope]:
55
- scope = current_inject_scope()
56
-
57
- _ctx: AbstractAsyncContextManager[Any]
58
-
59
- if scope is None or new_scope:
60
- stack = AsyncExitStack()
61
- scope = await stack.enter_async_context(push_inject_scope())
62
-
63
- _ctx = stack
64
- else:
65
- _ctx = nullcontext()
66
-
67
- async with _ctx:
68
- yield scope
69
-
70
-
71
- __all__ = [
72
- "InjectScope",
73
- "current_inject_scope",
74
- "inside_inject_scope",
75
- ]