Coverage for tests/test_extensions.py: 100.00%
62 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 `extensions` module."""
3from __future__ import annotations
5import sys
6from pathlib import Path
7from typing import TYPE_CHECKING, Any
9import pytest
11from griffe import Extension, load_extensions, temporary_visited_module
13if TYPE_CHECKING:
14 import ast
16 from griffe import Attribute, Class, Function, Module, Object, ObjectNode, TypeAlias
19class ExtensionTest(Extension): # noqa: D101
20 def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: D107
21 super().__init__()
22 self.records: list[str] = []
23 self.args = args
24 self.kwargs = kwargs
26 def on_attribute_instance(self, *, node: ast.AST | ObjectNode, attr: Attribute, **kwargs: Any) -> None: # noqa: D102,ARG002
27 self.records.append("on_attribute_instance")
29 def on_attribute_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
30 self.records.append("on_attribute_node")
32 def on_class_instance(self, *, node: ast.AST | ObjectNode, cls: Class, **kwargs: Any) -> None: # noqa: D102,ARG002
33 self.records.append("on_class_instance")
35 def on_class_members(self, *, node: ast.AST | ObjectNode, cls: Class, **kwargs: Any) -> None: # noqa: D102,ARG002
36 self.records.append("on_class_members")
38 def on_class_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
39 self.records.append("on_class_node")
41 def on_function_instance(self, *, node: ast.AST | ObjectNode, func: Function, **kwargs: Any) -> None: # noqa: D102,ARG002
42 self.records.append("on_function_instance")
44 def on_function_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
45 self.records.append("on_function_node")
47 def on_instance(self, *, node: ast.AST | ObjectNode, obj: Object, **kwargs: Any) -> None: # noqa: D102,ARG002
48 self.records.append("on_instance")
50 def on_members(self, *, node: ast.AST | ObjectNode, obj: Object, **kwargs: Any) -> None: # noqa: D102,ARG002
51 self.records.append("on_members")
53 def on_module_instance(self, *, node: ast.AST | ObjectNode, mod: Module, **kwargs: Any) -> None: # noqa: D102,ARG002
54 self.records.append("on_module_instance")
56 def on_module_members(self, *, node: ast.AST | ObjectNode, mod: Module, **kwargs: Any) -> None: # noqa: D102,ARG002
57 self.records.append("on_module_members")
59 def on_module_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
60 self.records.append("on_module_node")
62 def on_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
63 self.records.append("on_node")
65 def on_type_alias_instance(self, *, node: ast.AST | ObjectNode, type_alias: TypeAlias, **kwargs: Any) -> None: # noqa: D102,ARG002
66 self.records.append("on_type_alias_instance")
68 def on_type_alias_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
69 self.records.append("on_type_alias_node")
72@pytest.mark.parametrize(
73 "extension",
74 [
75 # With module path.
76 "tests.test_extensions",
77 {"tests.test_extensions": {"option": 0}},
78 # With extension path.
79 "tests.test_extensions.ExtensionTest",
80 {"tests.test_extensions.ExtensionTest": {"option": 0}},
81 # With filepath.
82 "tests/test_extensions.py",
83 {"tests/test_extensions.py": {"option": 0}},
84 # With filepath and extension name.
85 "tests/test_extensions.py:ExtensionTest",
86 {"tests/test_extensions.py:ExtensionTest": {"option": 0}},
87 # With instance.
88 ExtensionTest(option=0),
89 # With class.
90 ExtensionTest,
91 # With absolute paths (esp. important to test for Windows).
92 Path("tests/test_extensions.py").absolute().as_posix(),
93 Path("tests/test_extensions.py:ExtensionTest").absolute().as_posix(),
94 ],
95)
96def test_loading_extensions(extension: str | dict[str, dict[str, Any]] | Extension | type[Extension]) -> None:
97 """Test the extensions loading mechanisms.
99 Parameters:
100 extension: Extension specification (parametrized).
101 """
102 extensions = load_extensions(extension)
103 loaded: ExtensionTest = extensions._extensions[0] # type: ignore[assignment]
104 # We cannot use isinstance here,
105 # because loading from a filepath drops the parent `tests` package,
106 # resulting in a different object than the present ExtensionTest.
107 assert loaded.__class__.__name__ == "ExtensionTest"
108 if isinstance(extension, (dict, ExtensionTest)):
109 assert loaded.kwargs == {"option": 0}
112# YORE: EOL 3.11: Remove block.
113def test_extension_events_without_type_aliases() -> None:
114 """Test events triggering."""
115 extension = ExtensionTest()
116 with temporary_visited_module(
117 """
118 attr = 0
119 def func(): ...
120 class Class:
121 cattr = 1
122 def method(self): ...
123 """,
124 extensions=load_extensions(extension),
125 ):
126 pass
127 events = [
128 "on_attribute_instance",
129 "on_attribute_node",
130 "on_class_instance",
131 "on_class_members",
132 "on_class_node",
133 "on_function_instance",
134 "on_function_node",
135 "on_instance",
136 "on_members",
137 "on_module_instance",
138 "on_module_members",
139 "on_module_node",
140 "on_node",
141 ]
142 assert set(events) == set(extension.records)
145# YORE: EOL 3.11: Remove line.
146@pytest.mark.skipif(sys.version_info < (3, 12), reason="Python less than 3.12 does not have PEP 695 type aliases")
147def test_extension_events() -> None:
148 """Test events triggering."""
149 extension = ExtensionTest()
150 with temporary_visited_module(
151 """
152 attr = 0
153 def func(): ...
154 class Class:
155 cattr = 1
156 def method(self): ...
157 type TypeAlias = list[int]
158 """,
159 extensions=load_extensions(extension),
160 ):
161 pass
162 events = [
163 "on_attribute_instance",
164 "on_attribute_node",
165 "on_class_instance",
166 "on_class_members",
167 "on_class_node",
168 "on_function_instance",
169 "on_function_node",
170 "on_instance",
171 "on_members",
172 "on_module_instance",
173 "on_module_members",
174 "on_module_node",
175 "on_node",
176 "on_type_alias_instance",
177 "on_type_alias_node",
178 ]
179 assert set(events) == set(extension.records)