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

1import sys 1abcdefghijklmnopqrst

2import types 1abcdefghijklmnopqrst

3import typing 1abcdefghijklmnopqrst

4from types import NoneType 1abcdefghijklmnopqrst

5 

6import pytest 1abcdefghijklmnopqrst

7 

8from fast_depends._compat import ( 1abcdefghijklmnopqrst

9 eval_type_backport, 

10 evaluate_forwardref, 

11 is_backport_fixable_error, 

12) 

13 

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

18 

19 

20def forwardref(annotation: str) -> typing.ForwardRef: 1abcdefghijklmnopqrst

21 return typing.ForwardRef(annotation, is_argument=False, is_class=True) 1abcdefghijklmnopqrst

22 

23 

24def test_evaluate_forwardref_none() -> None: 1abcdefghijklmnopqrst

25 assert evaluate_forwardref(None) is NoneType 1abcdefghijklmnopqrst

26 

27 

28def test_evaluate_forwardref_string() -> None: 1abcdefghijklmnopqrst

29 assert evaluate_forwardref("int", {"int": int}, {}) is int 1abcdefghijklmnopqrst

30 

31 

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

38 

39 

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

58 

59 

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

63 

64 

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

79 

80 with pytest.raises(TypeError, match="install the `eval_type_backport` package"): 1abcdefghijklmnopqrst

81 eval_type_backport(forwardref(annotation), BACKPORT_NS, {}) 1abcdefghijklmnopqrst

82 

83 

84def test_eval_type_backport_delegates_to_package( 1abcdefghijklmnopqrst

85 monkeypatch: pytest.MonkeyPatch, 

86) -> None: 

87 calls = [] 1abcdefghijklmnopqrst

88 

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

97 

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

101 

102 assert eval_type_backport(forwardref("NOT_A_TYPE | None"), BACKPORT_NS, {}) == ( 1abcdefghijklmnopqrst

103 "delegated" 

104 ) 

105 assert calls == [False] 1abcdefghijklmnopqrst