diff --git a/src/orisys/configs.py b/src/orisys/configs.py index 63ee12d..8f69cec 100644 --- a/src/orisys/configs.py +++ b/src/orisys/configs.py @@ -104,16 +104,21 @@ def _runtime_roots() -> list[Path]: continue anchors = _unique_paths(anchors) - # Keep the *innermost* matching directories. Walking up from the launcher - # also visits every parent, and a parent that happens to carry an - # ``examples/`` directory must not outrank the real root below it: doing so - # made a workspace directory shadow the release root's own bundle. - return [ - path - for path in anchors - if _is_sdk_runtime_root(path) - and not any(other != path and other.is_relative_to(path) for other in anchors) - ] + # The run root is the *nearest* runnable directory, walking up from the + # launcher: starting at the cwd, then the script's directory, then the + # entries sys.path. Order carries the meaning here -- containment does not. + # Scanning every anchor for the shortest match instead picked the shallowest + # ancestor, so a workspace that carried ``examples/`` alongside ``sensors/`` + # absorbed the real root below it and its bundle shadowed that root's own. + roots: list[Path] = [] + for path in anchors: + if not _is_sdk_runtime_root(path): + continue + if any(existing.is_relative_to(path) or path.is_relative_to(existing) for existing in roots): + # Same tree: only the nearest root counts. + continue + roots.append(path) + return roots def _candidate_config_dirs() -> list[Path]: diff --git a/tests/test_signal_processing.py b/tests/test_signal_processing.py index 1a8c6f1..e3268e9 100644 --- a/tests/test_signal_processing.py +++ b/tests/test_signal_processing.py @@ -3,6 +3,7 @@ from __future__ import annotations import json +from pathlib import Path import numpy as np import pytest @@ -343,29 +344,53 @@ def test_runtime_root_anchor_keys_on_layout_not_on_the_folder_name(tmp_path): def test_runtime_roots_walk_up_from_a_subdirectory(tmp_path, monkeypatch): + """Launching from ``/examples`` still finds ````. + + Regression: the ancestor filter used to compare against *every* anchor, so a + platform ancestor such as ``D:\\`` disqualified every root and this list came + back empty. + """ root = _make_root(tmp_path, name="renamed-release") subdir = root / "examples" monkeypatch.setattr(configs.Path, "cwd", classmethod(lambda cls: subdir)) roots = [str(path) for path in configs._runtime_roots()] - assert str(root) in roots + assert str(root) in roots, roots + # The point of the regression: every returned entry is a genuine runnable + # root, never a bare platform ancestor that merely sat above the launcher. + for entry in roots: + assert configs._is_sdk_runtime_root(Path(entry)), entry + assert str(root.parent) not in roots + assert str(root.drive + "\\") not in roots def test_a_parent_root_never_outranks_the_root_below_it(tmp_path, monkeypatch): """Walking up must not let a parent's bundle shadow the real run root. - A directory that happens to carry ``examples/`` above the launcher (a - workspace holding several checkouts, one of which keeps an ``examples/`` - folder) used to be treated as a run root too, and its ``sensors/`` came - first — silently shadowing the root the operator was actually running from. + A parent that itself looks runnable (``examples/`` *and* ``sensors/``) must + not outrank the root below it, or its bundle would shadow the one the + operator is actually running from. """ parent = tmp_path / "workspace" inner = _make_root(parent, name="inner-release") + # Give the PARENT its own bundle. ``_make_root(parent, name=…)`` would put + # examples/ and sensors/ inside that named child, leaving the parent itself + # not runnable -- which is what made this assertion fail twice. (parent / "examples").mkdir(parents=True, exist_ok=True) + (parent / "sensors" / "mini").mkdir(parents=True, exist_ok=True) + (parent / "sensors" / "mini" / "sensor.json").write_text( + "{}", encoding="utf-8" + ) assert configs._is_sdk_runtime_root(parent) is True + assert configs._is_sdk_runtime_root(inner) is True monkeypatch.setattr(configs.Path, "cwd", classmethod(lambda cls: inner)) roots = [str(path) for path in configs._runtime_roots()] - assert roots == [str(inner)], roots + assert str(inner) in roots, roots + assert str(parent) not in roots, roots + # Nothing nested below the launcher's root may appear either. + assert not any( + Path(entry).is_relative_to(inner) and entry != str(inner) for entry in roots + ) def test_the_running_root_bundle_beats_a_calibration_less_checkout(