Coverage for tests/test_inspector.py: 98.63%
73 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"""Test inspection mechanisms."""
3from __future__ import annotations
5import pytest
7from griffe import inspect, temporary_inspected_module, temporary_inspected_package, temporary_pypackage
8from tests.helpers import clear_sys_modules
11def test_annotations_from_builtin_types() -> None:
12 """Assert builtin types are correctly transformed to annotations."""
13 with temporary_inspected_module("def func(a: int) -> str: pass") as module:
14 func = module["func"]
15 assert func.parameters[0].name == "a"
16 assert func.parameters[0].annotation.name == "int"
17 assert func.returns.name == "str"
20def test_annotations_from_classes() -> None:
21 """Assert custom classes are correctly transformed to annotations."""
22 with temporary_inspected_module("class A: pass\ndef func(a: A) -> A: pass") as module:
23 func = module["func"]
24 assert func.parameters[0].name == "a"
25 param = func.parameters[0].annotation
26 assert param.name == "A"
27 assert param.canonical_path == f"{module.name}.A"
28 returns = func.returns
29 assert returns.name == "A"
30 assert returns.canonical_path == f"{module.name}.A"
33def test_class_level_imports() -> None:
34 """Assert annotations using class-level imports are resolved."""
35 with temporary_inspected_module(
36 """
37 class A:
38 from io import StringIO
39 def method(self, p: StringIO):
40 pass
41 """,
42 ) as module:
43 method = module["A.method"]
44 name = method.parameters["p"].annotation
45 assert name.name == "StringIO"
46 assert name.canonical_path == "io.StringIO"
49def test_missing_dependency() -> None:
50 """Assert missing dependencies are handled during dynamic imports."""
51 with (
52 pytest.raises(ImportError, match="ModuleNotFoundError: No module named 'missing'"),
53 temporary_inspected_module("import missing"),
54 ):
55 pass
58def test_inspect_properties_as_attributes() -> None:
59 """Assert properties are created as attributes and not functions."""
60 with temporary_inspected_module(
61 """
62 try:
63 from functools import cached_property
64 except ImportError:
65 from cached_property import cached_property
67 class C:
68 @property
69 def prop(self) -> bool:
70 return True
71 @cached_property
72 def cached_prop(self) -> int:
73 return 0
74 """,
75 ) as module:
76 assert module["C.prop"].is_attribute
77 assert "property" in module["C.prop"].labels
78 assert module["C.cached_prop"].is_attribute
79 assert "cached" in module["C.cached_prop"].labels
82def test_inspecting_module_importing_other_module() -> None:
83 """Assert aliases to modules are correctly inspected and aliased."""
84 with temporary_inspected_module("import itertools as it") as module:
85 assert module["it"].is_alias
86 assert module["it"].target_path == "itertools"
89def test_inspecting_parameters_with_functions_as_default_values() -> None:
90 """Assert functions as default parameter values are serialized with their name."""
91 with temporary_inspected_module("def func(): ...\ndef other_func(f=func): ...") as module:
92 default = module["other_func"].parameters["f"].default
93 assert default == "func"
96def test_inspecting_package_and_module_with_same_names() -> None:
97 """Package and module having same name shouldn't cause issues."""
98 with temporary_inspected_package("package", {"package.py": "a = 0"}):
99 pass
102def test_inspecting_module_with_submodules() -> None:
103 """Inspecting a module shouldn't register any of its submodules if they're not imported."""
104 with temporary_pypackage("pkg", ["mod.py"]) as tmp_package:
105 pkg = inspect("pkg", filepath=tmp_package.path / "__init__.py")
106 assert "mod" not in pkg.members
107 clear_sys_modules("pkg")
110def test_inspecting_module_with_imported_submodules() -> None:
111 """When inspecting a package on the disk, direct submodules should be skipped entirely."""
112 with temporary_pypackage(
113 "pkg",
114 {
115 "__init__.py": "from pkg import subpkg\nfrom pkg.subpkg import mod",
116 "subpkg/__init__.py": "a = 0",
117 "subpkg/mod.py": "b = 0",
118 },
119 ) as tmp_package:
120 pkg = inspect("pkg", filepath=tmp_package.path / "__init__.py")
121 assert "subpkg" not in pkg.members
122 assert "mod" in pkg.members
123 assert pkg["mod"].is_alias
124 assert pkg["mod"].target_path == "pkg.subpkg.mod"
125 clear_sys_modules("pkg")
128def test_inspecting_objects_from_private_builtin_stdlib_moduless() -> None:
129 """Inspect objects from private built-in modules in the standard library."""
130 ast = inspect("ast")
131 assert "Assign" in ast.members
132 assert not ast["Assign"].is_alias
134 ast = inspect("_ast")
135 assert "Assign" in ast.members
136 assert not ast["Assign"].is_alias
139def test_inspecting_partials_as_functions() -> None:
140 """Assert partials are correctly inspected as functions."""
141 with temporary_inspected_module(
142 """
143 from functools import partial
144 def func(a: int, b: int) -> int: pass
145 partial_func = partial(func, 1)
146 partial_func.__module__ = __name__
147 """,
148 ) as module:
149 partial_func = module["partial_func"]
150 assert partial_func.is_function
151 assert partial_func.parameters[0].name == "b"
152 assert partial_func.parameters[0].annotation.name == "int"
153 assert partial_func.returns.name == "int"