fix preflight config path check
This commit is contained in:
+11
-1
@@ -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]:
|
||||||
|
|||||||
@@ -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 ():
|
||||||
|
|||||||
@@ -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"]
|
||||||
running / "sensors" / "mini" / "force_calibration.json"
|
assert cal.source_path == (
|
||||||
).resolve()
|
running / "sensors" / "mini" / "force_calibration.json"
|
||||||
finally:
|
).resolve()
|
||||||
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(
|
||||||
note = sensor._force_calibration_note()
|
_force_cal_attempts=[configs.describe_bundle_resolution("mini")],
|
||||||
assert "has no force_calibration.json" in note
|
_force_cal_sensor_name=None,
|
||||||
assert str((checkout / "sensors" / "mini").resolve()) in note
|
)
|
||||||
assert "roots searched" in note
|
note = sensor._force_calibration_note()
|
||||||
finally:
|
assert "has no force_calibration.json" in note
|
||||||
sensor.disconnect()
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user