fastapi-injected 0.3.1__tar.gz → 0.3.3__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.

Potentially problematic release.


This version of fastapi-injected might be problematic. Click here for more details.

Files changed (65) hide show
  1. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/PKG-INFO +2 -14
  2. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/README.md +1 -13
  3. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/__init__.py +0 -3
  4. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_bind.py +9 -8
  5. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_given.py +2 -1
  6. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/deps.py +1 -2
  7. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/types.py +1 -2
  8. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/pyproject.toml +1 -1
  9. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_bind.py +37 -3
  10. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/uv.lock +1 -1
  11. fastapi_injected-0.3.1/fastapi_injected/_params.py +0 -19
  12. fastapi_injected-0.3.1/tests/_typing/params.py +0 -30
  13. fastapi_injected-0.3.1/tests/test_params.py +0 -78
  14. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/.github/dependabot.yml +0 -0
  15. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/.github/workflows/automerge.yml +0 -0
  16. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/.github/workflows/lint.yml +0 -0
  17. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/.github/workflows/publish.yml +0 -0
  18. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/.github/workflows/test.yml +0 -0
  19. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/.gitignore +0 -0
  20. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/.pre-commit-config.yaml +0 -0
  21. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/LICENSE +0 -0
  22. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_cache.py +0 -0
  23. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_dataclass.py +0 -0
  24. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_deps_tp.py +0 -0
  25. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_fastapi_lifecycle.py +0 -0
  26. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_injected.py +0 -0
  27. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_overrides.py +0 -0
  28. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/_rlock.py +0 -0
  29. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/inject.py +0 -0
  30. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/overrides.py +0 -0
  31. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/resolve.py +0 -0
  32. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/scope.py +0 -0
  33. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/fastapi_injected/sign.py +0 -0
  34. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/__init__.py +0 -0
  35. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/__init__.py +0 -0
  36. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/annotations.py +0 -0
  37. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/arg.py +0 -0
  38. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/bind.py +0 -0
  39. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/dataclass.py +0 -0
  40. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/deps.py +0 -0
  41. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/errors.py +0 -0
  42. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/given.py +0 -0
  43. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/inject.py +0 -0
  44. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/injected.py +0 -0
  45. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/integration.py +0 -0
  46. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/markers.py +0 -0
  47. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/overrides.py +0 -0
  48. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/resolve.py +0 -0
  49. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/_typing/scope.py +0 -0
  50. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/deps.py +0 -0
  51. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/ext/__init__.py +0 -0
  52. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/ext/test_pydantic_ai.py +0 -0
  53. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_cache.py +0 -0
  54. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_concurrency.py +0 -0
  55. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_dataclass.py +0 -0
  56. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_dependant_cache.py +0 -0
  57. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_errors.py +0 -0
  58. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_fastapi.py +0 -0
  59. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_given.py +0 -0
  60. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_inject.py +0 -0
  61. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_injected.py +0 -0
  62. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_overrides.py +0 -0
  63. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_resolve.py +0 -0
  64. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_rlock.py +0 -0
  65. {fastapi_injected-0.3.1 → fastapi_injected-0.3.3}/tests/test_scope.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastapi-injected
3
- Version: 0.3.1
3
+ Version: 0.3.3
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>
@@ -253,18 +253,6 @@ class Config(MakeDataclass, frozen=True, kw_only=True):
253
253
  retries: int = 3
