Skip to content

Commit 8c9f6e6

Browse files
committed
fix: When deciding to alias an object or not during inspection, consider module paths to be equivalent even with arbitrary private components
When deciding whether an object should be aliased during dynamic analysis, we previously said "no" only if the parent module path and the child module path were equal after removing any leading underscores. In short, `_a` is equal to `a`, and `a.b` is equal to `_a.b`. But in some cases (see mentioned issue), path components other than the first have leading underscores or not. For example: `a.b` and `a._b`. These cases where not supported, and would result in objects being aliased instead of inspected in-place, later causing alias resolution issues (cyclic aliases, pointing at themselves). Now we decide that paths are equivalent if all their components stripped from leading underscores are equal. It means that cases like `a._b.c` vs. `a.b._c` are supported, and an object analyzed in one of them but declared in the other will be inspected in-place and not aliased. Even though this specific case is weird (like many other possible cases), we suppose that users know what they are doing with their module layout / public-private API. Issue-296: #296
1 parent 6e17def commit 8c9f6e6

1 file changed

Lines changed: 9 additions & 5 deletions

File tree

src/griffe/agents/nodes/_runtime.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717
("os", "nt"),
1818
("os", "posix"),
1919
("numpy.core._multiarray_umath", "numpy.core.multiarray"),
20-
("pymmcore._pymmcore_swig", "pymmcore.pymmcore_swig"),
2120
}
2221

2322

23+
def _same_components(a: str, b: str, /) -> bool:
24+
# TODO: Use `removeprefix` when we drop Python 3.8.
25+
return [cpn.lstrip("_") for cpn in a.split(".")] == [cpn.lstrip("_") for cpn in b.split(".")]
26+
27+
2428
class ObjectNode:
2529
"""Helper class to represent an object tree.
2630
@@ -253,10 +257,10 @@ def alias_target_path(self) -> str | None:
253257
return None
254258

255259
# If the current object was declared in the same module as its parent,
256-
# or in a module with the same name but starting/not starting with an underscore,
257-
# we don't want to alias it. Examples: (a, a), (a, _a), (_a, a), (_a, _a).
258-
# TODO: Use `removeprefix` when we drop Python 3.8.
259-
if parent_module_path.lstrip("_") == child_module_path.lstrip("_"):
260+
# or in a module with the same path components but starting/not starting with underscores,
261+
# we don't want to alias it. Examples: (a, a), (a, _a), (_a, a), (_a, _a),
262+
# (a.b, a.b), (a.b, _a.b), (_a._b, a.b), (a._b, _a.b), etc..
263+
if _same_components(parent_module_path, child_module_path):
260264
return None
261265

262266
# If the current object was declared in any other module, we alias it.

0 commit comments

Comments
 (0)