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
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-11 13:44 +0200
1"""Tests for the `encoders` module."""
3from __future__ import annotations
5import json
6import sys
8import pytest
9from jsonschema import ValidationError, validate
11from griffe import Function, GriffeLoader, Module, Object, temporary_visited_module
14def test_minimal_data_is_enough() -> None:
15 """Test serialization and de-serialization.
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
29 # Also works (but will result in a different type hint).
30 assert Object.from_json(minimal)
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)
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.
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
58 # Also works (but will result in a different type hint).
59 assert Object.from_json(minimal)
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)
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.
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
86 # Also works (but will result in a different type hint).
87 assert Object.from_json(minimal)
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)
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)
100 try:
101 validate(obj, schema)
102 except ValidationError:
103 print(obj["path"]) # noqa: T201
104 raise
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)
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.
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)
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.
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)