Skip to content

Commit f50fd18

Browse files
committed
Make API tests generic over packages
1 parent 09d0864 commit f50fd18

1 file changed

Lines changed: 39 additions & 30 deletions

File tree

tests/test_api.py

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,41 @@
55
from collections import defaultdict
66
from fnmatch import fnmatch
77
from pathlib import Path
8+
from types import ModuleType
89
from typing import TYPE_CHECKING
910

1011
import pytest
1112
from mkdocstrings import Inventory
1213

1314
import griffe
15+
import griffecli
1416

1517
if TYPE_CHECKING:
1618
from collections.abc import Iterator
1719

1820

19-
@pytest.fixture(name="loader", scope="module")
20-
def _fixture_loader() -> griffe.GriffeLoader:
21+
def _load_module(module: ModuleType) -> griffe.GriffeLoader:
2122
loader = griffe.GriffeLoader(
2223
extensions=griffe.load_extensions(
2324
"griffe_inherited_docstrings",
2425
"unpack_typeddict",
2526
),
2627
)
27-
loader.load("griffe")
28+
loader.load(module.__name__)
2829
loader.resolve_aliases()
2930
return loader
3031

3132

32-
@pytest.fixture(name="internal_api", scope="module")
33-
def _fixture_internal_api(loader: griffe.GriffeLoader) -> griffe.Module:
34-
return loader.modules_collection["griffe._internal"]
33+
def _get_internal_api(module: ModuleType, loader: griffe.GriffeLoader | None = None) -> griffe.Module:
34+
if loader is None:
35+
loader = _load_module(module)
36+
return loader.modules_collection[module.__name__ + "._internal"]
3537

3638

37-
@pytest.fixture(name="public_api", scope="module")
38-
def _fixture_public_api(loader: griffe.GriffeLoader) -> griffe.Module:
39-
return loader.modules_collection["griffe"]
39+
def _get_public_api(module: ModuleType, loader: griffe.GriffeLoader | None = None) -> griffe.Module:
40+
if loader is None:
41+
loader = _load_module(module)
42+
return loader.modules_collection[module.__name__]
4043

4144

