From f0d93eb32db9683986cfcafebec6d0f74a2a95da Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 19 Jul 2026 11:02:00 -0700 Subject: [PATCH] more ty, part 2 (#38379) --- openpilot/common/api.py | 2 + openpilot/common/hardware/tici/agnos.py | 2 +- openpilot/common/hardware/tici/hardware.py | 16 ++--- openpilot/common/hardware/tici/modem.py | 2 +- openpilot/common/pid.py | 12 ++-- openpilot/common/transformations/camera.py | 3 +- openpilot/selfdrive/car/tests/test_models.py | 3 +- openpilot/selfdrive/locationd/helpers.py | 5 +- openpilot/selfdrive/locationd/locationd.py | 2 +- openpilot/selfdrive/locationd/torqued.py | 9 +-- openpilot/selfdrive/modeld/modeld.py | 2 +- .../selfdrive/modeld/parse_model_outputs.py | 2 +- .../selfdrive/pandad/tests/test_pandad.py | 2 +- .../selfdrived/tests/test_state_machine.py | 8 +-- .../test/process_replay/process_replay.py | 8 +-- openpilot/selfdrive/ui/body/animations.py | 11 +-- openpilot/selfdrive/ui/layouts/main.py | 6 +- openpilot/selfdrive/ui/layouts/sidebar.py | 2 +- openpilot/selfdrive/ui/mici/layouts/home.py | 2 +- openpilot/selfdrive/ui/mici/layouts/main.py | 2 +- .../ui/mici/layouts/settings/device.py | 4 +- .../selfdrive/ui/mici/onroad/cameraview.py | 2 +- .../ui/mici/onroad/model_renderer.py | 4 +- .../selfdrive/ui/onroad/model_renderer.py | 4 +- .../selfdrive/ui/tests/diff/replay_script.py | 2 +- .../selfdrive/ui/tests/profile_onroad.py | 2 +- .../selfdrive/ui/translations/potools.py | 22 +++--- openpilot/system/athena/athenad.py | 5 +- openpilot/system/athena/tests/test_athenad.py | 4 +- openpilot/system/hardware/power_monitoring.py | 2 +- .../hardware/tests/test_power_monitoring.py | 2 +- .../loggerd/tests/loggerd_tests_common.py | 4 +- .../system/loggerd/tests/test_deleter.py | 2 +- .../system/loggerd/tests/test_loggerd.py | 5 +- openpilot/system/qcomgpsd/nmeaport.py | 72 +++++++++---------- .../system/sensord/tests/test_sensord.py | 2 +- openpilot/system/ubloxd/binary_struct.py | 2 +- openpilot/system/ui/lib/utils.py | 3 +- openpilot/system/ui/lib/wifi_manager.py | 2 +- openpilot/system/ui/tici_reset.py | 6 +- openpilot/system/ui/widgets/__init__.py | 29 +++++--- openpilot/system/ui/widgets/label.py | 3 +- openpilot/system/ui/widgets/nav_widget.py | 2 +- openpilot/system/ui/widgets/network.py | 19 ++--- openpilot/system/ui/widgets/scroller.py | 12 ++-- openpilot/system/ui/widgets/scroller_tici.py | 3 +- openpilot/system/updated/updated.py | 2 +- .../webrtc/tests/test_stream_session.py | 6 +- openpilot/system/webrtc/webrtcd.py | 2 +- openpilot/tools/lib/auth.py | 2 +- openpilot/tools/lib/file_sources.py | 8 +-- openpilot/tools/lib/tests/test_caching.py | 2 +- .../longitudinal_maneuvers/maneuversd.py | 2 +- openpilot/tools/replay/lib/ui_helpers.py | 3 +- openpilot/tools/sim/bridge/common.py | 2 +- openpilot/tools/sim/lib/common.py | 6 +- pyproject.toml | 4 -- 57 files changed, 194 insertions(+), 165 deletions(-) diff --git a/openpilot/common/api.py b/openpilot/common/api.py index c97f56c4b5..3e3733c3ad 100644 --- a/openpilot/common/api.py +++ b/openpilot/common/api.py @@ -27,6 +27,8 @@ class Api: return api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params) def get_token(self, payload_extra=None, expiry_hours=1): + if self.private_key is None: + raise RuntimeError("private key is not configured") now = datetime.now(UTC).replace(tzinfo=None) payload = { 'identity': self.dongle_id, diff --git a/openpilot/common/hardware/tici/agnos.py b/openpilot/common/hardware/tici/agnos.py index e1f62c841c..b3b5e05176 100755 --- a/openpilot/common/hardware/tici/agnos.py +++ b/openpilot/common/hardware/tici/agnos.py @@ -19,7 +19,7 @@ class StreamingDecompressor: def __init__(self, url: str) -> None: self.buf = b"" - self.req = requests.get(url, stream=True, headers={'Accept-Encoding': None}, timeout=60) + self.req = requests.get(url, stream=True, headers={'Accept-Encoding': 'identity'}, timeout=60) self.it = self.req.iter_content(chunk_size=1024 * 1024) self.decompressor = lzma.LZMADecompressor(format=lzma.FORMAT_AUTO) self.eof = False diff --git a/openpilot/common/hardware/tici/hardware.py b/openpilot/common/hardware/tici/hardware.py index 773cbd4bed..aa125ed97d 100644 --- a/openpilot/common/hardware/tici/hardware.py +++ b/openpilot/common/hardware/tici/hardware.py @@ -339,7 +339,7 @@ class Tici(HardwareBase): # Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL gpio_init(GPIO.SOM_ST_IO, True) - gpio_set(GPIO.SOM_ST_IO, 1) + gpio_set(GPIO.SOM_ST_IO, True) # *** IRQ config *** @@ -389,21 +389,21 @@ class Tici(HardwareBase): gpio_init(GPIO.STM_RST_N, True) gpio_init(GPIO.STM_BOOT0, True) - gpio_set(GPIO.STM_RST_N, 1) - gpio_set(GPIO.STM_BOOT0, 0) + gpio_set(GPIO.STM_RST_N, True) + gpio_set(GPIO.STM_BOOT0, False) time.sleep(0.01) - gpio_set(GPIO.STM_RST_N, 0) + gpio_set(GPIO.STM_RST_N, False) def recover_internal_panda(self): gpio_init(GPIO.STM_RST_N, True) gpio_init(GPIO.STM_BOOT0, True) - gpio_set(GPIO.STM_RST_N, 1) - gpio_set(GPIO.STM_BOOT0, 1) + gpio_set(GPIO.STM_RST_N, True) + gpio_set(GPIO.STM_BOOT0, True) time.sleep(0.01) - gpio_set(GPIO.STM_RST_N, 0) + gpio_set(GPIO.STM_RST_N, False) time.sleep(0.01) - gpio_set(GPIO.STM_BOOT0, 0) + gpio_set(GPIO.STM_BOOT0, False) def booted(self): # this normally boots within 8s, but on rare occasions takes 30+s diff --git a/openpilot/common/hardware/tici/modem.py b/openpilot/common/hardware/tici/modem.py index 0d30c49328..54251d4815 100755 --- a/openpilot/common/hardware/tici/modem.py +++ b/openpilot/common/hardware/tici/modem.py @@ -52,7 +52,7 @@ PPPD_CMD = [ "novj", "novjccomp", "ipcp-accept-local", "ipcp-accept-remote", "nomagic", "user", '""', "password", '""', ] -INITIAL_STATE = { +INITIAL_STATE: dict[str, object] = { "seconds_since_boot": 0, "state": "INITIALIZING", "connected": False, "ip_address": "", diff --git a/openpilot/common/pid.py b/openpilot/common/pid.py index b3d64d6fcd..f541baf05c 100644 --- a/openpilot/common/pid.py +++ b/openpilot/common/pid.py @@ -1,11 +1,13 @@ import numpy as np -from numbers import Number +from collections.abc import Sequence + +Gain = int | float | tuple[Sequence[float], Sequence[float]] | list[list[float]] class PIDController: - def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100): - self._k_p: list[list[float]] = [[0], [k_p]] if isinstance(k_p, Number) else k_p - self._k_i: list[list[float]] = [[0], [k_i]] if isinstance(k_i, Number) else k_i - self._k_d: list[list[float]] = [[0], [k_d]] if isinstance(k_d, Number) else k_d + def __init__(self, k_p: Gain, k_i: Gain, k_d: Gain = 0., pos_limit=1e308, neg_limit=-1e308, rate=100): + self._k_p = ([0], [k_p]) if isinstance(k_p, (int, float)) else k_p + self._k_i = ([0], [k_i]) if isinstance(k_i, (int, float)) else k_i + self._k_d = ([0], [k_d]) if isinstance(k_d, (int, float)) else k_d self.set_limits(pos_limit, neg_limit) diff --git a/openpilot/common/transformations/camera.py b/openpilot/common/transformations/camera.py index 2e68b5e37c..ada9c5b398 100644 --- a/openpilot/common/transformations/camera.py +++ b/openpilot/common/transformations/camera.py @@ -52,7 +52,7 @@ _ar_ox_config = DeviceCameraConfig(CameraConfig(1928, 1208, 2648.0), _ar_ox_fish _os_config = DeviceCameraConfig(CameraConfig(2688 // 2, 1520 // 2, 1522.0 * 3 / 4), _os_fisheye, _os_fisheye) _neo_config = DeviceCameraConfig(CameraConfig(1164, 874, 910.0), CameraConfig(816, 612, 650.0), _NoneCameraConfig()) -DEVICE_CAMERAS = { +DEVICE_CAMERAS: dict[tuple[str, str], DeviceCameraConfig] = { # A "device camera" is defined by a device type and sensor # sensor type was never set on eon/neo/two @@ -176,4 +176,3 @@ def img_from_device(pt_device): pt_img = pt_view/pt_view[:, 2:3] return pt_img.reshape(input_shape)[:, :2] - diff --git a/openpilot/selfdrive/car/tests/test_models.py b/openpilot/selfdrive/car/tests/test_models.py index cda94b3158..5ad31f93ab 100644 --- a/openpilot/selfdrive/car/tests/test_models.py +++ b/openpilot/selfdrive/car/tests/test_models.py @@ -21,6 +21,7 @@ from openpilot.selfdrive.pandad import can_capnp_to_list from openpilot.selfdrive.test.helpers import read_segment_list from openpilot.common.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT from openpilot.tools.lib.logreader import LogReader, LogsUnavailable, openpilotci_source, internal_source, comma_api_source +from openpilot.tools.lib.file_sources import Source from openpilot.tools.lib.route import SegmentName SafetyModel = car.CarParams.SafetyModel @@ -130,7 +131,7 @@ class TestCarModelBase(unittest.TestCase): segment_range = f"{cls.test_route.route}/{seg}" try: - sources = [internal_source] if len(INTERNAL_SEG_LIST) else [openpilotci_source, comma_api_source] + sources: list[Source] = [internal_source] if len(INTERNAL_SEG_LIST) else [openpilotci_source, comma_api_source] lr = LogReader(segment_range, sources=sources, sort_by_time=True) return cls.get_testing_data_from_logreader(lr) except (LogsUnavailable, AssertionError): diff --git a/openpilot/selfdrive/locationd/helpers.py b/openpilot/selfdrive/locationd/helpers.py index 849b950de2..5e3fa16027 100644 --- a/openpilot/selfdrive/locationd/helpers.py +++ b/openpilot/selfdrive/locationd/helpers.py @@ -1,4 +1,5 @@ import numpy as np +from collections.abc import Sequence from typing import Any from functools import cache @@ -68,7 +69,7 @@ class NPQueue: class PointBuckets: - def __init__(self, x_bounds: list[tuple[float, float]], min_points: list[float], min_points_total: int, points_per_bucket: int, rowsize: int) -> None: + def __init__(self, x_bounds: list[tuple[float, float]], min_points: Sequence[float], min_points_total: int, points_per_bucket: int, rowsize: int) -> None: self._rng = np.random.default_rng() self.x_bounds = x_bounds self.buckets = {bounds: NPQueue(maxlen=points_per_bucket, rowsize=rowsize) for bounds in x_bounds} @@ -101,7 +102,7 @@ class PointBuckets: return points return points[self._rng.choice(np.arange(len(points)), min(len(points), num_points), replace=False)] - def load_points(self, points: list[list[float]]) -> None: + def load_points(self, points: Sequence[Sequence[float]]) -> None: for point in points: self.add_point(*point) diff --git a/openpilot/selfdrive/locationd/locationd.py b/openpilot/selfdrive/locationd/locationd.py index 8e03995d13..eb3c42fce2 100755 --- a/openpilot/selfdrive/locationd/locationd.py +++ b/openpilot/selfdrive/locationd/locationd.py @@ -66,7 +66,7 @@ class LocationEstimator: self.observations = {kind: np.zeros(3, dtype=np.float32) for kind in obs_kinds} self.observation_errors = {kind: np.zeros(3, dtype=np.float32) for kind in obs_kinds} - def reset(self, t: float, x_initial: np.ndarray = PoseKalman.initial_x, P_initial: np.ndarray = PoseKalman.initial_P): + def reset(self, t: float | None, x_initial: np.ndarray = PoseKalman.initial_x, P_initial: np.ndarray = PoseKalman.initial_P): self.kf.init_state(x_initial, covs=P_initial, filter_time=t) def _validate_sensor_source(self, source: log.SensorEventData.SensorSource): diff --git a/openpilot/selfdrive/locationd/torqued.py b/openpilot/selfdrive/locationd/torqued.py index 6321461e8b..d36684563c 100755 --- a/openpilot/selfdrive/locationd/torqued.py +++ b/openpilot/selfdrive/locationd/torqued.py @@ -57,14 +57,14 @@ class TorqueEstimator(ParameterEstimator): self.lag = 0.0 self.track_all_points = track_all_points # for offline analysis, without max lateral accel or max steer torque filters if decimated: - self.min_bucket_points = MIN_BUCKET_POINTS / 10 + self.min_bucket_points: list[float] = (MIN_BUCKET_POINTS / 10).tolist() self.min_points_total = MIN_POINTS_TOTAL_QLOG self.fit_points = FIT_POINTS_TOTAL_QLOG self.factor_sanity = FACTOR_SANITY_QLOG self.friction_sanity = FRICTION_SANITY_QLOG else: - self.min_bucket_points = MIN_BUCKET_POINTS + self.min_bucket_points = MIN_BUCKET_POINTS.tolist() self.min_points_total = MIN_POINTS_TOTAL self.fit_points = FIT_POINTS_TOTAL self.factor_sanity = FACTOR_SANITY @@ -112,9 +112,10 @@ class TorqueEstimator(ParameterEstimator): 'latAccelOffset': cache_ltp.latAccelOffsetFiltered, 'frictionCoefficient': cache_ltp.frictionCoefficientFiltered } - initial_params['points'] = cache_ltp.points + cached_points: list[list[float]] = [list(point) for point in cache_ltp.points] + initial_params['points'] = cached_points self.decay = cache_ltp.decay - self.filtered_points.load_points(initial_params['points']) + self.filtered_points.load_points(cached_points) cloudlog.info("restored torque params from cache") except Exception: cloudlog.exception("failed to restore cached torque params") diff --git a/openpilot/selfdrive/modeld/modeld.py b/openpilot/selfdrive/modeld/modeld.py index 7c70cdb14d..39fcc0725c 100755 --- a/openpilot/selfdrive/modeld/modeld.py +++ b/openpilot/selfdrive/modeld/modeld.py @@ -89,7 +89,7 @@ class ModelState: self.frame_skip = ModelConstants.MODEL_RUN_FREQ // ModelConstants.MODEL_CONTEXT_FREQ self.input_queues, self.npy = make_input_queues(self.input_shapes, self.frame_skip, device=self.QUEUE_DEV) self.full_frames: dict[str, Tensor] = {} - self._blob_cache: dict[int, Tensor] = {} + self._blob_cache: dict[tuple[str, int], Tensor] = {} self.parser = Parser() self.frame_buf_params = {k: get_nv12_info(cam_w, cam_h) for k in ('img', 'big_img')} self.run_policy = jits['run_policy'] diff --git a/openpilot/selfdrive/modeld/parse_model_outputs.py b/openpilot/selfdrive/modeld/parse_model_outputs.py index 26c138b8ec..839c20f7cc 100644 --- a/openpilot/selfdrive/modeld/parse_model_outputs.py +++ b/openpilot/selfdrive/modeld/parse_model_outputs.py @@ -41,7 +41,7 @@ class Parser: raw = outs[name] outs[name] = sigmoid(raw) - def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=None): + def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=()): if self.check_missing(outs, name): return raw = outs[name] diff --git a/openpilot/selfdrive/pandad/tests/test_pandad.py b/openpilot/selfdrive/pandad/tests/test_pandad.py index e7a7107dcd..0f8fd9fc1a 100644 --- a/openpilot/selfdrive/pandad/tests/test_pandad.py +++ b/openpilot/selfdrive/pandad/tests/test_pandad.py @@ -60,7 +60,7 @@ class TestPandad: def test_in_reset(self): gpio_init(GPIO.STM_RST_N, True) - gpio_set(GPIO.STM_RST_N, 1) + gpio_set(GPIO.STM_RST_N, True) assert not Panda.list() self._run_test() diff --git a/openpilot/selfdrive/selfdrived/tests/test_state_machine.py b/openpilot/selfdrive/selfdrived/tests/test_state_machine.py index 8139e53d2e..ec8f068039 100644 --- a/openpilot/selfdrive/selfdrived/tests/test_state_machine.py +++ b/openpilot/selfdrive/selfdrived/tests/test_state_machine.py @@ -13,11 +13,11 @@ ALL_STATES = tuple(State.schema.enumerants.values()) ENABLE_EVENT_TYPES = (ET.ENABLE, ET.PRE_ENABLE, ET.OVERRIDE_LATERAL, ET.OVERRIDE_LONGITUDINAL) -def make_event(event_types): - event = {} +def make_event(event_types: list[str | None]): + EVENTS[0] = {} for ev in event_types: - event[ev] = NormalPermanentAlert("alert") - EVENTS[0] = event + if ev is not None: + EVENTS[0][ev] = NormalPermanentAlert("alert") return 0 diff --git a/openpilot/selfdrive/test/process_replay/process_replay.py b/openpilot/selfdrive/test/process_replay/process_replay.py index b34535e96b..90eb397996 100755 --- a/openpilot/selfdrive/test/process_replay/process_replay.py +++ b/openpilot/selfdrive/test/process_replay/process_replay.py @@ -215,7 +215,7 @@ class ProcessContainer: def _start_process(self): if self.capture is not None: - self.process.launcher = LauncherWithCapture(self.capture, self.process.launcher) + self.process.launcher = LauncherWithCapture(self.capture, self.process.launcher) # ty: ignore[invalid-assignment] # intentional wrapper self.process.prepare() self.process.start() @@ -631,10 +631,10 @@ def replay_process( fingerprint: str | None = None, return_all_logs: bool = False, custom_params: dict[str, Any] | None = None, captured_output_store: dict[str, dict[str, str]] | None = None, disable_progress: bool = False ) -> list[capnp._DynamicStructReader]: - if isinstance(cfg, Iterable): - cfgs = list(cfg) - else: + if isinstance(cfg, ProcessConfig): cfgs = [cfg] + else: + cfgs = list(cfg) all_msgs = migrate_all(lr, manager_states=True, diff --git a/openpilot/selfdrive/ui/body/animations.py b/openpilot/selfdrive/ui/body/animations.py index c40f7ecdef..302f8989bf 100644 --- a/openpilot/selfdrive/ui/body/animations.py +++ b/openpilot/selfdrive/ui/body/animations.py @@ -204,7 +204,10 @@ class FaceAnimator: frames_back = round(rewind_elapsed / self._animation.frame_duration) frame_index = self._rewind_from - frames_back if frame_index <= 0: - return self._switch_to_next(now) + if self._next is None: + self._rewinding = False + return self._animation.frames[0] + return self._switch_to_next(now, self._next) return self._animation.frames[frame_index] # Play starting frames first (once) @@ -223,7 +226,7 @@ class FaceAnimator: if self._next is not None: if frame_index == 0 and (len(self._animation.frames) == 1 or self._seen_nonzero): - return self._switch_to_next(now) + return self._switch_to_next(now, self._next) # No natural return to frame 0 — start rewinding if self._animation.mode in (AnimationMode.ONCE_FORWARD, AnimationMode.REPEAT_FORWARD): self._rewinding = True @@ -232,8 +235,8 @@ class FaceAnimator: return self._animation.frames[frame_index] - def _switch_to_next(self, now: float) -> list[tuple[int, int]]: - self._animation = self._next + def _switch_to_next(self, now: float, animation: Animation) -> list[tuple[int, int]]: + self._animation = animation self._next = None self._rewinding = False self._seen_nonzero = False diff --git a/openpilot/selfdrive/ui/layouts/main.py b/openpilot/selfdrive/ui/layouts/main.py index 47a52280f2..277b6f1404 100644 --- a/openpilot/selfdrive/ui/layouts/main.py +++ b/openpilot/selfdrive/ui/layouts/main.py @@ -31,7 +31,11 @@ class MainLayout(Widget): # Initialize layouts self._home_layout = HomeLayout() self._home_body_layout = BodyLayout() - self._layouts = {MainState.HOME: self._home_layout, MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()} + self._layouts: dict[MainState, Widget] = { + MainState.HOME: self._home_layout, + MainState.SETTINGS: SettingsLayout(), + MainState.ONROAD: AugmentedRoadView(), + } self._sidebar_rect = rl.Rectangle(0, 0, 0, 0) self._content_rect = rl.Rectangle(0, 0, 0, 0) diff --git a/openpilot/selfdrive/ui/layouts/sidebar.py b/openpilot/selfdrive/ui/layouts/sidebar.py index eabf5cc008..4a7c2856bb 100644 --- a/openpilot/selfdrive/ui/layouts/sidebar.py +++ b/openpilot/selfdrive/ui/layouts/sidebar.py @@ -65,7 +65,7 @@ class MetricData: class Sidebar(Widget): def __init__(self): super().__init__() - self._net_type = NETWORK_TYPES.get(NetworkType.none) + self._net_type = NETWORK_TYPES[NetworkType.none] self._net_strength = 0 self._temp_status = MetricData(tr_noop("TEMP"), tr_noop("GOOD"), Colors.GOOD) diff --git a/openpilot/selfdrive/ui/mici/layouts/home.py b/openpilot/selfdrive/ui/mici/layouts/home.py index adb02fb731..e7e99b38ba 100644 --- a/openpilot/selfdrive/ui/mici/layouts/home.py +++ b/openpilot/selfdrive/ui/mici/layouts/home.py @@ -251,7 +251,7 @@ class MiciHomeLayout(Widget): self._egpu_icon.set_visible(ui_state.usbgpu and ui_state.usbgpu_compiled) self._egpu_icon_gray.set_visible(ui_state.usbgpu and not ui_state.usbgpu_compiled) self._mic_icon.set_visible(ui_state.recording_audio) - self._body_icon.set_visible(ui_state.is_body) + self._body_icon.set_visible(bool(ui_state.is_body)) footer_rect = rl.Rectangle(self.rect.x + HOME_PADDING, self.rect.y + self.rect.height - 48, self.rect.width - HOME_PADDING, 48) self._status_bar_layout.render(footer_rect) diff --git a/openpilot/selfdrive/ui/mici/layouts/main.py b/openpilot/selfdrive/ui/mici/layouts/main.py index 26ec5555be..e592253544 100644 --- a/openpilot/selfdrive/ui/mici/layouts/main.py +++ b/openpilot/selfdrive/ui/mici/layouts/main.py @@ -146,4 +146,4 @@ class MiciMainLayout(Scroller): def _on_body_changed(self): self._car_onroad_layout.set_visible(not ui_state.is_body) - self._body_onroad_layout.set_visible(ui_state.is_body) + self._body_onroad_layout.set_visible(bool(ui_state.is_body)) diff --git a/openpilot/selfdrive/ui/mici/layouts/settings/device.py b/openpilot/selfdrive/ui/mici/layouts/settings/device.py index 0adcf53752..cd85ef3add 100644 --- a/openpilot/selfdrive/ui/mici/layouts/settings/device.py +++ b/openpilot/selfdrive/ui/mici/layouts/settings/device.py @@ -16,7 +16,7 @@ from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import Widget from openpilot.selfdrive.ui.ui_state import device, ui_state from openpilot.system.ui.widgets.label import UnifiedLabel -from openpilot.system.ui.widgets.html_render import HtmlModal, HtmlRenderer +from openpilot.system.ui.widgets.html_render import HtmlRenderer from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID @@ -160,7 +160,7 @@ class DeviceLayoutMici(NavScroller): def __init__(self): super().__init__() - self._fcc_dialog: HtmlModal | None = None + self._fcc_dialog: MiciFccModal | None = None def power_off_callback(): ui_state.params.put_bool("DoShutdown", True, block=True) diff --git a/openpilot/selfdrive/ui/mici/onroad/cameraview.py b/openpilot/selfdrive/ui/mici/onroad/cameraview.py index 991349dbf0..82e4865c76 100644 --- a/openpilot/selfdrive/ui/mici/onroad/cameraview.py +++ b/openpilot/selfdrive/ui/mici/onroad/cameraview.py @@ -219,7 +219,7 @@ class CameraView(Widget): [0.0, 0.0, 1.0] ]) - def _render(self, rect: rl.Rectangle): + def _render(self, rect: rl.Rectangle, /): if self._switching: self._handle_switch() diff --git a/openpilot/selfdrive/ui/mici/onroad/model_renderer.py b/openpilot/selfdrive/ui/mici/onroad/model_renderer.py index adf9814364..4d19850769 100644 --- a/openpilot/selfdrive/ui/mici/onroad/model_renderer.py +++ b/openpilot/selfdrive/ui/mici/onroad/model_renderer.py @@ -44,8 +44,8 @@ class ModelPoints: @dataclass class LeadVehicle: - glow: list[float] = field(default_factory=list) - chevron: list[float] = field(default_factory=list) + glow: list[tuple[float, float]] = field(default_factory=list) + chevron: list[tuple[float, float]] = field(default_factory=list) fill_alpha: int = 0 diff --git a/openpilot/selfdrive/ui/onroad/model_renderer.py b/openpilot/selfdrive/ui/onroad/model_renderer.py index 50ff3b1c53..8a40c90025 100644 --- a/openpilot/selfdrive/ui/onroad/model_renderer.py +++ b/openpilot/selfdrive/ui/onroad/model_renderer.py @@ -37,8 +37,8 @@ class ModelPoints: @dataclass class LeadVehicle: - glow: list[float] = field(default_factory=list) - chevron: list[float] = field(default_factory=list) + glow: list[tuple[float, float]] = field(default_factory=list) + chevron: list[tuple[float, float]] = field(default_factory=list) fill_alpha: int = 0 diff --git a/openpilot/selfdrive/ui/tests/diff/replay_script.py b/openpilot/selfdrive/ui/tests/diff/replay_script.py index 8517f0dece..109f32e47a 100644 --- a/openpilot/selfdrive/ui/tests/diff/replay_script.py +++ b/openpilot/selfdrive/ui/tests/diff/replay_script.py @@ -357,7 +357,7 @@ def build_mici_script(pm: PubMaster, main_layout, script: Script) -> None: params = Params() main_layout._alerts_layout._pending_params = ({"UpdaterNewDescription": params.get("UpdaterNewDescription")} | {alert_data.key: params.get(alert_data.key) for alert_data in main_layout._alerts_layout.sorted_alerts}) - main_layout._alerts_layout._refresh() + main_layout._alerts_layout._update_state() swipe_right(width, wait_after=WAIT_SHORT) # open alerts script.setup(setup_offroad_alerts_and_refresh) # show alerts diff --git a/openpilot/selfdrive/ui/tests/profile_onroad.py b/openpilot/selfdrive/ui/tests/profile_onroad.py index 29e44d6260..ec15e71c35 100755 --- a/openpilot/selfdrive/ui/tests/profile_onroad.py +++ b/openpilot/selfdrive/ui/tests/profile_onroad.py @@ -49,7 +49,7 @@ def patch_submaster(message_chunks): sm.recv_frame[service] = sm.frame sm.valid[service] = True sm.frame += 1 - ui_state.sm.update = mock_update + ui_state.sm.update = mock_update # ty: ignore[invalid-assignment] # profiling hook if __name__ == "__main__": diff --git a/openpilot/selfdrive/ui/translations/potools.py b/openpilot/selfdrive/ui/translations/potools.py index ac4dafb988..15da4c586c 100644 --- a/openpilot/selfdrive/ui/translations/potools.py +++ b/openpilot/selfdrive/ui/translations/potools.py @@ -67,22 +67,22 @@ def parse_po(path: str | Path) -> tuple[POEntry | None, list[POEntry]]: cur_field: str | None = None plural_idx = 0 - def finish(): - nonlocal cur, header - if cur is None: + def finish(entry: POEntry | None): + nonlocal header + if entry is None: return - if cur.msgid == "" and cur.msgstr: - header = cur - elif cur.msgid != "" or cur.is_plural: - entries.append(cur) - cur = None + if entry.msgid == "" and entry.msgstr: + header = entry + elif entry.msgid != "" or entry.is_plural: + entries.append(entry) for raw in lines: line = raw.rstrip('\n') stripped = line.strip() if not stripped: - finish() + finish(cur) + cur = None cur_field = None continue @@ -123,6 +123,8 @@ def parse_po(path: str | Path) -> tuple[POEntry | None, list[POEntry]]: continue if stripped.startswith('msgstr '): + if cur is None: + cur = POEntry() cur.msgstr = _parse_quoted(stripped[len('msgstr '):]) cur_field = 'msgstr' continue @@ -138,7 +140,7 @@ def parse_po(path: str | Path) -> tuple[POEntry | None, list[POEntry]]: elif cur_field == 'msgstr_plural': cur.msgstr_plural[plural_idx] += val - finish() + finish(cur) return header, entries diff --git a/openpilot/system/athena/athenad.py b/openpilot/system/athena/athenad.py index cf590afb40..91b12fc0a7 100755 --- a/openpilot/system/athena/athenad.py +++ b/openpilot/system/athena/athenad.py @@ -669,7 +669,10 @@ def log_handler(end_event: threading.Event) -> None: def ws_proxy_recv(ws: WebSocket, local_sock: socket.socket, ssock: socket.socket, end_event: threading.Event, global_end_event: threading.Event) -> None: while not (end_event.is_set() or global_end_event.is_set()): try: - r = select.select((ws.sock,), (), (), 30) + sock = ws.sock + if sock is None: + return + r = select.select((sock,), (), (), 30) if r[0]: data = ws.recv() if isinstance(data, str): diff --git a/openpilot/system/athena/tests/test_athenad.py b/openpilot/system/athena/tests/test_athenad.py index accf0784ab..8c090b93af 100644 --- a/openpilot/system/athena/tests/test_athenad.py +++ b/openpilot/system/athena/tests/test_athenad.py @@ -60,7 +60,7 @@ class TestAthenadMethods: @classmethod def setup_class(cls): cls.SOCKET_PORT = 45454 - athenad.Api = MockApi + athenad.Api = MockApi # ty: ignore[invalid-assignment] # test double athenad.LOCAL_PORT_WHITELIST = {cls.SOCKET_PORT} def setup_method(self): @@ -351,6 +351,7 @@ class TestAthenadMethods: assert items[0] == asdict(item) assert not items[0]['current'] + assert item.id is not None athenad.cancelled_uploads.add(item.id) items = dispatcher["listUploadQueue"]() assert len(items) == 0 @@ -363,6 +364,7 @@ class TestAthenadMethods: athenad.upload_queue.put_nowait(item2) # Ensure canceled items are not persisted + assert item2.id is not None athenad.cancelled_uploads.add(item2.id) # serialize item diff --git a/openpilot/system/hardware/power_monitoring.py b/openpilot/system/hardware/power_monitoring.py index 72a8c6848c..816d5e37dd 100644 --- a/openpilot/system/hardware/power_monitoring.py +++ b/openpilot/system/hardware/power_monitoring.py @@ -34,7 +34,7 @@ class PowerMonitoring: self.car_battery_capacity_uWh = max((CAR_BATTERY_CAPACITY_uWh / 10), car_battery_capacity_uWh) # Calculation tick - def calculate(self, voltage: int | None, ignition: bool): + def calculate(self, voltage: float | None, ignition: bool): try: now = time.monotonic() diff --git a/openpilot/system/hardware/tests/test_power_monitoring.py b/openpilot/system/hardware/tests/test_power_monitoring.py index 5e1b751a0f..f3ffb98e09 100644 --- a/openpilot/system/hardware/tests/test_power_monitoring.py +++ b/openpilot/system/hardware/tests/test_power_monitoring.py @@ -35,7 +35,7 @@ class TestPowerMonitoring: def test_panda_state_present(self): pm = PowerMonitoring() for _ in range(10): - pm.calculate(None, None) + pm.calculate(None, False) assert pm.get_power_used() == 0 assert pm.get_car_battery_capacity() == (CAR_BATTERY_CAPACITY_uWh / 10) diff --git a/openpilot/system/loggerd/tests/loggerd_tests_common.py b/openpilot/system/loggerd/tests/loggerd_tests_common.py index 757b61d17e..55f5fbb816 100644 --- a/openpilot/system/loggerd/tests/loggerd_tests_common.py +++ b/openpilot/system/loggerd/tests/loggerd_tests_common.py @@ -63,10 +63,10 @@ class UploaderTestCase: seg_dir: str def set_ignore(self): - uploader.Api = MockApiIgnore + uploader.Api = MockApiIgnore # ty: ignore[invalid-assignment] # test double def setup_method(self): - uploader.Api = MockApi + uploader.Api = MockApi # ty: ignore[invalid-assignment] # test double uploader.fake_upload = True uploader.force_wifi = True uploader.allow_sleep = False diff --git a/openpilot/system/loggerd/tests/test_deleter.py b/openpilot/system/loggerd/tests/test_deleter.py index 6222ea253b..8f18c8ceee 100644 --- a/openpilot/system/loggerd/tests/test_deleter.py +++ b/openpilot/system/loggerd/tests/test_deleter.py @@ -19,7 +19,7 @@ class TestDeleter(UploaderTestCase): self.f_type = "fcamera.hevc" super().setup_method() self.fake_stats = Stats(f_bavail=0, f_blocks=10, f_frsize=4096) - deleter.os.statvfs = self.fake_statvfs + deleter.os.statvfs = self.fake_statvfs # ty: ignore[invalid-assignment] # test double def start_thread(self): self.end_event = threading.Event() diff --git a/openpilot/system/loggerd/tests/test_loggerd.py b/openpilot/system/loggerd/tests/test_loggerd.py index 666b56dc03..3678ca95be 100644 --- a/openpilot/system/loggerd/tests/test_loggerd.py +++ b/openpilot/system/loggerd/tests/test_loggerd.py @@ -5,6 +5,7 @@ import random import string import subprocess import time +from collections.abc import Collection from collections import defaultdict from pathlib import Path import pytest @@ -74,8 +75,8 @@ class TestLoggerd: end_type = SentinelType.endOfRoute if route else SentinelType.endOfSegment assert msgs[-1].sentinel.type == end_type - def _publish_random_messages(self, services: list[str]) -> dict[str, list]: - pm = messaging.PubMaster(services) + def _publish_random_messages(self, services: Collection[str]) -> dict[str, list]: + pm = messaging.PubMaster(list(services)) managed_processes["loggerd"].start() for s in services: diff --git a/openpilot/system/qcomgpsd/nmeaport.py b/openpilot/system/qcomgpsd/nmeaport.py index ba7cf117b1..db1fe5552d 100644 --- a/openpilot/system/qcomgpsd/nmeaport.py +++ b/openpilot/system/qcomgpsd/nmeaport.py @@ -1,9 +1,9 @@ import os import sys -from dataclasses import dataclass, fields +from dataclasses import dataclass from subprocess import check_output, CalledProcessError from time import sleep -from typing import NoReturn, cast +from typing import NoReturn DEBUG = int(os.environ.get("DEBUG", "0")) @@ -17,27 +17,27 @@ class GnssClockNmeaPort: # 0x10 = bias_uncertainty_ns valid # 0x20 = drift_nsps valid # 0x40 = drift_uncertainty_nsps valid - flags: int - leap_seconds: int - time_ns: int - time_uncertainty_ns: int # 1-sigma - full_bias_ns: int - bias_ns: float - bias_uncertainty_ns: float # 1-sigma - drift_nsps: float - drift_uncertainty_nsps: float # 1-sigma + flags: int | None + leap_seconds: int | None + time_ns: int | None + time_uncertainty_ns: int | None # 1-sigma + full_bias_ns: int | None + bias_ns: float | None + bias_uncertainty_ns: float | None # 1-sigma + drift_nsps: float | None + drift_uncertainty_nsps: float | None # 1-sigma - def __post_init__(self): - for field in fields(self): - val = getattr(self, field.name) - field_type = cast(type, field.type) - setattr(self, field.name, field_type(val) if val else None) + @classmethod + def from_fields(cls, values: list[str]) -> 'GnssClockNmeaPort': + ints = [int(value) if value else None for value in values[:5]] + floats = [float(value) if value else None for value in values[5:9]] + return cls(*ints, *floats) @dataclass class GnssMeasNmeaPort: - messageCount: int - messageNum: int - svCount: int + messageCount: int | None + messageNum: int | None + svCount: int | None # constellation enum: # 1 = GPS # 2 = SBAS @@ -45,10 +45,10 @@ class GnssMeasNmeaPort: # 4 = QZSS # 5 = BEIDOU # 6 = GALILEO - constellation: int - svId: int - flags: int # always zero - time_offset_ns: int + constellation: int | None + svId: int | None + flags: int | None # always zero + time_offset_ns: int | None # state bit mask: # 0x0001 = CODE LOCK # 0x0002 = BIT SYNC @@ -64,18 +64,18 @@ class GnssMeasNmeaPort: # 0x0800 = GALILEO E1C 2ND CODE LOCK # 0x1000 = GALILEO E1B PAGE SYNC # 0x2000 = GALILEO E1B PAGE SYNC - state: int - time_of_week_ns: int - time_of_week_uncertainty_ns: int # 1-sigma - carrier_to_noise_ratio: float - pseudorange_rate: float - pseudorange_rate_uncertainty: float # 1-sigma + state: int | None + time_of_week_ns: int | None + time_of_week_uncertainty_ns: int | None # 1-sigma + carrier_to_noise_ratio: float | None + pseudorange_rate: float | None + pseudorange_rate_uncertainty: float | None # 1-sigma - def __post_init__(self): - for field in fields(self): - val = getattr(self, field.name) - field_type = cast(type, field.type) - setattr(self, field.name, field_type(val) if val else None) + @classmethod + def from_fields(cls, values: list[str]) -> 'GnssMeasNmeaPort': + ints = [int(value) if value else None for value in values[:10]] + floats = [float(value) if value else None for value in values[10:13]] + return cls(*ints, *floats) def nmea_checksum_ok(s): checksum = 0 @@ -109,11 +109,11 @@ def process_nmea_port_messages(device:str="/dev/ttyUSB1") -> NoReturn: match fields[0]: case "$GNCLK": # fields at end are reserved (not used) - gnss_clock = GnssClockNmeaPort(*fields[1:10]) + gnss_clock = GnssClockNmeaPort.from_fields(fields[1:10]) print(gnss_clock) case "$GNMEAS": # fields at end are reserved (not used) - gnss_meas = GnssMeasNmeaPort(*fields[1:14]) + gnss_meas = GnssMeasNmeaPort.from_fields(fields[1:14]) print(gnss_meas) except Exception as e: print(e) diff --git a/openpilot/system/sensord/tests/test_sensord.py b/openpilot/system/sensord/tests/test_sensord.py index fc4e3061bc..dc96886e4a 100644 --- a/openpilot/system/sensord/tests/test_sensord.py +++ b/openpilot/system/sensord/tests/test_sensord.py @@ -20,7 +20,7 @@ SENSOR_CONFIGS = ( ) SENSOR_CONFIGS_BY_MEASUREMENT = {config.measurement: config for config in SENSOR_CONFIGS} -def get_irq_count(irq: int): +def get_irq_count(irq: str): with open(f"/sys/kernel/irq/{irq}/per_cpu_count") as f: per_cpu = map(int, f.read().split(",")) return sum(per_cpu) diff --git a/openpilot/system/ubloxd/binary_struct.py b/openpilot/system/ubloxd/binary_struct.py index c144bd5696..5bc05094f3 100644 --- a/openpilot/system/ubloxd/binary_struct.py +++ b/openpilot/system/ubloxd/binary_struct.py @@ -184,7 +184,7 @@ class BinaryStruct: setattr(obj, name, value) return obj - cls._read = _read + cls._read = _read # ty: ignore[invalid-assignment] # installed dynamically for each subclass @classmethod def from_bytes(cls: type[T], data: bytes) -> T: diff --git a/openpilot/system/ui/lib/utils.py b/openpilot/system/ui/lib/utils.py index 77035d0da0..e97b3ba9d9 100644 --- a/openpilot/system/ui/lib/utils.py +++ b/openpilot/system/ui/lib/utils.py @@ -1,8 +1,9 @@ import pyray as rl +from collections.abc import Sequence class GuiStyleContext: - def __init__(self, styles: list[tuple[int, int, int]]): + def __init__(self, styles: Sequence[tuple[int, int, int]]): """styles is a list of tuples (control, prop, new_value)""" self.styles = styles self.prev_styles: list[tuple[int, int, int]] = [] diff --git a/openpilot/system/ui/lib/wifi_manager.py b/openpilot/system/ui/lib/wifi_manager.py index 147666bf85..c52e5a2ddf 100644 --- a/openpilot/system/ui/lib/wifi_manager.py +++ b/openpilot/system/ui/lib/wifi_manager.py @@ -257,7 +257,7 @@ class WifiManager: def add_callbacks(self, need_auth: Callable[[str], None] | None = None, activated: Callable[[], None] | None = None, - forgotten: Callable[[str], None] | None = None, + forgotten: Callable[[str | None], None] | None = None, networks_updated: Callable[[list[Network]], None] | None = None, disconnected: Callable[[], None] | None = None): if need_auth is not None: diff --git a/openpilot/system/ui/tici_reset.py b/openpilot/system/ui/tici_reset.py index 569faf45fe..5c175dbfa6 100755 --- a/openpilot/system/ui/tici_reset.py +++ b/openpilot/system/ui/tici_reset.py @@ -37,7 +37,11 @@ class Reset(Widget): self._reset_state = ResetState.NONE self._cancel_button = Button("Cancel", gui_app.request_close) self._confirm_button = Button("Confirm", self._confirm, button_style=ButtonStyle.PRIMARY) - self._reboot_button = Button("Reboot", lambda: subprocess.run("sudo reboot", shell=True)) + self._reboot_button = Button("Reboot", self._reboot) + + @staticmethod + def _reboot() -> None: + subprocess.run("sudo reboot", shell=True) def _do_erase(self): if PC: diff --git a/openpilot/system/ui/widgets/__init__.py b/openpilot/system/ui/widgets/__init__.py index 4ce1c1b694..f8cb04d8a7 100644 --- a/openpilot/system/ui/widgets/__init__.py +++ b/openpilot/system/ui/widgets/__init__.py @@ -3,16 +3,25 @@ from __future__ import annotations import abc import pyray as rl from enum import IntEnum -from typing import TypeVar +from typing import Protocol, TypeVar from collections.abc import Callable from openpilot.system.ui.lib.application import gui_app, MousePos, MAX_TOUCH_SLOTS, MouseEvent -try: - from openpilot.selfdrive.ui.ui_state import device -except ImportError: - class Device: - awake = True - device = Device() +class DeviceLike(Protocol): + awake: bool + + +def _get_device() -> DeviceLike: + try: + from openpilot.selfdrive.ui.ui_state import device + return device + except ImportError: + class Device: + awake = True + return Device() + + +device = _get_device() W = TypeVar('W', bound='Widget') @@ -185,16 +194,16 @@ class Widget(abc.ABC): """Optionally update the widget's non-layout state. This is called before rendering.""" @abc.abstractmethod - def _render(self, rect: rl.Rectangle) -> bool | int | None: + def _render(self, rect: rl.Rectangle, /) -> bool | int | None: """Render the widget within the given rectangle.""" def _update_layout_rects(self) -> None: """Optionally update any layout rects on Widget rect change.""" - def _handle_mouse_press(self, mouse_pos: MousePos) -> None: + def _handle_mouse_press(self, mouse_pos: MousePos, /) -> None: """Optionally handle mouse press events.""" - def _handle_mouse_release(self, mouse_pos: MousePos) -> None: + def _handle_mouse_release(self, mouse_pos: MousePos, /) -> None: """Optionally handle mouse release events.""" if self._click_delay is not None: self._click_release_time = rl.get_time() + self._click_delay diff --git a/openpilot/system/ui/widgets/label.py b/openpilot/system/ui/widgets/label.py index 7052b2c2a9..fdaf6f3148 100644 --- a/openpilot/system/ui/widgets/label.py +++ b/openpilot/system/ui/widgets/label.py @@ -1,7 +1,6 @@ import math from enum import IntEnum from collections.abc import Callable -from itertools import zip_longest from typing import Union import pyray as rl @@ -210,7 +209,7 @@ class Label(Widget): icon_x = self._rect.x + (self._rect.width - self._icon.width) / 2 rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE) - for text, text_size, emojis in zip_longest(self._text_wrapped, self._text_size, self._emojis, fillvalue=[]): + for text, text_size, emojis in zip(self._text_wrapped, self._text_size, self._emojis, strict=True): line_pos = rl.Vector2(text_pos.x, text_pos.y) if self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_LEFT: line_pos.x += self._text_padding diff --git a/openpilot/system/ui/widgets/nav_widget.py b/openpilot/system/ui/widgets/nav_widget.py index 11770bbe5d..58ff8123bb 100644 --- a/openpilot/system/ui/widgets/nav_widget.py +++ b/openpilot/system/ui/widgets/nav_widget.py @@ -78,7 +78,7 @@ class NavWidget(Widget, abc.ABC): # the top of a vertical scroll panel to prevent erroneous swipes return True - def set_back_callback(self, callback: Callable[[], None]) -> None: + def set_back_callback(self, callback: Callable[[], None] | None) -> None: self._back_callback = callback def set_shown_callback(self, callback: Callable[[], None] | None) -> None: diff --git a/openpilot/system/ui/widgets/network.py b/openpilot/system/ui/widgets/network.py index 710202c54f..4068d552b9 100644 --- a/openpilot/system/ui/widgets/network.py +++ b/openpilot/system/ui/widgets/network.py @@ -15,16 +15,6 @@ from openpilot.system.ui.widgets.label import gui_label from openpilot.system.ui.widgets.scroller_tici import Scroller from openpilot.system.ui.widgets.list_view import ButtonAction, ListItem, MultipleButtonAction, ToggleAction, button_item, text_item -# These are only used for AdvancedNetworkSettings, standalone apps just need WifiManagerUI -try: - from openpilot.common.params import Params - from openpilot.selfdrive.ui.ui_state import ui_state - from openpilot.selfdrive.ui.lib.prime_state import PrimeType -except Exception: - Params = None - ui_state = None - PrimeType = None - NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 MAX_PASSWORD_LENGTH = 64 @@ -105,11 +95,16 @@ class NetworkUI(Widget): class AdvancedNetworkSettings(Widget): def __init__(self, wifi_manager: WifiManager): - assert Params is not None + # AdvancedNetworkSettings needs the full openpilot environment, standalone apps just use WifiManagerUI + from openpilot.common.params import Params + from openpilot.selfdrive.ui.ui_state import ui_state + from openpilot.selfdrive.ui.lib.prime_state import PrimeType super().__init__() self._wifi_manager = wifi_manager self._wifi_manager.add_callbacks(networks_updated=self._on_network_updated) self._params = Params() + self._prime_state = ui_state.prime_state + self._cell_prime_types = (PrimeType.NONE, PrimeType.LITE) self._keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) @@ -254,7 +249,7 @@ class AdvancedNetworkSettings(Widget): self._wifi_manager.process_callbacks() # If not using prime SIM, show GSM settings and enable IPv4 forwarding - show_cell_settings = ui_state.prime_state.get_type() in (PrimeType.NONE, PrimeType.LITE) + show_cell_settings = self._prime_state.get_type() in self._cell_prime_types self._wifi_manager.set_ipv4_forward(show_cell_settings) self._roaming_btn.set_visible(show_cell_settings) self._apn_btn.set_visible(show_cell_settings) diff --git a/openpilot/system/ui/widgets/scroller.py b/openpilot/system/ui/widgets/scroller.py index b7b6bf5932..55195b30fb 100644 --- a/openpilot/system/ui/widgets/scroller.py +++ b/openpilot/system/ui/widgets/scroller.py @@ -1,6 +1,6 @@ import pyray as rl import numpy as np -from collections.abc import Callable +from collections.abc import Callable, Sequence from openpilot.common.filter_simple import FirstOrderFilter, BounceFilter from openpilot.common.swaglog import cloudlog @@ -40,7 +40,7 @@ class ScrollIndicator(Widget): self._content_size = content_size self._viewport = viewport - def _render(self, _): + def _render(self, _, /): # scale indicator width based on content size indicator_w = float(np.interp(self._content_size, [1000, 3000], [300, 100])) @@ -69,7 +69,7 @@ class ScrollIndicator(Widget): class _Scroller(Widget): """Should use wrapper below to reduce boilerplate""" - def __init__(self, items: list[Widget], horizontal: bool = True, snap_items: bool = False, spacing: int = ITEM_SPACING, + def __init__(self, items: Sequence[Widget], horizontal: bool = True, snap_items: bool = False, spacing: int = ITEM_SPACING, pad: int = ITEM_SPACING, scroll_indicator: bool = True, edge_shadows: bool = True): super().__init__() self._items: list[Widget] = [] @@ -150,7 +150,7 @@ class _Scroller(Widget): and not self.moving_items and (original_touch_valid_callback() if original_touch_valid_callback else True)) - def add_widgets(self, items: list[Widget]) -> None: + def add_widgets(self, items: Sequence[Widget]) -> None: for item in items: self.add_widget(item) @@ -332,7 +332,7 @@ class _Scroller(Widget): else: item.render() - def _render(self, _): + def _render(self, _, /): rl.begin_scissor_mode(int(self._rect.x), int(self._rect.y), int(self._rect.width), int(self._rect.height)) @@ -397,7 +397,7 @@ class Scroller(Widget): # pass down enabled to child widget for nav stack self._scroller.set_enabled(lambda: self.enabled) - def _render(self, _): + def _render(self, _, /): self._scroller.render(self._rect) diff --git a/openpilot/system/ui/widgets/scroller_tici.py b/openpilot/system/ui/widgets/scroller_tici.py index a843010d56..fc81e1b079 100644 --- a/openpilot/system/ui/widgets/scroller_tici.py +++ b/openpilot/system/ui/widgets/scroller_tici.py @@ -1,4 +1,5 @@ import pyray as rl +from collections.abc import Sequence from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.widgets import Widget @@ -23,7 +24,7 @@ class LineSeparator(Widget): class Scroller(Widget): - def __init__(self, items: list[Widget], spacing: int = ITEM_SPACING, line_separator: bool = False, pad_end: bool = True): + def __init__(self, items: Sequence[Widget], spacing: int = ITEM_SPACING, line_separator: bool = False, pad_end: bool = True): super().__init__() self._items: list[Widget] = [] self._spacing = spacing diff --git a/openpilot/system/updated/updated.py b/openpilot/system/updated/updated.py index ac8111d5c5..9610785861 100755 --- a/openpilot/system/updated/updated.py +++ b/openpilot/system/updated/updated.py @@ -357,7 +357,7 @@ class Updater: setup_git_options(OVERLAY_MERGED) output = run(["git", "ls-remote", "--heads"], OVERLAY_MERGED) - self.branches = defaultdict(lambda: None) + self.branches.clear() for line in output.split('\n'): ls_remotes_re = r'(?P\b[0-9a-f]{5,40}\b)(\s+)(refs\/heads\/)(?P.*$)' x = re.fullmatch(ls_remotes_re, line.strip()) diff --git a/openpilot/system/webrtc/tests/test_stream_session.py b/openpilot/system/webrtc/tests/test_stream_session.py index b83e1223d2..1f7bd5747a 100644 --- a/openpilot/system/webrtc/tests/test_stream_session.py +++ b/openpilot/system/webrtc/tests/test_stream_session.py @@ -55,9 +55,11 @@ class TestStreamSession: mocked_pubmaster.send.assert_called_once() mt, md = mocked_pubmaster.send.call_args.args - assert mt == msg["type"] + msg_type = msg["type"] + assert isinstance(msg_type, str) + assert mt == msg_type assert isinstance(md, capnp._DynamicStructBuilder) - assert hasattr(md, msg["type"]) + assert hasattr(md, msg_type) mocked_pubmaster.reset_mock() diff --git a/openpilot/system/webrtc/webrtcd.py b/openpilot/system/webrtc/webrtcd.py index 82ebb7b018..7901f9847f 100755 --- a/openpilot/system/webrtc/webrtcd.py +++ b/openpilot/system/webrtc/webrtcd.py @@ -519,7 +519,7 @@ class WebrtcdHandler(BaseHTTPRequestHandler): def do_OPTIONS(self) -> None: self._dispatch_request() - def log_message(self, fmt, *args) -> None: + def log_message(self, format: str, *args: object) -> None: # noqa: A002 # stdlib override # silence default access logging; errors are logged explicitly in _dispatch_request pass diff --git a/openpilot/tools/lib/auth.py b/openpilot/tools/lib/auth.py index 5988397d0a..9139d8b42d 100755 --- a/openpilot/tools/lib/auth.py +++ b/openpilot/tools/lib/auth.py @@ -54,7 +54,7 @@ class ClientRedirectHandler(BaseHTTPRequestHandler): self.end_headers() self.wfile.write(b'Return to the CLI to continue') - def log_message(self, *args): + def log_message(self, format: str, *args: object) -> None: # noqa: A002 # stdlib override pass # this prevent http server from dumping messages to stdout diff --git a/openpilot/tools/lib/file_sources.py b/openpilot/tools/lib/file_sources.py index cb7bf15114..2d2bfccfd7 100755 --- a/openpilot/tools/lib/file_sources.py +++ b/openpilot/tools/lib/file_sources.py @@ -12,7 +12,7 @@ Source = Callable[[SegmentRange, list[int], FileNames], dict[int, str]] InternalUnavailableException = Exception("Internal source not available") -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, /) -> dict[int, str]: route = Route(sr.route_name) # comma api will have already checked if the file exists @@ -22,7 +22,7 @@ def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> d return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, /, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: if not internal_source_available(endpoint_url): raise InternalUnavailableException @@ -32,11 +32,11 @@ def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, endpo return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, /) -> dict[int, str]: return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, /) -> dict[int, str]: return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) diff --git a/openpilot/tools/lib/tests/test_caching.py b/openpilot/tools/lib/tests/test_caching.py index 0753ef1d3c..c70d7fc81b 100644 --- a/openpilot/tools/lib/tests/test_caching.py +++ b/openpilot/tools/lib/tests/test_caching.py @@ -16,7 +16,7 @@ class CachingTestRequestHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): if self.FILE_EXISTS: - self.send_response(206 if "Range" in self.headers else 200, b'1234') + self.send_response(206 if "Range" in self.headers else 200, '1234') else: self.send_response(404) self.end_headers() diff --git a/openpilot/tools/longitudinal_maneuvers/maneuversd.py b/openpilot/tools/longitudinal_maneuvers/maneuversd.py index a9a5ba6304..8cdc2f26cf 100755 --- a/openpilot/tools/longitudinal_maneuvers/maneuversd.py +++ b/openpilot/tools/longitudinal_maneuvers/maneuversd.py @@ -59,7 +59,7 @@ class Maneuver: return float(action_accel) - def get_accel(self, v_ego: float, long_active: bool, standstill: bool, cruise_standstill: bool) -> float: + def get_accel(self, v_ego: float, long_active: bool, standstill: bool, cruise_standstill: bool, /) -> float: ready = abs(v_ego - self.initial_speed) < 0.3 and long_active and not cruise_standstill if self.initial_speed < 0.01: ready = ready and standstill diff --git a/openpilot/tools/replay/lib/ui_helpers.py b/openpilot/tools/replay/lib/ui_helpers.py index 8f0c14983f..b9da0d225f 100644 --- a/openpilot/tools/replay/lib/ui_helpers.py +++ b/openpilot/tools/replay/lib/ui_helpers.py @@ -5,6 +5,7 @@ import numpy as np import pyray as rl from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.artist import Artist from matplotlib.offsetbox import AnchoredOffsetbox, HPacker, TextArea from openpilot.common.transformations.camera import get_view_frame_from_calib_frame @@ -119,7 +120,7 @@ def init_plots(arr, name_to_arr_idx, plot_xlims, plot_ylims, plot_names, plot_co idxs.append(name_to_arr_idx[item]) plot_select.append(i) # Build colored title: each label colored to match its plot line - title_texts = [] + title_texts: list[Artist] = [] for j2, (nm, cl) in enumerate(zip(pl_list, plot_colors[i], strict=False)): if j2 > 0: title_texts.append(TextArea(", ", textprops={"color": "white", "fontsize": 10})) diff --git a/openpilot/tools/sim/bridge/common.py b/openpilot/tools/sim/bridge/common.py index 048bf6cb11..1d00e3b0f9 100644 --- a/openpilot/tools/sim/bridge/common.py +++ b/openpilot/tools/sim/bridge/common.py @@ -94,7 +94,7 @@ Ignition: {self.simulator_state.ignition} Engaged: {self.simulator_state.is_enga """) @abstractmethod - def spawn_world(self, q: Queue) -> World: + def spawn_world(self, q: Queue, /) -> World: pass def _run(self, q: Queue): diff --git a/openpilot/tools/sim/lib/common.py b/openpilot/tools/sim/lib/common.py index 1324932131..eb29119fb4 100644 --- a/openpilot/tools/sim/lib/common.py +++ b/openpilot/tools/sim/lib/common.py @@ -40,7 +40,7 @@ class SimulatorState: self.is_engaged = False self.ignition = True - self.velocity: vec3 = None + self.velocity = vec3(0, 0, 0) self.bearing: float = 0 self.gps = GPSState() self.imu = IMUState() @@ -72,7 +72,7 @@ class World(ABC): self.exit_event = multiprocessing.Event() @abstractmethod - def apply_controls(self, steer_sim, throttle_out, brake_out): + def apply_controls(self, steer_sim, throttle_out, brake_out, /): pass @abstractmethod @@ -84,7 +84,7 @@ class World(ABC): pass @abstractmethod - def read_sensors(self, simulator_state: SimulatorState): + def read_sensors(self, simulator_state: SimulatorState, /): pass @abstractmethod diff --git a/pyproject.toml b/pyproject.toml index 5c9f16d33a..5895a2a095 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -186,10 +186,6 @@ quote-style = "preserve" [tool.ty.rules] unresolved-import = "ignore" # Cython-compiled modules (.pyx) unresolved-attribute = "ignore" # many from capnp and Cython modules -invalid-method-override = "ignore" # signature variance issues -possibly-missing-attribute = "ignore" # too many false positives -invalid-assignment = "ignore" # often intentional monkey-patching -invalid-argument-type = "ignore" # many false positives from raylib, ctypes, numpy [tool.uv] python-preference = "only-managed"