From c1138c34b89965fd780d669c7dd6b12f245d8cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 28 Sep 2023 16:45:14 +0200 Subject: [PATCH] fix: Fix visiting relative imports in non-init modules --- src/griffe/agents/visitor.py | 4 ++-- tests/test_nodes.py | 2 ++ tests/test_visitor.py | 17 ++++++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/griffe/agents/visitor.py b/src/griffe/agents/visitor.py index bf52730b..fd78e208 100644 --- a/src/griffe/agents/visitor.py +++ b/src/griffe/agents/visitor.py @@ -522,8 +522,8 @@ def visit_importfrom(self, node: ast.ImportFrom) -> None: node: The node to visit. """ for name in node.names: - if not node.module and node.level == 1 and not name.asname: - # special case: when being in `a` and doing `from . import b`, + if not node.module and node.level == 1 and not name.asname and self.current.module.is_init_module: + # special case: when being in `a/__init__.py` and doing `from . import b`, # we are effectively creating a member `b` in `a` that is pointing to `a.b` # -> cyclic alias! in that case, we just skip it, as both the member and module # have the same name and can be accessed the same way diff --git a/tests/test_nodes.py b/tests/test_nodes.py index 8b3ab51f..716722a6 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -76,7 +76,9 @@ ("code", "path", "is_package", "expected"), [ ("from . import b", "a", False, "a.b"), + ("from . import b", "a", True, "a.b"), ("from . import c", "a.b", False, "a.c"), + ("from . import c", "a.b", True, "a.b.c"), ("from . import d", "a.b.c", False, "a.b.d"), ("from .c import d", "a", False, "a.c.d"), ("from .c import d", "a.b", False, "a.c.d"), diff --git a/tests/test_visitor.py b/tests/test_visitor.py index 091e5f87..14fa05e4 100644 --- a/tests/test_visitor.py +++ b/tests/test_visitor.py @@ -7,7 +7,7 @@ import pytest from griffe.loader import GriffeLoader -from griffe.tests import temporary_pypackage, temporary_visited_module +from griffe.tests import temporary_pypackage, temporary_visited_module, temporary_visited_package # @given(hs.from_node(node=libcst.Module)) # @pytest.mark.skipif(sys.version_info >= (3, 11, 0), reason="Too slow on Python 3.11?") @@ -352,3 +352,18 @@ class B: """, ) as module: assert module["A.B"].runtime + + +def test_visiting_relative_imports_triggering_cyclic_aliases() -> None: + """Skip specific imports to avoid cyclic aliases.""" + with temporary_visited_package( + "pkg", + { + "__init__.py": "from . import a", + "a.py": "from . import b", + "b.py": "", + }, + ) as pkg: + assert "a" not in pkg.imports + assert "b" in pkg["a"].imports + assert pkg["a"].imports["b"] == "pkg.b"