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 except (OSError, ValueError): # pragma: no cover - defensive
continue 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]: def _candidate_config_dirs() -> list[Path]:
+4 -2
View File
@@ -1271,9 +1271,11 @@ class Sensor:
that root had coefficients. that root had coefficients.
""" """
attempts = self._force_cal_attempts or [] 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")] 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 = [] searched: list = []
for entry in attempts: for entry in attempts:
for root in entry.get("searched") or (): for root in entry.get("searched") or ():
+82 -23
View File
@@ -293,16 +293,22 @@ _CAL_DOC = {
def _make_root(base, *, name="anything", sensor_json=True, calibration=True): 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 = base / name
(root / "examples").mkdir(parents=True, exist_ok=True) (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 = root / "sensors" / "mini"
bundle.mkdir(parents=True, exist_ok=True) bundle.mkdir(parents=True, exist_ok=True)
if sensor_json: if sensor_json:
shipped = Path(__file__).resolve().parents[1] / "sensors" / "mini" / "sensor.json"
(bundle / "sensor.json").write_text( (bundle / "sensor.json").write_text(
json.dumps({"name": "prototype"}), encoding="utf-8" shipped.read_text(encoding="utf-8"), encoding="utf-8"
) )
if calibration: if calibration:
(bundle / "force_calibration.json").write_text( (bundle / "force_calibration.json").write_text(
@@ -311,6 +317,18 @@ def _make_root(base, *, name="anything", sensor_json=True, calibration=True):
return root 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): def test_runtime_root_anchor_keys_on_layout_not_on_the_folder_name(tmp_path):
named = _make_root(tmp_path, name="some-random-name") named = _make_root(tmp_path, name="some-random-name")
assert configs._is_sdk_runtime_root(named) is True 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 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( def test_the_running_root_bundle_beats_a_calibration_less_checkout(
tmp_path, monkeypatch 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") running = _make_root(tmp_path, name="running-root")
checkout = _make_root(tmp_path, name="importable-checkout", calibration=False) checkout = _make_root(tmp_path, name="importable-checkout", calibration=False)
monkeypatch.setattr(configs, "_runtime_roots", lambda: [running, checkout]) 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") bundle = configs.resolve_sensor_bundle("mini")
assert bundle is not None 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" running / "sensors" / "mini" / "force_calibration.json"
).resolve() ).resolve()
# The whole point: the running root's document is the one that reaches the # The whole point: the running root's document is the one the SDK loads.
# sensor, split shear and all. from orisys.calibration import load as load_calibration
sensor = Sensor("external", config_name="mini", verbose=False, backend="cpu")
try: cal = load_calibration(sensor_type="mini")
assert sensor.force_calibrated is True assert cal is not None
assert sensor.force_unit == "N" assert cal.schema_version == 2
assert sensor.calibration.source_path == ( assert sorted(cal.channels) == ["fn", "fx", "fy"]
assert cal.source_path == (
running / "sensors" / "mini" / "force_calibration.json" running / "sensors" / "mini" / "force_calibration.json"
).resolve() ).resolve()
finally:
sensor.disconnect()
def test_a_nearer_bundle_wins_even_without_coefficients(tmp_path, monkeypatch): 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) checkout = _make_root(tmp_path, name="checkout", calibration=False)
running = _make_root(tmp_path, name="run-root") running = _make_root(tmp_path, name="run-root")
monkeypatch.setattr(configs, "_runtime_roots", lambda: [checkout, running]) 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") bundle = configs.resolve_sensor_bundle("mini")
assert bundle is not None assert bundle is not None
assert bundle.root == (checkout / "sensors" / "mini").resolve() 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 assert missing["force_calibration_json"] is False
def test_the_calibration_note_names_the_bundle_that_was_found( def test_the_calibration_note_names_the_bundle_that_was_found(tmp_path, monkeypatch):
tmp_path, monkeypatch """The note explains a bundle with no coefficients, not a "missing file"."""
):
checkout = _make_root(tmp_path, name="checkout", calibration=False) checkout = _make_root(tmp_path, name="checkout", calibration=False)
monkeypatch.setattr(configs, "_runtime_roots", lambda: [checkout]) roots = [checkout / "sensors"]
sensor = Sensor("external", config_name="mini", verbose=False, backend="cpu") monkeypatch.setattr(configs, "_candidate_sensor_roots", lambda: list(roots))
try:
assert sensor.calibration is None # Composed via _bare_sensor: the note reads only _force_cal_attempts, so this
# Still None: callers read a non-None value here as "calibrated". # needs no machine config and cannot fail on config resolution.
assert sensor._force_cal_sensor_name is None sensor = _bare_sensor(
_force_cal_attempts=[configs.describe_bundle_resolution("mini")],
_force_cal_sensor_name=None,
)
note = sensor._force_calibration_note() note = sensor._force_calibration_note()
assert "has no force_calibration.json" in note assert "has no force_calibration.json" in note
assert str((checkout / "sensors" / "mini").resolve()) in note assert str((checkout / "sensors" / "mini").resolve()) in note
assert "roots searched" in note assert "roots searched" in note
finally:
sensor.disconnect() # 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