Coverage for tests/test_extensions.py: 100.00%
50 statements
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-12 01:34 +0200
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-12 01:34 +0200
1"""Tests for the `extensions` module."""
3from __future__ import annotations
5from pathlib import Path
6from typing import TYPE_CHECKING, Any
8import pytest
10from griffe import Extension, load_extensions, temporary_visited_module
12if TYPE_CHECKING:
13 import ast
15 from griffe import Attribute, Class, Function, Module, Object, ObjectNode
18class ExtensionTest(Extension): # noqa: D101
19 def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: D107
20 super().__init__()
21 self.records: list[str] = []
22 self.args = args
23 self.kwargs = kwargs
25 def on_attribute_instance(self, *, node: ast.AST | ObjectNode, attr: Attribute, **kwargs: Any) -> None: # noqa: D102,ARG002
26 self.records.append("on_attribute_instance")
28 def on_attribute_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
29 self.records.append("on_attribute_node")
31 def on_class_instance(self, *, node: ast.AST | ObjectNode, cls: Class, **kwargs: Any) -> None: # noqa: D102,ARG002
32 self.records.append("on_class_instance")
34 def on_class_members(self, *, node: ast.AST | ObjectNode, cls: Class, **kwargs: Any) -> None: # noqa: D102,ARG002
35 self.records.append("on_class_members")
37 def on_class_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
38 self.records.append("on_class_node")
40 def on_function_instance(self, *, node: ast.AST | ObjectNode, func: Function, **kwargs: Any) -> None: # noqa: D102,ARG002
41 self.records.append("on_function_instance")
43 def on_function_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
44 self.records.append("on_function_node")
46 def on_instance(self, *, node: ast.AST | ObjectNode, obj: Object, **kwargs: Any) -> None: # noqa: D102,ARG002
47 self.records.append("on_instance")
49 def on_members(self, *, node: ast.AST | ObjectNode, obj: Object, **kwargs: Any) -> None: # noqa: D102,ARG002
50 self.records.append("on_members")
52 def on_module_instance(self, *, node: ast.AST | ObjectNode, mod: Module, **kwargs: Any) -> None: # noqa: D102,ARG002
53 self.records.append("on_module_instance")
55 def on_module_members(self, *, node: ast.AST | ObjectNode, mod: Module, **kwargs: Any) -> None: # noqa: D102,ARG002
56 self.records.append("on_module_members")
58 def on_module_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
59 self.records.append("on_module_node")
61 def on_node(self, *, node: ast.AST | ObjectNode, **kwargs: Any) -> None: # noqa: D102,ARG002
62 self.records.append("on_node")
65@pytest.mark.parametrize(
66 "extension",
67 [
68 # with module path
69 "tests.test_extensions",
70 {"tests.test_extensions": {"option": 0}},
71 # with extension path
72 "tests.test_extensions.ExtensionTest",
73 {"tests.test_extensions.ExtensionTest": {"option": 0}},
74 # with filepath
75 "tests/test_extensions.py",
76 {"tests/test_extensions.py": {"option": 0}},
77 # with filepath and extension name
78 "tests/test_extensions.py:ExtensionTest",
79 {"tests/test_extensions.py:ExtensionTest": {"option": 0}},
80 # with instance
81 ExtensionTest(option=0),
82 # with class
83 ExtensionTest,
84 # with absolute paths (esp. important to test for Windows)
85 Path("tests/test_extensions.py").absolute().as_posix(),
86 Path("tests/test_extensions.py:ExtensionTest").absolute().as_posix(),
87 ],
88)
89def test_loading_extensions(extension: str | dict[str, dict[str, Any]] | Extension | type[Extension]) -> None:
90 """Test the extensions loading mechanisms.
92 Parameters:
93 extension: Extension specification (parametrized).
94 """
95 extensions = load_extensions(extension)
96 loaded: ExtensionTest = extensions._extensions[0] # type: ignore[assignment]
97 # We cannot use isinstance here,
98 # because loading from a filepath drops the parent `tests` package,
99 # resulting in a different object than the present ExtensionTest.
100 assert loaded.__class__.__name__ == "ExtensionTest"
101 if isinstance(extension, (dict, ExtensionTest)):
102 assert loaded.kwargs == {"option": 0}
105def test_extension_events() -> None:
106 """Test events triggering."""
107 extension = ExtensionTest()
108 with temporary_visited_module(
109 """
110 attr = 0
111 def func(): ...
112 class Class:
113 cattr = 1
114 def method(self): ...
115 """,
116 extensions=load_extensions(extension),
117 ):
118 pass
119 events = [
120 "on_attribute_instance",
121 "on_attribute_node",
122 "on_class_instance",
123 "on_class_members",
124 "on_class_node",
125 "on_function_instance",
126 "on_function_node",
127 "on_instance",
128 "on_members",
129 "on_module_instance",
130 "on_module_members",
131 "on_module_node",
132 "on_node",
133 ]
134 assert set(events) == set(extension.records)