Skip to content

Commit

Permalink
fix: When deciding to alias an object or not during inspection, consi…
Browse files Browse the repository at this point in the history
…der 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
  • Loading branch information
pawamoy committed Jun 26, 2024
1 parent 6e17def commit 8c9f6e6
Showing 1 changed file with 9 additions and 5 deletions.
14 changes: 9 additions & 5 deletions src/griffe/agents/nodes/_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
("os", "nt"),
("os", "posix"),
("numpy.core._multiarray_umath", "numpy.core.multiarray"),
("pymmcore._pymmcore_swig", "pymmcore.pymmcore_swig"),
}


def _same_components(a: str, b: str, /) -> bool:
# TODO: Use `removeprefix` when we drop Python 3.8.
return [cpn.lstrip("_") for cpn in a.split(".")] == [cpn.lstrip("_") for cpn in b.split(".")]


class ObjectNode:
"""Helper class to represent an object tree.
Expand Down Expand Up @@ -253,10 +257,10 @@ def alias_target_path(self) -> str | None:
return None

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

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

0 comments on commit 8c9f6e6

Please sign in to comment.