Coverage for tests/test_encoders.py: 83.78%

70 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2025-08-11 13:44 +0200

1"""Tests for the `encoders` module.""" 

2 

3from __future__ import annotations 

4 

5import json 

6import sys 

7 

8import pytest 

9from jsonschema import ValidationError, validate 

10 

11from griffe import Function, GriffeLoader, Module, Object, temporary_visited_module 

12 

13 

14def test_minimal_data_is_enough() -> None: 

15 """Test serialization and de-serialization. 

16 

17 This is an end-to-end test that asserts 

18 we can load back a serialized tree and 

19 infer as much data as within the original tree. 

20 """ 

21 loader = GriffeLoader() 

22 module = loader.load("griffe") 

23 minimal = module.as_json(full=False) 

24 full = module.as_json(full=True) 

25 reloaded = Module.from_json(minimal) 

26 assert reloaded.as_json(full=False) == minimal 

27 assert reloaded.as_json(full=True) == full 

28 

29 # Also works (but will result in a different type hint). 

30 assert Object.from_json(minimal) 

31 

32 # Won't work if the JSON doesn't represent the type requested. 

33 with pytest.raises(TypeError, match="provided JSON object is not of type"): 

34 Function.from_json(minimal) 

35 

36 

37# YORE: EOL 3.12: Remove block. 

38# YORE: EOL 3.11: Remove line. 

39@pytest.mark.skipif(sys.version_info < (3, 12), reason="Python less than 3.12 does not have PEP 695 generics") 

40def test_encoding_pep695_generics_without_defaults() -> None: 

41 """Test serialization and de-serialization of PEP 695 generics without defaults. 

42 

43 Defaults are only possible from Python 3.13 onwards. 

44 """ 

45 with temporary_visited_module( 

46 """ 

47 class Class[X: Exception]: pass 

48 def func[**P, T, *R](arg: T, *args: P.args, **kwargs: P.kwargs) -> tuple[*R]: pass 

49 type TA[T: (int, str)] = dict[str, T] 

50 """, 

51 ) as module: 

52 minimal = module.as_json(full=False) 

53 full = module.as_json(full=True) 

54 reloaded = Module.from_json(minimal) 

55 assert reloaded.as_json(full=False) == minimal 

56 assert reloaded.as_json(full=True) == full 

57 

58 # Also works (but will result in a different type hint). 

59 assert Object.from_json(minimal) 

60 

61 # Won't work if the JSON doesn't represent the type requested. 

62 with pytest.raises(TypeError, match="provided JSON object is not of type"): 

63 Function.from_json(minimal) 

64 

65 

66# YORE: EOL 3.12: Remove line. 

67@pytest.mark.skipif(sys.version_info < (3, 13), reason="Python less than 3.13 does not have defaults in PEP 695 generics") # fmt: skip 

68def test_encoding_pep695_generics() -> None: 

69 """Test serialization and de-serialization of PEP 695 generics with defaults. 

70 

71 Defaults are only possible from Python 3.13 onwards. 

72 """ 

73 with temporary_visited_module( 

74 """ 

75 class Class[X: Exception = OSError]: pass 

76 def func[**P, T, *R](arg: T, *args: P.args, **kwargs: P.kwargs) -> tuple[*R]: pass 

77 type TA[T: (int, str) = str] = dict[str, T] 

78 """, 

79 ) as module: 

80 minimal = module.as_json(full=False) 

81 full = module.as_json(full=True) 

82 reloaded = Module.from_json(minimal) 

83 assert reloaded.as_json(full=False) == minimal 

84 assert reloaded.as_json(full=True) == full 

85 

86 # Also works (but will result in a different type hint). 

87 assert Object.from_json(minimal) 

88 

89 # Won't work if the JSON doesn't represent the type requested. 

90 with pytest.raises(TypeError, match="provided JSON object is not of type"): 

91 Function.from_json(minimal) 

92 

93 

94# use this function in test_json_schema to ease schema debugging 

95def _validate(obj: dict, schema: dict) -> None: 

96 if "members" in obj: 

97 for member in obj["members"]: 

98 _validate(member, schema) 

99 

100 try: 

101 validate(obj, schema) 

102 except ValidationError: 

103 print(obj["path"]) # noqa: T201 

104 raise 

105 

106 

107def test_json_schema() -> None: 

108 """Assert that our serialized data matches our JSON schema.""" 

109 loader = GriffeLoader() 

110 module = loader.load("griffe") 

111 loader.resolve_aliases() 

112 data = json.loads(module.as_json(full=True)) 

113 with open("docs/schema.json") as f: # noqa: PTH123 

114 schema = json.load(f) 

115 validate(data, schema) 

116 

117 

118# YORE: EOL 3.12: Remove block. 

119# YORE: EOL 3.11: Remove line. 

120@pytest.mark.skipif(sys.version_info < (3, 12), reason="Python less than 3.12 does not have PEP 695 generics") 

121def test_json_schema_for_pep695_generics_without_defaults() -> None: 

122 """Assert that serialized PEP 695 generics without defaults match our JSON schema. 

123 

124 Defaults are only possible from Python 3.13 onwards. 

125 """ 

126 with temporary_visited_module( 

127 """ 

128 class Class[X: Exception]: pass 

129 def func[**P, T, *R](arg: T, *args: P.args, **kwargs: P.kwargs) -> tuple[*R]: pass 

130 type TA[T: (int, str)] = dict[str, T] 

131 """, 

132 ) as module: 

133 data = json.loads(module.as_json(full=True)) 

134 with open("docs/schema.json") as f: # noqa: PTH123 

135 schema = json.load(f) 

136 validate(data, schema) 

137 

138 

139# YORE: EOL 3.12: Remove line. 

140@pytest.mark.skipif(sys.version_info < (3, 13), reason="Python less than 3.13 does not have defaults in PEP 695 generics") # fmt: skip 

141def test_json_schema_for_pep695_generics() -> None: 

142 """Assert that serialized PEP 695 generics with defaults match our JSON schema. 

143 

144 Defaults are only possible from Python 3.13 onwards. 

145 """ 

146 with temporary_visited_module( 

147 """ 

148 class Class[X: Exception = OSError]: pass 

149 def func[**P, T, *R](arg: T, *args: P.args, **kwargs: P.kwargs) -> tuple[*R]: pass 

150 type TA[T: (int, str) = str] = dict[str, T] 

151 """, 

152 ) as module: 

153 data = json.loads(module.as_json(full=True)) 

154 with open("docs/schema.json") as f: # noqa: PTH123 

155 schema = json.load(f) 

156 validate(data, schema)