diff --git a/common/file_chunker.py b/common/file_chunker.py index 0ad002603..641fedca4 100644 --- a/common/file_chunker.py +++ b/common/file_chunker.py @@ -58,26 +58,34 @@ def file_chunked_exists(path) -> bool: class ChunkStream(io.RawIOBase): def __init__(self, paths): self._paths = iter(paths) - self._buffer = memoryview(b"") + self._file = None def readable(self): return True def readinto(self, buffer): count = 0 + view = memoryview(buffer) while count < len(buffer): - if not self._buffer: + if self._file is None: path = next(self._paths, None) if path is None: break - self._buffer = memoryview(Path(path).read_bytes()) + self._file = open(path, "rb") + bytes_read = self._file.readinto(view[count:]) + if not bytes_read: + self._file.close() + self._file = None continue - take = min(len(buffer) - count, len(self._buffer)) - buffer[count:count + take] = self._buffer[:take] - self._buffer = self._buffer[take:] - count += take + count += bytes_read return count + def close(self): + if self._file is not None: + self._file.close() + self._file = None + super().close() + def open_file_chunked(path): chunks = get_existing_chunks(path) diff --git a/common/tests/test_file_chunker.py b/common/tests/test_file_chunker.py index 3d0e54a26..da73b77d6 100644 --- a/common/tests/test_file_chunker.py +++ b/common/tests/test_file_chunker.py @@ -1,3 +1,5 @@ +from pathlib import Path + from openpilot.common import file_chunker @@ -14,3 +16,19 @@ def test_chunked_stream_round_trip(tmp_path, monkeypatch): assert file_chunker.read_file_chunked(path) == payload with file_chunker.open_file_chunked(path) as stream: assert stream.read(9) + stream.read() == payload + + +def test_unchunked_stream_does_not_materialize_file(tmp_path, monkeypatch): + path = tmp_path / "fat.onnx" + payload = b"large model read through bounded buffers" + path.write_bytes(payload) + original_read_bytes = Path.read_bytes + + def reject_whole_file_read(candidate): + if candidate == path: + raise AssertionError("streaming must not call Path.read_bytes()") + return original_read_bytes(candidate) + + monkeypatch.setattr(Path, "read_bytes", reject_whole_file_read) + with file_chunker.open_file_chunked(path) as stream: + assert stream.read() == payload diff --git a/selfdrive/modeld/compile_modeld.py b/selfdrive/modeld/compile_modeld.py index 2867492bc..50f3df792 100644 --- a/selfdrive/modeld/compile_modeld.py +++ b/selfdrive/modeld/compile_modeld.py @@ -477,8 +477,13 @@ def read_file_chunked_to_disk(path): from openpilot.common.file_chunker import open_file_chunked temporary_path = f"{path}.unchunked" - with open(temporary_path, "wb") as output, open_file_chunked(path) as source: - shutil.copyfileobj(source, output) + try: + with open(temporary_path, "wb") as output, open_file_chunked(path) as source: + shutil.copyfileobj(source, output) + except Exception: + if os.path.exists(temporary_path): + os.remove(temporary_path) + raise atexit.register(lambda: os.path.exists(temporary_path) and os.remove(temporary_path)) return temporary_path diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc index 83ccd5df5..d3f757ff6 100644 --- a/selfdrive/pandad/pandad.cc +++ b/selfdrive/pandad/pandad.cc @@ -502,9 +502,7 @@ void pandad_run(std::vector &pandas) { sm["selfdriveState"].getSelfdriveState().getEnabled() || preap_aol_engaged ); is_onroad = params.getBool("IsOnroad"); - const std::string car_make = params.get("CarMake"); - const bool is_gm = car_make == "gm" || car_make == "GM" || car_make == "Gm"; - const bool ignore_ignition_line = is_gm && params.getBool("IgnoreIgnitionLine"); + const bool ignore_ignition_line = params.getBool("IgnoreIgnitionLine"); process_panda_state(pandas, &pm, engaged, is_onroad, spoofing_started, ignore_ignition_line); panda_safety.configureSafetyMode(is_onroad); } diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index 9d421077e..f08079fe0 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -64,7 +64,7 @@ def get_hkg_remote_start_boots_comma(params: Params) -> bool: def get_ignore_ignition_line(params: Params) -> bool: try: - return (params.get("CarMake", encoding="utf-8") or "").lower() == "gm" and params.get_bool("IgnoreIgnitionLine") + return params.get_bool("IgnoreIgnitionLine") except UnknownKeyName: return False diff --git a/selfdrive/pandad/tests/test_pandad_firmware.py b/selfdrive/pandad/tests/test_pandad_firmware.py index 1b89e4ed6..5f4138eb2 100644 --- a/selfdrive/pandad/tests/test_pandad_firmware.py +++ b/selfdrive/pandad/tests/test_pandad_firmware.py @@ -12,25 +12,17 @@ SPEC.loader.exec_module(PANDAD) class FakeParams: - def __init__(self, car_make, ignore_ignition_line): - self.car_make = car_make + def __init__(self, ignore_ignition_line): self.ignore_ignition_line = ignore_ignition_line - def get(self, key, encoding=None): - assert key == "CarMake" - return self.car_make - def get_bool(self, key): assert key == "IgnoreIgnitionLine" return self.ignore_ignition_line -@pytest.mark.parametrize(("car_make", "enabled", "expected"), [ - ("gm", True, True), - ("GM", True, True), - ("tesla", True, False), - ("tesla", False, False), - (None, True, False), +@pytest.mark.parametrize(("enabled", "expected"), [ + (True, True), + (False, False), ]) -def test_ignore_ignition_line_is_gm_only(car_make, enabled, expected): - assert PANDAD.get_ignore_ignition_line(FakeParams(car_make, enabled)) == expected +def test_ignore_ignition_line_follows_toggle(enabled, expected): + assert PANDAD.get_ignore_ignition_line(FakeParams(enabled)) == expected diff --git a/starpilot/common/starpilot_utilities.py b/starpilot/common/starpilot_utilities.py index 60c32790b..a80f25ac4 100644 --- a/starpilot/common/starpilot_utilities.py +++ b/starpilot/common/starpilot_utilities.py @@ -199,7 +199,7 @@ def flash_panda(params_memory): except Exception: hkg_remote_start = False try: - ignore_ignition_line = (params.get("CarMake", encoding="utf-8") or "").lower() == "gm" and params.get_bool("IgnoreIgnitionLine") + ignore_ignition_line = params.get_bool("IgnoreIgnitionLine") except Exception: ignore_ignition_line = False