Skip to content

Commit 3622b62

Browse files
authored
Merge pull request #31 from taskiq-python/feature/skip-undefined
Skip functions with undefined types.
2 parents 6caa86e + 0b7518b commit 3622b62

File tree

3 files changed

+126
-6
lines changed

3 files changed

+126
-6
lines changed

Diff for: taskiq_dependencies/dependency.py

+12
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,15 @@ def __eq__(self, rhs: object) -> bool:
156156
if not isinstance(rhs, Dependency):
157157
return False
158158
return self._id == rhs._id
159+
160+
def __repr__(self) -> str:
161+
func_name = str(self.dependency)
162+
if self.dependency is not None and hasattr(self.dependency, "__name__"):
163+
func_name = self.dependency.__name__
164+
return (
165+
f"Dependency({func_name}, "
166+
f"use_cache={self.use_cache}, "
167+
f"kwargs={self.kwargs}, "
168+
f"parent={self.parent}"
169+
")"
170+
)

Diff for: taskiq_dependencies/graph.py

+52-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import inspect
22
import sys
3+
import warnings
34
from collections import defaultdict, deque
5+
from pathlib import Path
46
from typing import Any, Callable, Dict, List, Optional, TypeVar, get_type_hints
57

68
from graphlib import TopologicalSorter
@@ -171,19 +173,64 @@ def _build_graph(self) -> None: # noqa: C901
171173
if inspect.isclass(origin):
172174
# If this is a class, we need to get signature of
173175
# an __init__ method.
174-
hints = get_type_hints(origin.__init__)
176+
try:
177+
hints = get_type_hints(origin.__init__)
178+
except NameError:
179+
_, src_lineno = inspect.getsourcelines(origin)
180+
src_file = Path(inspect.getfile(origin)).relative_to(
181+
Path.cwd(),
182+
)
183+
warnings.warn(
184+
"Cannot resolve type hints for "
185+
f"a class {origin.__name__} defined "
186+
f"at {src_file}:{src_lineno}.",
187+
RuntimeWarning,
188+
stacklevel=2,
189+
)
190+
continue
175191
sign = inspect.signature(
176192
origin.__init__,
177193
**signature_kwargs,
178194
)
179195
elif inspect.isfunction(dep.dependency):
180196
# If this is function or an instance of a class, we get it's type hints.
181-
hints = get_type_hints(dep.dependency)
197+
try:
198+
hints = get_type_hints(dep.dependency)
199+
except NameError:
200+
_, src_lineno = inspect.getsourcelines(dep.dependency) # type: ignore
201+
src_file = Path(inspect.getfile(dep.dependency)).relative_to(
202+
Path.cwd(),
203+
)
204+
warnings.warn(
205+
"Cannot resolve type hints for "
206+
f"a function {dep.dependency.__name__} defined "
207+
f"at {src_file}:{src_lineno}.",
208+
RuntimeWarning,
209+
stacklevel=2,
210+
)
211+
continue
182212
sign = inspect.signature(origin, **signature_kwargs) # type: ignore
183213
else:
184-
hints = get_type_hints(
185-
dep.dependency.__call__, # type: ignore
186-
)
214+
try:
215+
hints = get_type_hints(
216+
dep.dependency.__call__, # type: ignore
217+
)
218+
except NameError:
219+
_, src_lineno = inspect.getsourcelines(dep.dependency.__class__)
220+
src_file = Path(
221+
inspect.getfile(dep.dependency.__class__),
222+
).relative_to(
223+
Path.cwd(),
224+
)
225+
cls_name = dep.dependency.__class__.__name__
226+
warnings.warn(
227+
"Cannot resolve type hints for "
228+
f"an object of class {cls_name} defined "
229+
f"at {src_file}:{src_lineno}.",
230+
RuntimeWarning,
231+
stacklevel=2,
232+
)
233+
continue
187234
sign = inspect.signature(origin, **signature_kwargs) # type: ignore
188235

189236
# Now we need to iterate over parameters, to

Diff for: tests/test_graph.py

+62-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import re
22
import uuid
33
from contextlib import asynccontextmanager, contextmanager
4-
from typing import Any, AsyncGenerator, Generator, Generic, Tuple, TypeVar
4+
from typing import (
5+
TYPE_CHECKING,
6+
Any,
7+
AsyncGenerator,
8+
Generator,
9+
Generic,
10+
Tuple,
11+
TypeVar,
12+
)
513

614
import pytest
715

@@ -891,3 +899,56 @@ def target(info: ParamInfo = Depends(inner_dep, use_cache=False)) -> None:
891899
assert info.name == ""
892900
assert info.definition is None
893901
assert info.graph == graph
902+
903+
904+
def test_skip_type_checking_function() -> None:
905+
"""Test if we can skip type only for type checking for the function."""
906+
if TYPE_CHECKING:
907+
908+
class A:
909+
pass
910+
911+
def target(unknown: "A") -> None:
912+
pass
913+
914+
with pytest.warns(RuntimeWarning, match=r"Cannot resolve.*function target.*"):
915+
graph = DependencyGraph(target=target)
916+
with graph.sync_ctx() as ctx:
917+
assert "unknown" not in ctx.resolve_kwargs()
918+
919+
920+
def test_skip_type_checking_class() -> None:
921+
"""Test if we can skip type only for type checking for the function."""
922+
if TYPE_CHECKING:
923+
924+
class A:
925+
pass
926+
927+
class Target:
928+
def __init__(self, unknown: "A") -> None:
929+
pass
930+
931+
with pytest.warns(RuntimeWarning, match=r"Cannot resolve.*class Target.*"):
932+
graph = DependencyGraph(target=Target)
933+
with graph.sync_ctx() as ctx:
934+
assert "unknown" not in ctx.resolve_kwargs()
935+
936+
937+
def test_skip_type_checking_object() -> None:
938+
"""Test if we can skip type only for type checking for the function."""
939+
if TYPE_CHECKING:
940+
941+
class A:
942+
pass
943+
944+
class Target:
945+
def __call__(self, unknown: "A") -> None:
946+
pass
947+
948+
with pytest.warns(
949+
RuntimeWarning,
950+
match=r"Cannot resolve.*object of class Target.*",
951+
):
952+
graph = DependencyGraph(target=Target())
953+
with graph.sync_ctx() as ctx:
954+
assert "unknown" not in ctx.resolve_kwargs()

0 commit comments

Comments
 (0)