254
254
  ```
255
255
 
256
- The same is true of what holds a dependency. FastAPI's `Depends` and `Security` are frozen dataclasses, so they hash by field — and `Security(get_user, scopes=["items:read"])`, the documented spelling, holds a list, which makes the whole annotation unhashable and fails as a bare `TypeError` from `typing`. `fastapi_injected` ships both as `MakeDataclass` subclasses, which is the same class in every way that matters to FastAPI, and one that can be cached:
257
-
258
- ```python
259
- from fastapi_injected import Security, resolve
260
-
261
- type CurrentUser = Annotated[User, Security(get_user, scopes=["items:read"])]
262
-
263
- await resolve(CurrentUser)
264
- ```
265
-
266
- Scopes are kept as a tuple, so the same scopes written as another list stay one dependency. Everything the package builds internally is held this way too, so a dependency object that cannot be hashed by fields no longer decides whether the annotation around it can be.
267
-
268
256
  ### When resolution fails
269
257
 
270
258
  A dependency can fail to resolve for the same reasons it would in a route — a missing header, a query parameter that does not validate. `DependencyResolutionError` carries those errors in the shape pydantic produced them, and turns into the response FastAPI would have returned:
@@ -369,7 +357,7 @@ Everything the package supports is importable from `fastapi_injected` itself, an
369
357
 
370
358
  | | |
371
359
  | --- | --- |
372
- | Markers | `Dep`, `DepFactory`, `DepOf`, `Arg`, `Given`, `Injected`, `Depends`, `Security` |
360
+ | Markers | `Dep`, `DepFactory`, `DepOf`, `Arg`, `Given`, `Injected` |
373
361
  | Resolving | `inject`, `resolve`, `bind_deps`, `signature_with_deps`, `remap_dep_args`, `clear_dependant_cache` |
374
362
  | Scopes and overrides | `InjectScope`, `push_inject_scope`, `inside_inject_scope`, `push_overrides`, `Overrides`, `OverridesProvider`, `ValueOverride`, `FactoryOverride` |
375
363
  | Building on top | `MakeDataclass`, `MakeInjected`, `HasDependsHook`, `ArgMarker`, `is_arg`, `is_dep`, `unwrap_dep_tp`, `unwrap_dep_dependency` |
@@ -232,18 +232,6 @@ class Config(MakeDataclass, frozen=True, kw_only=True):
232
232
  retries: int = 3
233
233
  ```
234
234
 
235
- The same is true of what holds a dependency. FastAPI's `Depends` and `Security` are frozen dataclasses, so they hash by field — and `Security(get_user, scopes=["items:read"])`, the documented spelling, holds a list, which makes the whole annotation unhashable and fails as a bare `TypeError` from `typing`. `fastapi_injected` ships both as `MakeDataclass` subclasses, which is the same class in every way that matters to FastAPI, and one that can be cached:
236
-
237
- ```python
238
- from fastapi_injected import Security, resolve
239
-
240
- type CurrentUser = Annotated[User, Security(get_user, scopes=["items:read"])]
241
-
242
- await resolve(CurrentUser)
243
- ```
244
-
245
- Scopes are kept as a tuple, so the same scopes written as another list stay one dependency. Everything the package builds internally is held this way too, so a dependency object that cannot be hashed by fields no longer decides whether the annotation around it can be.
246
-
247
235
  ### When resolution fails
248
236
 
249
237
  A dependency can fail to resolve for the same reasons it would in a route — a missing header, a query parameter that does not validate. `DependencyResolutionError` carries those errors in the shape pydantic produced them, and turns into the response FastAPI would have returned:
@@ -348,7 +336,7 @@ Everything the package supports is importable from `fastapi_injected` itself, an
348
336
 
349
337
  | | |
350
338
  | --- | --- |
351
- | Markers | `Dep`, `DepFactory`, `DepOf`, `Arg`, `Given`, `Injected`, `Depends`, `Security` |
339
+ | Markers | `Dep`, `DepFactory`, `DepOf`, `Arg`, `Given`, `Injected` |
352
340
  | Resolving | `inject`, `resolve`, `bind_deps`, `signature_with_deps`, `remap_dep_args`, `clear_dependant_cache` |
353
341
  | Scopes and overrides | `InjectScope`, `push_inject_scope`, `inside_inject_scope`, `push_overrides`, `Overrides`, `OverridesProvider`, `ValueOverride`, `FactoryOverride` |
354
342
  | Building on top | `MakeDataclass`, `MakeInjected`, `HasDependsHook`, `ArgMarker`, `is_arg`, `is_dep`, `unwrap_dep_tp`, `unwrap_dep_dependency` |
