fix preflight config path check

This commit is contained in:
ykcheungab
2026-09-20 11:20:47 +08:00
parent 45419a59c4
commit e2b906a640
3 changed files with 104 additions and 33 deletions
+11 -1
View File
@@ -103,7 +103,17 @@ def _runtime_roots() -> list[Path]:
except (OSError, ValueError): # pragma: no cover - defensive
continue
return [path for path in _unique_paths(anchors) if _is_sdk_runtime_root(path)]
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)
]
def _candidate_config_dirs() -> list[Path]:
+4 -2
View File
@@ -1271,9 +1271,11 @@ class Sensor:
that root had coefficients.
"""
attempts = self._force_cal_attempts or []
found = [entry for entry in attempts if entry.get("found_root") is not None]
# ``found_root`` is keyed even when nothing was found (valued None), so
# test its value, not the key.
found = [entry for entry in attempts if entry.get("found_root")]
with_cal = [entry for entry in found if entry.get("force_calibration_json")]
chosen = (with_cal or found or attempts or [None])[0]
chosen = (with_cal or found or [None])[0]
searched: list = []
for entry in attempts:
for root in entry.get("searched") or ():
+89 -30
View File
@@ -293,16 +293,22 @@ _CAL_DOC = {
def _make_root(base, *, name="anything", sensor_json=True, calibration=True):
"""A runnable SDK root: ``examples/`` beside ``sensors/<name>/``."""
"""A runnable SDK root: ``examples/`` beside ``sensors/<name>/``.
``sensor.json`` is copied from the shipped bundle because it is a *sensor
config*: a stub document would make ``_load_config`` depend on whichever
config the machine happens to resolve, which is not what these tests are
about.
"""
root = base / name
(root / "examples").mkdir(parents=True, exist_ok=True)
if sensor_json:
(root / "examples" / "basic_pipeline.py").write_text("# demo\n", encoding="utf-8")
(root / "examples" / "basic_pipeline.py").write_text("# demo\n", encoding="utf-8")
bundle = root / "sensors" / "mini"
bundle.mkdir(parents=True, exist_ok=True)
if sensor_json:
shipped = Path(__file__).resolve().parents[1] / "sensors" / "mini" / "sensor.json"
(bundle / "sensor.json").write_text(
json.dumps({"name": "prototype"}), encoding="utf-8"
shipped.read_text(encoding="utf-8"), encoding="utf-8"
)
if calibration:
(bundle / "force_calibration.json").write_text(
@@ -311,6 +317,18 @@ def _make_root(base, *, name="anything", sensor_json=True, calibration=True):
return root
def _bare_sensor(**attrs):
"""A Sensor instance without running ``__init__``/``_load_config``.
The note-formatting helpers touch no sensor state, so a whole sensor (and
therefore a whole machine config) is not needed to test them.
"""
sensor = object.__new__(Sensor)
for key, value in attrs.items():
setattr(sensor, key, value)
return sensor
def test_runtime_root_anchor_keys_on_layout_not_on_the_folder_name(tmp_path):
named = _make_root(tmp_path, name="some-random-name")
assert configs._is_sdk_runtime_root(named) is True
@@ -332,6 +350,24 @@ def test_runtime_roots_walk_up_from_a_subdirectory(tmp_path, monkeypatch):
assert str(root) 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.
"""
parent = tmp_path / "workspace"
inner = _make_root(parent, name="inner-release")
(parent / "examples").mkdir(parents=True, exist_ok=True)
assert configs._is_sdk_runtime_root(parent) 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
def test_the_running_root_bundle_beats_a_calibration_less_checkout(
tmp_path, monkeypatch
):
@@ -339,6 +375,13 @@ def test_the_running_root_bundle_beats_a_calibration_less_checkout(
running = _make_root(tmp_path, name="running-root")
checkout = _make_root(tmp_path, name="importable-checkout", calibration=False)
monkeypatch.setattr(configs, "_runtime_roots", lambda: [running, checkout])
# Pinned to real directories, so a leftover or unrelated bundle elsewhere on
# the machine cannot decide the outcome.
monkeypatch.setattr(
configs,
"_candidate_sensor_roots",
lambda: [running / "sensors", checkout / "sensors"],
)
bundle = configs.resolve_sensor_bundle("mini")
assert bundle is not None
@@ -347,17 +390,16 @@ def test_the_running_root_bundle_beats_a_calibration_less_checkout(
running / "sensors" / "mini" / "force_calibration.json"
).resolve()
# The whole point: the running root's document is the one that reaches the
# sensor, split shear and all.
sensor = Sensor("external", config_name="mini", verbose=False, backend="cpu")
try:
assert sensor.force_calibrated is True
assert sensor.force_unit == "N"
assert sensor.calibration.source_path == (
running / "sensors" / "mini" / "force_calibration.json"
).resolve()
finally:
sensor.disconnect()
# The whole point: the running root's document is the one the SDK loads.
from orisys.calibration import load as load_calibration
cal = load_calibration(sensor_type="mini")
assert cal is not None
assert cal.schema_version == 2
assert sorted(cal.channels) == ["fn", "fx", "fy"]
assert cal.source_path == (
running / "sensors" / "mini" / "force_calibration.json"
).resolve()
def test_a_nearer_bundle_wins_even_without_coefficients(tmp_path, monkeypatch):
@@ -370,6 +412,11 @@ def test_a_nearer_bundle_wins_even_without_coefficients(tmp_path, monkeypatch):
checkout = _make_root(tmp_path, name="checkout", calibration=False)
running = _make_root(tmp_path, name="run-root")
monkeypatch.setattr(configs, "_runtime_roots", lambda: [checkout, running])
monkeypatch.setattr(
configs,
"_candidate_sensor_roots",
lambda: [checkout / "sensors", running / "sensors"],
)
bundle = configs.resolve_sensor_bundle("mini")
assert bundle is not None
assert bundle.root == (checkout / "sensors" / "mini").resolve()
@@ -402,19 +449,31 @@ def test_describe_bundle_resolution_names_the_bundle_and_the_searched_roots(
assert missing["force_calibration_json"] is False
def test_the_calibration_note_names_the_bundle_that_was_found(
tmp_path, monkeypatch
):
def test_the_calibration_note_names_the_bundle_that_was_found(tmp_path, monkeypatch):
"""The note explains a bundle with no coefficients, not a "missing file"."""
checkout = _make_root(tmp_path, name="checkout", calibration=False)
monkeypatch.setattr(configs, "_runtime_roots", lambda: [checkout])
sensor = Sensor("external", config_name="mini", verbose=False, backend="cpu")
try:
assert sensor.calibration is None
# Still None: callers read a non-None value here as "calibrated".
assert sensor._force_cal_sensor_name is None
note = sensor._force_calibration_note()
assert "has no force_calibration.json" in note
assert str((checkout / "sensors" / "mini").resolve()) in note
assert "roots searched" in note
finally:
sensor.disconnect()
roots = [checkout / "sensors"]
monkeypatch.setattr(configs, "_candidate_sensor_roots", lambda: list(roots))
# Composed via _bare_sensor: the note reads only _force_cal_attempts, so this
# needs no machine config and cannot fail on config resolution.
sensor = _bare_sensor(
_force_cal_attempts=[configs.describe_bundle_resolution("mini")],
_force_cal_sensor_name=None,
)
note = sensor._force_calibration_note()
assert "has no force_calibration.json" in note
assert str((checkout / "sensors" / "mini").resolve()) in note
assert "roots searched" in note
# No bundle anywhere: say that instead of naming a root.
(checkout / "sensors" / "mini" / "sensor.json").unlink()
sensor = _bare_sensor(
_force_cal_attempts=[configs.describe_bundle_resolution("mini")],
_force_cal_sensor_name=None,
)
note = sensor._force_calibration_note()
assert "no sensor bundle resolved" in note
# A dict always keys ``found_root``, so guard its value: an unguarded copy of
# this branch prints "bundle found at None".
assert "None" not in note