Skip to content

Commit 7ac0adf

Browse files
committed
Merge branch 'release/1.2.0'
2 parents 37c0872 + d5d5f2e commit 7ac0adf

8 files changed

Lines changed: 133 additions & 6 deletions

File tree

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,31 @@ with graph.sync_ctx({DefaultDep: DefaultDep()}) as ctx:
9797
```
9898

9999
You can run this code. It will resolve dd dependency into a `DefaultDep` variable you provide.
100+
101+
102+
## Getting parameters information
103+
104+
If you want to get the information about how this dependency was specified,
105+
you can use special class `ParamInfo` for that.
106+
107+
```python
108+
from taskiq_dependencies import Depends, DependencyGraph, ParamInfo
109+
110+
111+
def dependency(info: ParamInfo = Depends()) -> str:
112+
assert info.name == "dd"
113+
return info.name
114+
115+
def target_func(dd: str = Depends(dependency)):
116+
print(dd)
117+
return 1
118+
119+
120+
graph = DependencyGraph(target_func)
121+
122+
with graph.sync_ctx() as ctx:
123+
print(ctx.resolve_kwargs())
124+
125+
```
126+
127+
The ParamInfo has the information about name and parameters signature. It's useful if you want to create a dependency that changes based on parameter name, or signature.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "taskiq-dependencies"
3-
version = "1.1.2"
3+
version = "1.2.0"
44
description = "FastAPI like dependency injection implementation"
55
authors = ["Pavel Kirilin <win10@list.ru>"]
66
readme = "README.md"

taskiq_dependencies/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
"""
99
from taskiq_dependencies.dependency import Depends
1010
from taskiq_dependencies.graph import DependencyGraph
11+
from taskiq_dependencies.utils import ParamInfo
1112

12-
__all__ = ["DependencyGraph", "Depends"]
13+
__all__ = ["DependencyGraph", "Depends", "ParamInfo"]

taskiq_dependencies/ctx.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from copy import copy
44
from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional
55

6+
from taskiq_dependencies.utils import ParamInfo
7+
68
if TYPE_CHECKING:
79
from taskiq_dependencies.graph import DependencyGraph # pragma: no cover
810

@@ -62,6 +64,11 @@ def traverse_deps( # noqa: C901, WPS210
6264
# we skip it.
6365
if subdep.dependency is None:
6466
continue
67+
# If the user want to get ParamInfo,
68+
# we get declaration of the current dependency.
69+
if subdep.dependency == ParamInfo:
70+
kwargs[subdep.param_name] = ParamInfo(dep.param_name, dep.signature)
71+
continue
6572
if subdep.use_cache:
6673
# If this dependency can be calculated, using cache,
6774
# we try to get it from cache.
@@ -82,7 +89,12 @@ def traverse_deps( # noqa: C901, WPS210
8289

8390
# We don't want to calculate least function,
8491
# Because it's a target function.
85-
if index < len(self.graph.ordered_deps) - 1:
92+
if ( # noqa: WPS337
93+
index < len(self.graph.ordered_deps) - 1
94+
# We skip all ParamInfo dependencies,
95+
# because we calculate them when needed.
96+
and dep.dependency != ParamInfo
97+
):
8698
user_kwargs = dep.kwargs
8799
user_kwargs.update(kwargs)
88100
cache[dep.dependency] = yield dep.dependency(**user_kwargs)

taskiq_dependencies/dependency.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import inspect
12
import uuid
23
from typing import ( # noqa: WPS235
34
Any,
@@ -107,12 +108,14 @@ def __init__( # noqa: WPS234
107108
*,
108109
use_cache: bool = True,
109110
kwargs: Optional[Dict[str, Any]] = None,
111+
signature: Optional[inspect.Parameter] = None,
110112
) -> None:
111113
self._id = uuid.uuid4()
112114
self.dependency = dependency
113115
self.use_cache = use_cache
114116
self.param_name = ""
115117
self.kwargs = kwargs or {}
118+
self.signature = signature
116119

117120
def __hash__(self) -> int:
118121
return hash(self._id)

taskiq_dependencies/graph.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,13 @@ def _build_graph(self) -> None: # noqa: C901, WPS210
100100
# If this is a class, we need to get signature of
101101
# an __init__ method.
102102
hints = get_type_hints(dep.dependency.__init__) # noqa: WPS609
103-
else:
104-
# If this is function, we get it's type hints.
103+
elif inspect.isfunction(dep.dependency):
104+
# If this is function or an instance of a class, we get it's type hints.
105105
hints = get_type_hints(dep.dependency)
106+
else:
107+
hints = get_type_hints(
108+
dep.dependency.__call__, # type: ignore # noqa: WPS609
109+
)
106110

107111
# Now we need to iterate over parameters, to
108112
# find all parameters, that have TaskiqDepends as it's
@@ -119,6 +123,7 @@ def _build_graph(self) -> None: # noqa: C901, WPS210
119123
default_value = Dependency(
120124
dependency=default_value.dependency,
121125
use_cache=default_value.use_cache,
126+
signature=param,
122127
)
123128

124129
# We check, that default value is an instance of
@@ -158,6 +163,7 @@ def _build_graph(self) -> None: # noqa: C901, WPS210
158163
dependency_func,
159164
use_cache=default_value.use_cache,
160165
kwargs=default_value.kwargs,
166+
signature=param,
161167
)
162168
# Also we set the parameter name,
163169
# it will help us in future when

taskiq_dependencies/utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import inspect
2+
from typing import Optional
3+
4+
5+
class ParamInfo:
6+
"""
7+
Parameter information.
8+
9+
This class helps you to get information,
10+
about how the current dependency was specified.
11+
12+
If there's no dependant function, the name will be an empty string
13+
and the definition will be None.
14+
"""
15+
16+
def __init__(
17+
self,
18+
name: str,
19+
signature: Optional[inspect.Parameter] = None,
20+
) -> None:
21+
self.name = name
22+
self.definition = signature
23+
24+
def __repr__(self) -> str:
25+
return f"ParamInfo<name={self.name}>"

tests/test_graph.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66

7-
from taskiq_dependencies import DependencyGraph, Depends
7+
from taskiq_dependencies import DependencyGraph, Depends, ParamInfo
88

99

1010
@pytest.mark.anyio
@@ -258,3 +258,55 @@ def __init__(self, dep=Depends()) -> None: # type: ignore
258258

259259
with pytest.raises(ValueError):
260260
DependencyGraph(Target)
261+
262+
263+
def test_get_param_info() -> None:
264+
"""Tests that param info resolved correctly."""
265+
266+
def dep(info: ParamInfo = Depends()) -> ParamInfo:
267+
return info
268+
269+
def target(my_test_param: ParamInfo = Depends(dep)) -> None:
270+
return None
271+
272+
with DependencyGraph(target=target).sync_ctx() as g:
273+
kwargs = g.resolve_kwargs()
274+
275+
info: ParamInfo = kwargs["my_test_param"]
276+
assert info.name == "my_test_param"
277+
assert info.definition
278+
assert info.definition.annotation == ParamInfo
279+
280+
281+
def test_param_info_no_dependant() -> None:
282+
"""Tests that if ParamInfo is used on the target, no error is raised."""
283+
284+
def target(info: ParamInfo = Depends()) -> None:
285+
return None
286+
287+
with DependencyGraph(target=target).sync_ctx() as g:
288+
kwargs = g.resolve_kwargs()
289+
290+
info: ParamInfo = kwargs["info"]
291+
assert info.name == ""
292+
assert info.definition is None
293+
294+
295+
def test_class_based_dependencies() -> None:
296+
"""Tests that if ParamInfo is used on the target, no error is raised."""
297+
298+
class TeClass:
299+
def __init__(self, return_val: str) -> None:
300+
self.return_val = return_val
301+
302+
def __call__(self) -> str:
303+
return self.return_val
304+
305+
def target(class_val: str = Depends(TeClass("tval"))) -> None:
306+
return None
307+
308+
with DependencyGraph(target=target).sync_ctx() as g:
309+
kwargs = g.resolve_kwargs()
310+
311+
info: str = kwargs["class_val"]
312+
assert info == "tval"

0 commit comments

Comments
 (0)