@@ -4,7 +4,6 @@ from ._deps_tp import is_arg, is_dep, unwrap_dep_dependency, unwrap_dep_tp
4
4
  from ._fastapi_lifecycle import MissingDependencyCacheError, add_injected_scope, init_inject_scope
5
5
  from ._given import Given
6
6
  from ._injected import MakeInjected
7
- from ._params import Depends, Security
8
7
  from .deps import (
9
8
  DependencyResolutionError,
10
9
  HasDependsHook,
@@ -25,7 +24,6 @@ __all__ = [
25
24
  "DepFactory",
26
25
  "DepOf",
27
26
  "DependencyResolutionError",
28
- "Depends",
29
27
  "FactoryOverride",
30
28
  "Given",
31
29
  "HasDependsHook",
@@ -38,7 +36,6 @@ __all__ = [
38
36
  "NotADependencyError",
39
37
  "Overrides",
40
38
  "OverridesProvider",
41
- "Security",
42
39
  "UnboundDepArgsError",
43
40
  "UnboundScopeError",
44
41
  "ValueOverride",
@@ -1,13 +1,12 @@
1
1
  import inspect
2
2
  from collections.abc import Callable, Sequence
3
3
  from functools import wraps
4
- from typing import Annotated, Any, Concatenate, overload
4
+ from typing import Annotated, Any, Concatenate, get_origin, overload
5
5
 
6
- from fastapi import params
7
6
  from fastapi.dependencies.utils import get_typed_signature
7
+ from fastapi.params import Depends
8
8
 
9
- from ._deps_tp import is_dep
10
- from ._params import Depends
9
+ from ._deps_tp import unwrap_tp
11
10
  from .sign import update_func_sign
12
11
  from .types import AsyncFunc, DepOf, Func
13
12
 
@@ -34,13 +33,15 @@ def _dep_arg_index(name: str, /) -> int | None:
34
33
  return int(index) if index.isdigit() else None
35
34
 
36
35
 
36
+ def _is_annotation(tp: Any, /) -> bool:
37
+ return get_origin(unwrap_tp(tp)) is Annotated
38
+
39
+
37
40
  def _dep_annotation(dep: DepOf[Any] | Callable[..., Any], /) -> Any:
38
- if isinstance(dep, params.Depends):
41
+ if isinstance(dep, Depends):
39
42
  return Annotated[Any, dep]
40
43
 
41
- # a marker is an annotation already, while a callable is a dependency the same way
42
- # `resolve` takes one - anything else is left as it was written
43
- if is_dep(dep) or not callable(dep):
44
+ if _is_annotation(dep) or not callable(dep):
44
45
  return dep
45
46
 
46
47
  return Annotated[Any, Depends(dep)]
@@ -1,7 +1,8 @@
1
1
  from typing import Annotated, Any, cast
2
2
 
3
+ from fastapi import Depends
4
+
3
5
  from ._dataclass import MakeDataclass
4
- from ._params import Depends
5
6
  from .types import DepOf
6
7
 
7
8
 
@@ -4,13 +4,12 @@ from contextlib import AsyncExitStack
4
4
  from functools import lru_cache, wraps
5
5
  from typing import Annotated, Any, Literal, Protocol, cast, overload, runtime_checkable
6
6
 
7
- from fastapi import params
7
+ from fastapi import Depends, params
8
8
  from fastapi.dependencies.models import Dependant
9
9
  from fastapi.dependencies.utils import get_dependant, get_typed_signature, solve_dependencies
10
10
  from fastapi.exceptions import RequestValidationError
11
11
 
12
12
  from ._deps_tp import is_dep, unwrap_tp
13
- from ._params import Depends
14
13
  from .scope import InjectScope
15
14
  from .sign import prepare_sign, update_func_sign
16
15
  from .types import Coro, DependencyCache, HasSignature
@@ -2,11 +2,10 @@ import inspect
2
2
  from collections.abc import AsyncIterator, Callable, Coroutine, Iterator, Mapping
3
3
  from typing import TYPE_CHECKING, Annotated, Any, Protocol, TypeVar, runtime_checkable
4
4
 
5
+ from fastapi import Depends
5
6
  from fastapi.types import DependencyCacheKey
6
7
  from typing_extensions import TypeForm, sentinel
7
8
 
8
- from ._params import Depends
9
-
10
9
  if TYPE_CHECKING:
11
10
  Injected: Any = object()
12
11
  else:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fastapi-injected"
3
- version = "0.3.1"
3
+ version = "0.3.3"
4
4
  description = "Yet another library to reuse fastapi dependency injection"
5
5
  authors = [
6
6
  { name = "Yurii Karabas", email = "1998uriyyo@gmail.com" },
@@ -2,9 +2,19 @@ from dataclasses import dataclass
2
2
  from typing import Annotated, Any
3
3
 
4
4
  import pytest
5
- from fastapi import Depends
6
-
7
- from fastapi_injected import Given, bind_deps, inject, push_inject_scope, push_overrides, resolve, signature_with_deps
5
+ from fastapi import Depends, FastAPI, Header
6
+ from fastapi.testclient import TestClient
7
+
8
+ from fastapi_injected import (
9
+ Given,
10
+ bind_deps,
11
+ init_inject_scope,
12
+ inject,
13
+ push_inject_scope,
14
+ push_overrides,
15
+ resolve,
16
+ signature_with_deps,
17
+ )
8
18
  from fastapi_injected._bind import UnboundDepArgsError, dep_arg_name, remap_dep_args, take_dep_args
9
19
 
10
20
  pytestmark = pytest.mark.asyncio
@@ -87,6 +97,30 @@ async def test_a_dependency_can_be_bound_however_it_is_written(dep):
87
97
  assert await resolve(bind_deps(describe, Given(Doc()), dep)) == "readme:admin"
88
98
 
89
99
 
100
+ TitleHeader = Annotated[str, Header(alias="x-title")]
101
+
102
+
103
+ async def titled(title: str, role: Role) -> str:
104
+ return f"{title}:{role.name}"
105
+
106
+
107
+ async def test_a_request_marker_is_bound_as_written() -> None:
108
+ sign = signature_with_deps(titled, TitleHeader, RoleDep)
109
+
110
+ assert next(iter(sign.parameters.values())).annotation == TitleHeader
111
+
112
+
113
+ async def test_a_request_marker_resolves_from_the_request() -> None:
114
+ app = FastAPI(dependencies=[Depends(init_inject_scope)])
115
+
116
+ @app.get("/marker")
117
+ async def _route(described: Annotated[str, Depends(bind_deps(titled, TitleHeader, RoleDep))]) -> str:
118
+ return described
119
+
120
+ with TestClient(app) as client:
121
+ assert client.get("/marker", headers={"x-title": "readme"}).json() == "readme:admin"
122
+
123
+
90
124
  async def test_signature_with_deps_names_the_bound_parameters() -> None:
91
125
  sign = signature_with_deps(describe, Given(Doc()), RoleDep)
92
126
 
@@ -186,7 +186,7 @@ wheels = [
186
186
 
187
187
  [[package]]
188
188
  name = "fastapi-injected"
189
- version = "0.3.1"
189
+ version = "0.3.3"
190
190
  source = { editable = "." }
191
191
  dependencies = [
192
192
  { name = "fastapi" },
@@ -1,19 +0,0 @@
1
- from fastapi import params
2
-
3
- from ._dataclass import MakeDataclass
4
-
5
-
6
- class Depends(MakeDataclass, params.Depends, frozen=True):
7
- pass
8
-
9
-
10
- class Security(Depends, params.Security, frozen=True):
11
- def __post_init__(self) -> None:
12
- if self.scopes is not None:
13
- object.__setattr__(self, "scopes", tuple(self.scopes))
14
-
15
-
16
- __all__ = [
17
- "Depends",
18
- "Security",
19
- ]
@@ -1,30 +0,0 @@
1
- from typing import Annotated
2
-
3
- from fastapi import params
4
-
5
- from fastapi_injected import Depends, Security
6
-
7
- from .deps import (
8
- ContextState,
9
- ctx_dep,
10
- is_assignable_to,
11
- is_subtype_of,
12
- static_assert,
13
- )
14
-
15
- # they are the fastapi params they wrap, so anything taking one takes these
16
- static_assert(is_subtype_of(Depends, params.Depends))
17
- static_assert(is_subtype_of(Security, params.Security))
18
- static_assert(is_subtype_of(Security, Depends))
19
- static_assert(is_assignable_to(Annotated[ContextState, Security(ctx_dep, scopes=["me"])], ContextState))
20
-
21
-
22
- def _params() -> None:
23
- Depends()
24
- Depends(ctx_dep, use_cache=False)
25
- Security(ctx_dep, scopes=["me"], use_cache=False)
26
-
27
-
28
- def _negatives() -> None:
29
- Depends(use_cache="yes") # type: ignore[ty:invalid-argument-type]
30
- Security(ctx_dep, scopes=1) # type: ignore[ty:invalid-argument-type]
@@ -1,78 +0,0 @@
1
- from typing import Annotated
2
-
3
- import pytest
4
- from fastapi import params
5
- from fastapi.security import SecurityScopes
6
-
7
- from fastapi_injected import Depends, Security, is_dep, push_inject_scope, resolve
8
-
9
-
10
- async def scopes_dep(scopes: SecurityScopes) -> list[str]:
11
- return scopes.scopes
12
-
13
-
14
- class Unhashable:
15
- __hash__ = None # type: ignore[ty:invalid-assignment]
16
-
17
- async def __call__(self) -> str:
18
- return "value"
19
-
20
-
21
- def test_they_are_fastapi_params():
22
- assert isinstance(Depends(scopes_dep), params.Depends)
23
- assert isinstance(Security(scopes_dep), params.Security)
24
- assert isinstance(Security(scopes_dep), params.Depends)
25
-
26
-
27
- def test_scopes_are_written_as_documented():
28
- security = Security(scopes_dep, scopes=["items:read"])
29
-
30
- assert security.scopes == ("items:read",)
31
- assert is_dep(Annotated[list[str], security])
32
-
33
-
34
- def test_equal_params_hash_equally():
35
- assert hash(Depends(scopes_dep)) == hash(Depends(scopes_dep))
36
- assert Security(scopes_dep, scopes=["me"]) == Security(scopes_dep, scopes=["me"])
37
- assert hash(Security(scopes_dep, scopes=["me"])) == hash(Security(scopes_dep, scopes=("me",)))
38
-
39
-
40
- def test_a_scoped_annotation_can_be_hashed():
41
- # the annotation is the key a dependant is cached by, and a list of scopes used to break it
42
- assert hash(Annotated[list[str], Security(scopes_dep, scopes=["items:read", "me"])])
43
-
44
- with pytest.raises(TypeError, match="unhashable type: 'list'"):
45
- hash(Annotated[list[str], params.Security(scopes_dep, scopes=["items:read", "me"])])
46
-
47
-
48
- def test_unhashable_dependencies_fall_back_to_identity():
49
- depends = Depends(Unhashable())
50
-
51
- assert hash(depends) == object.__hash__(depends)
52
- assert hash(Annotated[str, depends])
53
-
54
-
55
- @pytest.mark.asyncio
56
- async def test_a_scoped_dependency_resolves():
57
- scoped = Annotated[list[str], Security(scopes_dep, scopes=["items:read", "me"])]
58
-
59
- assert await resolve(scoped) == ["items:read", "me"]
60
-
61
-
62
- @pytest.mark.asyncio
63
- async def test_equal_params_are_one_dependency():
64
- calls = 0
65
-
66
- async def counted() -> int:
67
- nonlocal calls
68
- calls += 1
69
- return calls
70
-
71
- async with push_inject_scope():
72
- assert await resolve(Annotated[int, Security(counted, scopes=["me"])]) == 1
73
- # the same scopes written as another list are the same dependency, so it is cached
74
- assert await resolve(Annotated[int, Security(counted, scopes=["me"])]) == 1
75
- # different scopes are not
76
- assert await resolve(Annotated[int, Security(counted, scopes=["other"])]) == 2
77
-
78
- assert calls == 2