4245
def _yield_public_objects(
@@ -76,19 +79,12 @@ def _yield_public_objects(
7679
continue
7780

7881

79-
@pytest.fixture(name="modulelevel_internal_objects", scope="module")
80-
def _fixture_modulelevel_internal_objects(internal_api: griffe.Module) -> list[griffe.Object | griffe.Alias]:
81-
return list(_yield_public_objects(internal_api, modulelevel=True))
82+
def _get_modulelevel_internal_objects(tested_module: ModuleType) -> list[griffe.Object | griffe.Alias]:
83+
return list(_yield_public_objects(_get_internal_api(tested_module), modulelevel=True))
8284

8385

84-
@pytest.fixture(name="internal_objects", scope="module")
85-
def _fixture_internal_objects(internal_api: griffe.Module) -> list[griffe.Object | griffe.Alias]:
86-
return list(_yield_public_objects(internal_api, modulelevel=False, special=True))
87-
88-
89-
@pytest.fixture(name="public_objects", scope="module")
90-
def _fixture_public_objects(public_api: griffe.Module) -> list[griffe.Object | griffe.Alias]:
91-
return list(_yield_public_objects(public_api, modulelevel=False, inherited=True, special=True))
86+
def _get_public_objects(tested_module: ModuleType) -> list[griffe.Object | griffe.Alias]:
87+
return list(_yield_public_objects(_get_public_api(tested_module), modulelevel=False, inherited=True, special=True))
9288

9389

9490
@pytest.fixture(name="inventory", scope="module")
@@ -100,8 +96,9 @@ def _fixture_inventory() -> Inventory:
10096
return Inventory.parse_sphinx(file)
10197

10298

103-
def test_alias_proxies(internal_api: griffe.Module) -> None:
99+
def test_alias_proxies() -> None:
104100
"""The Alias class has all the necessary methods and properties."""
101+
internal_api = _get_internal_api(griffe)
105102
alias_members = set(internal_api["models.Alias"].all_members.keys())
106103
for cls in (
107104
internal_api["models.Module"],
@@ -114,33 +111,39 @@ def test_alias_proxies(internal_api: griffe.Module) -> None:
114111
assert name in alias_members
115112

116113

117-
def test_exposed_objects(modulelevel_internal_objects: list[griffe.Object | griffe.Alias]) -> None:
114+
@pytest.mark.parametrize("tested_module", [griffe, griffecli])
115+
def test_exposed_objects(tested_module: ModuleType) -> None:
118116
"""All public objects in the internal API are exposed under `griffe`."""
117+
modulelevel_internal_objects = _get_modulelevel_internal_objects(tested_module)
119118
not_exposed = [
120119
obj.path
121120
for obj in modulelevel_internal_objects
122-
if obj.name not in griffe.__all__ or not hasattr(griffe, obj.name)
121+
if obj.name not in tested_module.__all__ or not hasattr(tested_module, obj.name)
123122
]
124123
assert not not_exposed, "Objects not exposed:\n" + "\n".join(sorted(not_exposed))
125124

126125

127-
def test_unique_names(modulelevel_internal_objects: list[griffe.Object | griffe.Alias]) -> None:
126+
@pytest.mark.parametrize("tested_module", [griffe, griffecli])
127+
def test_unique_names(tested_module: ModuleType) -> None:
128128
"""All internal objects have unique names."""
129+
modulelevel_internal_objects = _get_modulelevel_internal_objects(tested_module)
129130
names_to_paths = defaultdict(list)
130131
for obj in modulelevel_internal_objects:
131132
names_to_paths[obj.name].append(obj.path)
132133
non_unique = [paths for paths in names_to_paths.values() if len(paths) > 1]
133134
assert not non_unique, "Non-unique names:\n" + "\n".join(str(paths) for paths in non_unique)
134135

135136

136-
def test_single_locations(public_api: griffe.Module) -> None:
137+
@pytest.mark.parametrize("tested_module", [griffe, griffecli])
138+
def test_single_locations(tested_module: ModuleType) -> None:
137139
"""All objects have a single public location."""
138140

139141
def _public_path(obj: griffe.Object | griffe.Alias) -> bool:
140142
return obj.is_public and (obj.parent is None or _public_path(obj.parent))
141143

144+
public_api = _get_public_api(tested_module)
142145
multiple_locations = {}
143-
for obj_name in set(griffe.__all__) - griffe._CLI_MISSING_FEATURES:
146+
for obj_name in tested_module.__all__:
144147
obj = public_api[obj_name]
145148
if obj.aliases and (
146149
public_aliases := [path for path, alias in obj.aliases.items() if path != obj.path and _public_path(alias)]
@@ -168,16 +171,19 @@ def test_api_matches_inventory(inventory: Inventory, public_objects: list[griffe
168171
assert not not_in_inventory, msg.format(paths="\n".join(sorted(not_in_inventory)))
169172

170173

174+
@pytest.mark.parametrize("tested_module", [griffe, griffecli])
171175
def test_inventory_matches_api(
172176
inventory: Inventory,
173177
public_objects: list[griffe.Object | griffe.Alias],
174-
loader: griffe.GriffeLoader,
178+
tested_module: ModuleType,
175179
) -> None:
176180
"""The inventory doesn't contain any additional Python object."""
181+
loader = _load_module(tested_module)
182+
public_api = _get_public_api(tested_module, loader=loader)
183+
public_objects = _get_public_objects(public_api)
177184
not_in_api = []
178185
public_api_paths = {obj.path for obj in public_objects}
179-
public_api_paths.add("griffe")
180-
public_api_paths.add("griffecli")
186+
public_api_paths.add(tested_module.__name__)
181187
for item in inventory.values():
182188
if item.domain == "py" and "(" not in item.name:
183189
obj = loader.modules_collection[item.name]
@@ -187,13 +193,16 @@ def test_inventory_matches_api(
187193
assert not not_in_api, msg.format(paths="\n".join(sorted(not_in_api)))
188194

189195

190-
def test_no_module_docstrings_in_internal_api(internal_api: griffe.Module) -> None:
196+
@pytest.mark.parametrize("tested_module", [griffe, griffecli])
197+
def test_no_module_docstrings_in_internal_api(tested_module: ModuleType) -> None:
191198
"""No module docstrings should be written in our internal API.
192199
193200
The reasoning is that docstrings are addressed to users of the public API,
194201
but internal modules are not exposed to users, so they should not have docstrings.
195202
"""
196203

204+
internal_api = _get_internal_api(tested_module)
205+
197206
def _modules(obj: griffe.Module) -> Iterator[griffe.Module]:
198207
for member in obj.modules.values():
199208
yield member

0 commit comments

Comments
 (0)