Coverage for tests/test_compat.py: 100%
39 statements
« prev ^ index » next coverage.py v7.17.3, created at 2026-11-13 05:19 +0000
« prev ^ index » next coverage.py v7.17.3, created at 2026-11-13 05:19 +0000
1import sys 1abcdefghijklmnopqrst
2import types 1abcdefghijklmnopqrst
3import typing 1abcdefghijklmnopqrst
4from types import NoneType 1abcdefghijklmnopqrst
6import pytest 1abcdefghijklmnopqrst
8from fast_depends._compat import ( 1abcdefghijklmnopqrst
9 eval_type_backport,
10 evaluate_forwardref,
11 is_backport_fixable_error,
12)
14# `X | Y` and `X[Y]` are valid syntax on every supported Python version, so a
15# backport-fixable `TypeError` can only be provoked with a non-type operand.
16NOT_A_TYPE = object() 1abcdefghijklmnopqrst
17BACKPORT_NS = {"NOT_A_TYPE": NOT_A_TYPE, "typing": typing} 1abcdefghijklmnopqrst
20def forwardref(annotation: str) -> typing.ForwardRef: 1abcdefghijklmnopqrst
21 return typing.ForwardRef(annotation, is_argument=False, is_class=True) 1abcdefghijklmnopqrst
24def test_evaluate_forwardref_none() -> None: 1abcdefghijklmnopqrst
25 assert evaluate_forwardref(None) is NoneType 1abcdefghijklmnopqrst
28def test_evaluate_forwardref_string() -> None: 1abcdefghijklmnopqrst
29 assert evaluate_forwardref("int", {"int": int}, {}) is int 1abcdefghijklmnopqrst
32def test_evaluate_forwardref_tolerates_unresolvable_name() -> None: 1abcdefghijklmnopqrst
33 # The whole point of the helper: an unresolvable reference is returned as is
34 # instead of raising `NameError`.
35 resolved = evaluate_forwardref("Undefined", {}, {}) 1abcdefghijklmnopqrst
36 assert isinstance(resolved, typing.ForwardRef) 1abcdefghijklmnopqrst
37 assert resolved.__forward_arg__ == "Undefined" 1abcdefghijklmnopqrst
40@pytest.mark.parametrize( 1abcdefghijklmnopqrst
41 ("message", "expected"),
42 (
43 pytest.param(
44 "unsupported operand type(s) for |: 'object' and 'NoneType'",
45 True,
46 id="union-syntax",
47 ),
48 pytest.param(
49 "'object' object is not subscriptable",
50 True,
51 id="subscription-syntax",
52 ),
53 pytest.param("some other problem", False, id="unrelated"),
54 ),
55)
56def test_is_backport_fixable_error(message: str, expected: bool) -> None: 1abcdefghijklmnopqrst
57 assert is_backport_fixable_error(TypeError(message)) is expected 1abcdefghijklmnopqrst
60def test_eval_type_backport_reraises_unrelated_type_error() -> None: 1abcdefghijklmnopqrst
61 with pytest.raises(TypeError, match="requires a single type"): 1abcdefghijklmnopqrst
62 eval_type_backport(forwardref("typing.Optional[int, str]"), BACKPORT_NS, {}) 1abcdefghijklmnopqrst
65@pytest.mark.parametrize( 1abcdefghijklmnopqrst
66 "annotation",
67 (
68 pytest.param("NOT_A_TYPE | None", id="union-syntax"),
69 pytest.param("NOT_A_TYPE[int]", id="subscription-syntax"),
70 ),
71)
72def test_eval_type_backport_without_package_installed( 1abcdefghijklmnopqrst
73 annotation: str,
74 monkeypatch: pytest.MonkeyPatch,
75) -> None:
76 # A `None` entry in `sys.modules` makes the import fail deterministically,
77 # whether or not `eval_type_backport` is actually installed.
78 monkeypatch.setitem(sys.modules, "eval_type_backport", None) 1abcdefghijklmnopqrst
80 with pytest.raises(TypeError, match="install the `eval_type_backport` package"): 1abcdefghijklmnopqrst
81 eval_type_backport(forwardref(annotation), BACKPORT_NS, {}) 1abcdefghijklmnopqrst
84def test_eval_type_backport_delegates_to_package( 1abcdefghijklmnopqrst
85 monkeypatch: pytest.MonkeyPatch,
86) -> None:
87 calls = [] 1abcdefghijklmnopqrst
89 def fake_backport( 1abcdefghijklmnopqrst
90 value: typing.Any,
91 globalns: typing.Any,
92 localns: typing.Any,
93 try_default: bool,
94 ) -> str:
95 calls.append(try_default) 1abcdefghijklmnopqrst
96 return "delegated" 1abcdefghijklmnopqrst
98 stub = types.ModuleType("eval_type_backport") 1abcdefghijklmnopqrst
99 stub.eval_type_backport = fake_backport # type: ignore[attr-defined] 1abcdefghijklmnopqrst
100 monkeypatch.setitem(sys.modules, "eval_type_backport", stub) 1abcdefghijklmnopqrst
102 assert eval_type_backport(forwardref("NOT_A_TYPE | None"), BACKPORT_NS, {}) == ( 1abcdefghijklmnopqrst
103 "delegated"
104 )
105 assert calls == [False] 1abcdefghijklmnopqrst