Coverage for tests/test_inspector.py: 100.00%
73 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-15 16:47 +0200
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-15 16:47 +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 pytest.raises(ImportError, match="ModuleNotFoundError: No module named 'missing'"), temporary_inspected_module(
52 "import missing",
53 ):
54 pass
57def test_inspect_properties_as_attributes() -> None:
58 """Assert properties are created as attributes and not functions."""
59 with temporary_inspected_module(
60 """
61 try:
62 from functools import cached_property
63 except ImportError:
64 from cached_property import cached_property
66 class C:
67 @property
68 def prop(self) -> bool:
69 return True
70 @cached_property
71 def cached_prop(self) -> int:
72 return 0
73 """,
74 ) as module:
75 assert module["C.prop"].is_attribute
76 assert "property" in module["C.prop"].labels
77 assert module["C.cached_prop"].is_attribute
78 assert "cached" in module["C.cached_prop"].labels
81def test_inspecting_module_importing_other_module() -> None:
82 """Assert aliases to modules are correctly inspected and aliased."""
83 with temporary_inspected_module("import itertools as it") as module:
84 assert module["it"].is_alias
85 assert module["it"].target_path == "itertools"
88def test_inspecting_parameters_with_functions_as_default_values() -> None:
89 """Assert functions as default parameter values are serialized with their name."""
90 with temporary_inspected_module("def func(): ...\ndef other_func(f=func): ...") as module:
91 default = module["other_func"].parameters["f"].default
92 assert default == "func"
95def test_inspecting_package_and_module_with_same_names() -> None:
96 """Package and module having same name shouldn't cause issues."""
97 with temporary_inspected_package("package", {"package.py": "a = 0"}):
98 pass
101def test_inspecting_module_with_submodules() -> None:
102 """Inspecting a module shouldn't register any of its submodules if they're not imported."""
103 with temporary_pypackage("pkg", ["mod.py"]) as tmp_package:
104 pkg = inspect("pkg", filepath=tmp_package.path / "__init__.py")
105 assert "mod" not in pkg.members
106 clear_sys_modules("pkg")
109def test_inspecting_module_with_imported_submodules() -> None:
110 """When inspecting a package on the disk, direct submodules should be skipped entirely."""
111 with temporary_pypackage(
112 "pkg",
113 {
114 "__init__.py": "from pkg import subpkg\nfrom pkg.subpkg import mod",
115 "subpkg/__init__.py": "a = 0",
116 "subpkg/mod.py": "b = 0",
117 },
118 ) as tmp_package:
119 pkg = inspect("pkg", filepath=tmp_package.path / "__init__.py")
120 assert "subpkg" not in pkg.members
121 assert "mod" in pkg.members
122 assert pkg["mod"].is_alias
123 assert pkg["mod"].target_path == "pkg.subpkg.mod"
124 clear_sys_modules("pkg")
127def test_inspecting_objects_from_private_builtin_stdlib_moduless() -> None:
128 """Inspect objects from private built-in modules in the standard library."""
129 ast = inspect("ast")
130 assert "Assign" in ast.members
131 assert not ast["Assign"].is_alias
133 ast = inspect("_ast")
134 assert "Assign" in ast.members
135 assert not ast["Assign"].is_alias
138def test_inspecting_partials_as_functions() -> None:
139 """Assert partials are correctly inspected as functions."""
140 with temporary_inspected_module(
141 """
142 from functools import partial
143 def func(a: int, b: int) -> int: pass
144 partial_func = partial(func, 1)
145 partial_func.__module__ = __name__
146 """,
147 ) as module:
148 partial_func = module["partial_func"]
149 assert partial_func.is_function
150 assert partial_func.parameters[0].name == "b"
151 assert partial_func.parameters[0].annotation.name == "int"
152 assert partial_func.returns.name == "int"