diff --git a/.flake8 b/.flake8 index 9ca27b137..52d6e9cdf 100644 --- a/.flake8 +++ b/.flake8 @@ -7,5 +7,8 @@ max-line-length = 88 # this error conflicts with black linting # E501(line is too long): # TODO: deal with it in the future -extend-ignore = E266, E501, E203 +# E701(multiple statements on one line) +extend-ignore = E266, E501, E203, E701 extend-exclude = *_pb2.py*, *_pb2_grpc.py* +# TODO: ignore tools and tests for now +exclude = tools/**, tests/**, proto/** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 08cce5766..437250fb3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,4 +8,4 @@ This is the main entry for selecting a template for your PR. If PR type is not in the above list, feel free to start from blank PR body. -** DON'T INCLUDE THIS PAGE'S CONTENTS IN YOUR PR BODY. ** +**DON'T INCLUDE THIS PAGE'S CONTENTS IN YOUR PR BODY.** diff --git a/.github/PULL_REQUEST_TEMPLATE/feature.md b/.github/PULL_REQUEST_TEMPLATE/feature.md index f3cd29c38..d53c44a9b 100644 --- a/.github/PULL_REQUEST_TEMPLATE/feature.md +++ b/.github/PULL_REQUEST_TEMPLATE/feature.md @@ -14,7 +14,6 @@ For better understanding, adding reason/motivation of this PR are also recommend - [ ] local test is passed. - [ ] design docs/implementation docs are prepared. - ## Documents @@ -42,6 +41,7 @@ Does this PR introduce behavior change(s)? ## Breaking change Does this PR introduce breaking change? + - [ ] Yes. - [ ] No. diff --git a/.github/PULL_REQUEST_TEMPLATE/refinement.md b/.github/PULL_REQUEST_TEMPLATE/refinement.md index 2a04a4327..8fd1a9053 100644 --- a/.github/PULL_REQUEST_TEMPLATE/refinement.md +++ b/.github/PULL_REQUEST_TEMPLATE/refinement.md @@ -35,6 +35,7 @@ Does this PR introduce behavior change(s)? ## Breaking change Does this PR introduce breaking change? + - [ ] Yes. - [ ] No. diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 294043c5b..381206dde 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -4,10 +4,6 @@ on: pull_request: branches: - main - paths: - - "otaclient/**" - - "tests/**" - - ".github/workflows/test.yaml" push: branches: - main @@ -47,42 +43,3 @@ jobs: with: pytest-xml-coverage-path: test_result/coverage.xml junitxml-path: test_result/pytest.xml - - python_lint_check: - runs-on: ubuntu-20.04 - timeout-minutes: 3 - strategy: - matrix: - python-version: [3.8] - steps: - - name: Checkout commit - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install test dependencies - run: | - python -m pip install -q --upgrade pip - python -m pip install -q -r tests/requirements.txt - - name: Lint codes without modification - run: | - $pythonLocation/bin/python -m black ./otaclient --check - - name: Lint codes for syntax check - run: | - $pythonLocation/bin/python -m flake8 ./otaclient - - markdown_lint_check: - runs-on: ubuntu-20.04 - timeout-minutes: 3 - steps: - - name: Checkout commit - uses: actions/checkout@v3 - - name: markdownlint-cli - uses: nosborn/github-action-markdown-cli@v3.2.0 - with: - files: . - config_file: .markdownlint.yaml - ignore_files: .markdownlintignore - #ignore_path: examples/.markdownlintignore - #rules: examples/rules/custom.js diff --git a/.markdownlint.yaml b/.markdownlint.yaml index bd00abdbe..44fcb49ce 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -1,3 +1,4 @@ "MD013": false +"MD041": false "MD024": "siblings_only": true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b6789ede9..4348be26d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,14 +2,8 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: - - id: detect-private-key - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.10.0 - hooks: - - id: python-check-mock-methods - - id: python-use-type-annotations # Using this mirror lets us use mypyc-compiled black, which is about 2x faster - repo: https://github.com/psf/black-pre-commit-mirror rev: 24.4.2 @@ -38,5 +32,16 @@ repos: - id: pyproject-fmt # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: ["tox>=4.9"] + # - repo: https://github.com/codespell-project/codespell + # rev: v2.2.4 + # hooks: + # - id: codespell + # additional_dependencies: + # - tomli + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.40.0 + hooks: + - id: markdownlint + args: ["-c", ".markdownlint.yaml", "--fix"] ci: autoupdate_schedule: monthly diff --git a/otaclient/app/boot_control/_common.py b/otaclient/app/boot_control/_common.py index 253a8342c..189287405 100644 --- a/otaclient/app/boot_control/_common.py +++ b/otaclient/app/boot_control/_common.py @@ -16,6 +16,7 @@ from __future__ import annotations +import contextlib import logging import shutil import sys @@ -23,8 +24,12 @@ from subprocess import CalledProcessError from typing import Callable, Literal, NoReturn, Optional, Union -from ..common import (read_str_from_file, subprocess_call, - subprocess_check_output, write_str_to_file_sync) +from ..common import ( + read_str_from_file, + subprocess_call, + subprocess_check_output, + write_str_to_file_sync, +) from ..configs import config as cfg from ..proto import wrapper @@ -554,10 +559,9 @@ def _load_current_status(self) -> Optional[wrapper.StatusOta]: if _status_str := read_str_from_file( self.current_ota_status_dir / cfg.OTA_STATUS_FNAME ).upper(): - try: + with contextlib.suppress(KeyError): + # invalid status string return wrapper.StatusOta[_status_str] - except KeyError: - pass # invalid status string # version control diff --git a/otaclient/app/boot_control/_grub.py b/otaclient/app/boot_control/_grub.py index a927241c2..90766691c 100644 --- a/otaclient/app/boot_control/_grub.py +++ b/otaclient/app/boot_control/_grub.py @@ -43,11 +43,20 @@ from typing import ClassVar, Dict, Generator, List, Optional, Tuple from .. import errors as ota_errors -from ..common import (re_symlink_atomic, read_str_from_file, subprocess_call, - subprocess_check_output, write_str_to_file_sync) +from ..common import ( + re_symlink_atomic, + read_str_from_file, + subprocess_call, + subprocess_check_output, + write_str_to_file_sync, +) from ..proto import wrapper -from ._common import (CMDHelperFuncs, OTAStatusFilesControl, SlotMountHelper, - cat_proc_cmdline) +from ._common import ( + CMDHelperFuncs, + OTAStatusFilesControl, + SlotMountHelper, + cat_proc_cmdline, +) from .configs import grub_cfg as cfg from .protocol import BootControllerProtocol @@ -160,20 +169,19 @@ def update_entry_rootfs( entry_l, entry_r = entry.span() entry_block = entry.group() # parse the entry block - if _linux := cls.linux_pa.search(entry_block): - if _linux.group("ver") == kernel_ver: - linux_line_l, linux_line_r = _linux.span() - _new_linux_line, _count = cls.rootfs_pa.subn( - rootfs_str, _linux.group() + if (_linux := cls.linux_pa.search(entry_block)) and _linux.group( + "ver" + ) == kernel_ver: + linux_line_l, linux_line_r = _linux.span() + _new_linux_line, _count = cls.rootfs_pa.subn(rootfs_str, _linux.group()) + if _count == 1: + # replace rootfs string + new_entry_block = ( + f"{entry_block[:linux_line_l]}" + f"{_new_linux_line}" + f"{entry_block[linux_line_r:]}" ) - if _count == 1: - # replace rootfs string - new_entry_block = ( - f"{entry_block[:linux_line_l]}" - f"{_new_linux_line}" - f"{entry_block[linux_line_r:]}" - ) - break + break if new_entry_block is not None: updated_grub_cfg = ( @@ -200,9 +208,10 @@ def get_entry(cls, grub_cfg: str, *, kernel_ver: str) -> Tuple[int, _GrubMenuEnt recovery entry). """ for index, entry_ma in enumerate(cls.menuentry_pa.finditer(grub_cfg)): - if _linux := cls.linux_pa.search(entry_ma.group()): - if kernel_ver == _linux.group("ver"): - return index, _GrubMenuEntry(entry_ma) + if ( + _linux := cls.linux_pa.search(entry_ma.group()) + ) and kernel_ver == _linux.group("ver"): + return index, _GrubMenuEntry(entry_ma) raise ValueError(f"requested entry for {kernel_ver} not found") @@ -331,13 +340,12 @@ def _get_sibling_dev(self, active_dev: str) -> str: # FSTYPE="ext4" and # not (parent_device_file, root_device_file and boot_device_file) for blk in output: - if m := re.search(r'NAME="(.*)"\s+FSTYPE="(.*)"', blk): - if ( - m.group(1) != active_dev - and m.group(1) != boot_dev - and m.group(2) == "ext4" - ): - return m.group(1) + if (m := re.search(r'NAME="(.*)"\s+FSTYPE="(.*)"', blk)) and ( + m.group(1) != active_dev + and m.group(1) != boot_dev + and m.group(2) == "ext4" + ): + return m.group(1) _err_msg = f"{parent=} has unexpected partition layout: {output=}" logger.error(_err_msg) diff --git a/otaclient/app/boot_control/_jetson_cboot.py b/otaclient/app/boot_control/_jetson_cboot.py index c857f7999..dbbe8dc42 100644 --- a/otaclient/app/boot_control/_jetson_cboot.py +++ b/otaclient/app/boot_control/_jetson_cboot.py @@ -28,8 +28,11 @@ from typing_extensions import Annotated, Self from otaclient.app import errors as ota_errors -from otaclient.app.common import (copytree_identical, subprocess_run_wrapper, - write_str_to_file_sync) +from otaclient.app.common import ( + copytree_identical, + subprocess_run_wrapper, + write_str_to_file_sync, +) from otaclient.app.proto import wrapper from ._common import CMDHelperFuncs, OTAStatusFilesControl, SlotMountHelper diff --git a/otaclient/app/boot_control/_rpi_boot.py b/otaclient/app/boot_control/_rpi_boot.py index 6f596624c..3c78ae94f 100644 --- a/otaclient/app/boot_control/_rpi_boot.py +++ b/otaclient/app/boot_control/_rpi_boot.py @@ -26,8 +26,12 @@ from .. import errors as ota_errors from ..common import replace_atomic, subprocess_call, subprocess_check_output from ..proto import wrapper -from ._common import (CMDHelperFuncs, OTAStatusFilesControl, SlotMountHelper, - write_str_to_file_sync) +from ._common import ( + CMDHelperFuncs, + OTAStatusFilesControl, + SlotMountHelper, + write_str_to_file_sync, +) from .configs import rpi_boot_cfg as cfg from .protocol import BootControllerProtocol @@ -118,12 +122,10 @@ def _init_slots_info(self): try: # NOTE: exclude the first 2 lines(parent and system-boot) - _child_partitions = list( - map( - lambda _raw: _raw.split("=")[-1].strip('"'), - _raw_child_partitions.splitlines()[2:], - ) - ) + _child_partitions = [ + raw.split("=")[-1].strip('"') + for raw in _raw_child_partitions.splitlines()[2:] + ] if ( len(_child_partitions) != 2 or self._active_slot_dev not in _child_partitions diff --git a/otaclient/app/common.py b/otaclient/app/common.py index 611eeaa37..08e874917 100644 --- a/otaclient/app/common.py +++ b/otaclient/app/common.py @@ -29,14 +29,28 @@ from hashlib import sha256 from pathlib import Path from queue import Queue -from typing import (Any, Callable, Generator, Generic, Iterable, NamedTuple, - Optional, Set, TypeVar, Union) +from typing import ( + Any, + Callable, + Generator, + Generic, + Iterable, + NamedTuple, + Optional, + Set, + TypeVar, + Union, +) from urllib.parse import urljoin import requests -from otaclient._utils.linux import (ParsedGroup, ParsedPasswd, - map_gid_by_grpnam, map_uid_by_pwnam) +from otaclient._utils.linux import ( + ParsedGroup, + ParsedPasswd, + map_gid_by_grpnam, + map_uid_by_pwnam, +) from .configs import config as cfg diff --git a/otaclient/app/configs.py b/otaclient/app/configs.py index fbd072d38..f7a0ed725 100644 --- a/otaclient/app/configs.py +++ b/otaclient/app/configs.py @@ -17,10 +17,8 @@ from logging import INFO from typing import Dict, Tuple -from otaclient.configs.ecu_info import ecu_info -from otaclient.configs.proxy_info import proxy_info - -ecu_info, proxy_info = ecu_info, proxy_info # to prevent static check warnings +from otaclient.configs.ecu_info import ecu_info # noqa +from otaclient.configs.proxy_info import proxy_info # noqa class CreateStandbyMechanism(Enum): diff --git a/otaclient/app/create_standby/common.py b/otaclient/app/create_standby/common.py index e5ffcaa3e..dd2b51e53 100644 --- a/otaclient/app/create_standby/common.py +++ b/otaclient/app/create_standby/common.py @@ -14,6 +14,9 @@ r"""Common used helpers, classes and functions for different bank creating methods.""" +from __future__ import annotations + +import contextlib import logging import os import random @@ -23,16 +26,18 @@ from hashlib import sha256 from pathlib import Path from threading import Event, Lock -from typing import (Any, Dict, Iterator, List, Optional, OrderedDict, Set, - Tuple, Union) +from typing import Any, Dict, Iterator, List, Optional, OrderedDict, Set, Tuple, Union from weakref import WeakKeyDictionary, WeakValueDictionary from ..common import create_tmp_fname from ..configs import config as cfg from ..ota_metadata import MetafilesV1, OTAMetadata from ..proto.wrapper import DirectoryInf, RegularInf -from ..update_stats import (OTAUpdateStatsCollector, RegInfProcessedStats, - RegProcessOperation) +from ..update_stats import ( + OTAUpdateStatsCollector, + RegInfProcessedStats, + RegProcessOperation, +) logger = logging.getLogger(__name__) @@ -67,12 +72,10 @@ def subscribe(self) -> str: time.sleep(self.POLLINTERVAL) - try: + # it won't happen generally as this tracker will be gc + # after the ref holder holds no more ref. + with contextlib.suppress(IndexError): self._ref_holder.pop() - except IndexError: - # it won't happen generally as this tracker will be gc - # after the ref holder holds no more ref. - pass return self.first_copy_path @@ -210,15 +213,13 @@ def _gen(): class DeltaGenerator: # entry under the following folders will be scanned # no matter it is existed in new image or not - FULL_SCAN_PATHS = set( - [ - "/lib", - "/var/lib", - "/usr", - "/opt/nvidia", - "/home/autoware/autoware.proj", - ] - ) + FULL_SCAN_PATHS = { + "/lib", + "/var/lib", + "/usr", + "/opt/nvidia", + "/home/autoware/autoware.proj", + } # introduce limitations here to prevent unexpected # scanning in unknown large, deep folders in full @@ -376,10 +377,8 @@ def _process_delta_src(self): "exceeded files will be ignored silently" ) self._rm.extend( - map( - lambda x: str(canonical_curdir_path / x), - filenames[self.MAX_FILENUM_PER_FOLDER :], - ) + str(canonical_curdir_path / x) + for x in filenames[self.MAX_FILENUM_PER_FOLDER :] ) # process the files under this dir diff --git a/otaclient/app/create_standby/rebuild_mode.py b/otaclient/app/create_standby/rebuild_mode.py index ae7d26912..b8f7e0a24 100644 --- a/otaclient/app/create_standby/rebuild_mode.py +++ b/otaclient/app/create_standby/rebuild_mode.py @@ -25,8 +25,11 @@ from ..configs import config as cfg from ..ota_metadata import MetafilesV1, OTAMetadata from ..proto.wrapper import RegularInf -from ..update_stats import (OTAUpdateStatsCollector, RegInfProcessedStats, - RegProcessOperation) +from ..update_stats import ( + OTAUpdateStatsCollector, + RegInfProcessedStats, + RegProcessOperation, +) from .common import DeltaBundle, DeltaGenerator, HardlinkRegister from .interface import StandbySlotCreatorProtocol diff --git a/otaclient/app/downloader.py b/otaclient/app/downloader.py index 41cdaf1fd..34ef9190e 100644 --- a/otaclient/app/downloader.py +++ b/otaclient/app/downloader.py @@ -24,8 +24,19 @@ from functools import wraps from hashlib import sha256 from os import PathLike -from typing import (IO, Any, ByteString, Callable, Dict, Iterator, Mapping, - Optional, Protocol, Tuple, Union) +from typing import ( + IO, + Any, + ByteString, + Callable, + Dict, + Iterator, + Mapping, + Optional, + Protocol, + Tuple, + Union, +) from urllib.parse import urlsplit import requests @@ -414,7 +425,7 @@ def _check_against_cache_policy_in_resp( Returns: A tuple of file_sha256 and file_compression_alg for the requested resources. """ - if not (cache_policy_str := resp_headers.get(CACHE_CONTROL_HEADER, None)): + if not (cache_policy_str := resp_headers.get(CACHE_CONTROL_HEADER)): return digest, compression_alg cache_policy = OTAFileCacheControl.parse_header(cache_policy_str) diff --git a/otaclient/app/log_setting.py b/otaclient/app/log_setting.py index cdc82ab3e..a60629685 100644 --- a/otaclient/app/log_setting.py +++ b/otaclient/app/log_setting.py @@ -16,6 +16,7 @@ from __future__ import annotations import atexit +import contextlib import logging from queue import Queue from threading import Event, Thread @@ -37,10 +38,8 @@ def __init__(self, max_backlog: int = 2048) -> None: self._queue: Queue[str | None] = Queue(maxsize=max_backlog) def emit(self, record: logging.LogRecord) -> None: - try: + with contextlib.suppress(Exception): self._queue.put_nowait(self.format(record)) - except Exception: - pass def start_upload_thread(self, endpoint_url: str): log_queue = self._queue @@ -56,10 +55,8 @@ def _thread_main(): if not entry: continue # skip uploading empty log line - try: + with contextlib.suppress(Exception): _session.post(endpoint_url, data=entry, timeout=3) - except Exception: - pass log_upload_thread = Thread(target=_thread_main, daemon=True) log_upload_thread.start() diff --git a/otaclient/app/ota_client.py b/otaclient/app/ota_client.py index 0c3879f1d..17b104b7a 100644 --- a/otaclient/app/ota_client.py +++ b/otaclient/app/ota_client.py @@ -16,6 +16,7 @@ from __future__ import annotations import asyncio +import contextlib import gc import json import logging @@ -35,18 +36,24 @@ from . import errors as ota_errors from . import ota_metadata from .boot_control import BootControllerProtocol, get_boot_controller -from .common import (PersistFilesHandler, RetryTaskMap, - RetryTaskMapInterrupted, ensure_otaproxy_start, - get_backoff) +from .common import ( + PersistFilesHandler, + RetryTaskMap, + RetryTaskMapInterrupted, + ensure_otaproxy_start, + get_backoff, +) from .configs import config as cfg from .configs import ecu_info -from .create_standby import (StandbySlotCreatorProtocol, - get_standby_slot_creator) +from .create_standby import StandbySlotCreatorProtocol, get_standby_slot_creator from .interface import OTAClientProtocol from .ota_status import LiveOTAStatus from .proto import wrapper -from .update_stats import (OTAUpdateStatsCollector, RegInfProcessedStats, - RegProcessOperation) +from .update_stats import ( + OTAUpdateStatsCollector, + RegInfProcessedStats, + RegProcessOperation, +) try: from otaclient import __version__ # type: ignore @@ -773,7 +780,8 @@ async def _update_task(): if self._otaclient_inst is None: return - try: + # error should be collected by otaclient, not us + with contextlib.suppress(Exception): await self._run_in_executor( partial( self._otaclient_inst.update, @@ -782,11 +790,8 @@ async def _update_task(): request.cookies, ) ) - except Exception: - pass # error should be collected by otaclient, not us - finally: - self.last_operation = None - self._update_rollback_lock.release() + self.last_operation = None + self._update_rollback_lock.release() # dispatch update to background asyncio.create_task(_update_task()) @@ -821,13 +826,11 @@ async def _rollback_task(): if self._otaclient_inst is None: return - try: + # error should be collected by otaclient, not us + with contextlib.suppress(Exception): await self._run_in_executor(self._otaclient_inst.rollback) - except Exception: - pass # error should be collected by otaclient, not us - finally: - self.last_operation = None - self._update_rollback_lock.release() + self.last_operation = None + self._update_rollback_lock.release() # dispatch to background asyncio.create_task(_rollback_task()) diff --git a/otaclient/app/ota_client_stub.py b/otaclient/app/ota_client_stub.py index 68ff0217c..ff932c0cd 100644 --- a/otaclient/app/ota_client_stub.py +++ b/otaclient/app/ota_client_stub.py @@ -397,13 +397,11 @@ async def _generate_overall_status_report(self): # NOTE(20230801): this property is calculated against all reachable ECUs, # including further child ECUs. _old_lost_ecus_id = self.lost_ecus_id - self.lost_ecus_id = lost_ecus = set( - ( - ecu_id - for ecu_id in self._all_ecus_last_contact_timestamp - if self._is_ecu_lost(ecu_id, cur_timestamp) - ) - ) + self.lost_ecus_id = lost_ecus = { + ecu_id + for ecu_id in self._all_ecus_last_contact_timestamp + if self._is_ecu_lost(ecu_id, cur_timestamp) + } if _new_lost_ecus_id := lost_ecus.difference(_old_lost_ecus_id): logger.warning(f"new lost ecu(s) detected: {_new_lost_ecus_id}") if lost_ecus: @@ -413,17 +411,15 @@ async def _generate_overall_status_report(self): # check ECUs in tracked active ECUs set that are updating _old_in_update_ecus_id = self.in_update_ecus_id - self.in_update_ecus_id = in_update_ecus_id = set( - ( - status.ecu_id - for status in chain( - self._all_ecus_status_v2.values(), self._all_ecus_status_v1.values() - ) - if status.ecu_id in self._tracked_active_ecus - and status.is_in_update - and status.ecu_id not in lost_ecus + self.in_update_ecus_id = in_update_ecus_id = { + status.ecu_id + for status in chain( + self._all_ecus_status_v2.values(), self._all_ecus_status_v1.values() ) - ) + if status.ecu_id in self._tracked_active_ecus + and status.is_in_update + and status.ecu_id not in lost_ecus + } self.in_update_child_ecus_id = in_update_ecus_id - {self.my_ecu_id} if _new_in_update_ecu := in_update_ecus_id.difference(_old_in_update_ecus_id): logger.info( @@ -437,17 +433,15 @@ async def _generate_overall_status_report(self): # check if there is any failed child/self ECU in tracked active ECUs set _old_failed_ecus_id = self.failed_ecus_id - self.failed_ecus_id = set( - ( - status.ecu_id - for status in chain( - self._all_ecus_status_v2.values(), self._all_ecus_status_v1.values() - ) - if status.ecu_id in self._tracked_active_ecus - and status.is_failed - and status.ecu_id not in lost_ecus + self.failed_ecus_id = { + status.ecu_id + for status in chain( + self._all_ecus_status_v2.values(), self._all_ecus_status_v1.values() ) - ) + if status.ecu_id in self._tracked_active_ecus + and status.is_failed + and status.ecu_id not in lost_ecus + } if _new_failed_ecu := self.failed_ecus_id.difference(_old_failed_ecus_id): logger.warning( f"new failed ECU(s) detected: {_new_failed_ecu}, current {self.failed_ecus_id=}" @@ -467,17 +461,15 @@ async def _generate_overall_status_report(self): # check if all tracked active_ota_ecus are in SUCCESS ota_status _old_all_success, _old_success_ecus_id = self.all_success, self.success_ecus_id - self.success_ecus_id = set( - ( - status.ecu_id - for status in chain( - self._all_ecus_status_v2.values(), self._all_ecus_status_v1.values() - ) - if status.ecu_id in self._tracked_active_ecus - and status.is_success - and status.ecu_id not in lost_ecus + self.success_ecus_id = { + status.ecu_id + for status in chain( + self._all_ecus_status_v2.values(), self._all_ecus_status_v1.values() ) - ) + if status.ecu_id in self._tracked_active_ecus + and status.is_success + and status.ecu_id not in lost_ecus + } # NOTE: all_success doesn't count the lost ECUs self.all_success = len(self.success_ecus_id) == len(self._tracked_active_ecus) if _new_success_ecu := self.success_ecus_id.difference(_old_success_ecus_id): @@ -653,7 +645,7 @@ async def export(self) -> wrapper.StatusResponse: continue _ecu_status_rep = self._all_ecus_status_v2.get( - ecu_id, self._all_ecus_status_v1.get(ecu_id, None) + ecu_id, self._all_ecus_status_v1.get(ecu_id) ) if _ecu_status_rep: res.add_ecu(_ecu_status_rep) diff --git a/otaclient/app/ota_metadata.py b/otaclient/app/ota_metadata.py index c3d6fa487..d0c2651cc 100644 --- a/otaclient/app/ota_metadata.py +++ b/otaclient/app/ota_metadata.py @@ -51,8 +51,21 @@ from os import PathLike from pathlib import Path from tempfile import NamedTemporaryFile, TemporaryDirectory -from typing import (Any, Callable, ClassVar, Dict, Generic, Iterator, List, - Optional, Tuple, Type, TypeVar, Union, overload) +from typing import ( + Any, + Callable, + ClassVar, + Dict, + Generic, + Iterator, + List, + Optional, + Tuple, + Type, + TypeVar, + Union, + overload, +) from urllib.parse import quote from OpenSSL import crypto @@ -63,10 +76,14 @@ from .common import RetryTaskMap, get_backoff, urljoin_ensure_base from .configs import config as cfg from .downloader import Downloader -from .proto.streamer import (Uint32LenDelimitedMsgReader, - Uint32LenDelimitedMsgWriter) -from .proto.wrapper import (DirectoryInf, MessageWrapper, PersistentInf, - RegularInf, SymbolicLinkInf) +from .proto.streamer import Uint32LenDelimitedMsgReader, Uint32LenDelimitedMsgWriter +from .proto.wrapper import ( + DirectoryInf, + MessageWrapper, + PersistentInf, + RegularInf, + SymbolicLinkInf, +) logger = logging.getLogger(__name__) diff --git a/otaclient/app/proto/_common.py b/otaclient/app/proto/_common.py index 38dd48db7..27d607a6e 100644 --- a/otaclient/app/proto/_common.py +++ b/otaclient/app/proto/_common.py @@ -20,9 +20,22 @@ from enum import EnumMeta, IntEnum from functools import update_wrapper from io import StringIO -from typing import (Any, Dict, Generic, Iterable, List, Mapping, Optional, - Type, TypeVar, Union, get_args, get_origin, get_type_hints, - overload) +from typing import ( + Any, + Dict, + Generic, + Iterable, + List, + Mapping, + Optional, + Type, + TypeVar, + Union, + get_args, + get_origin, + get_type_hints, + overload, +) from google.protobuf.duration_pb2 import Duration as _Duration from google.protobuf.message import Message as _pb_Message @@ -585,7 +598,10 @@ def __init_subclass__(cls) -> None: - Parse type annotations defined in wrapper class. - bypass the user defined __init__. """ - if not (_orig_bases := getattr(cls, "__orig_bases__")) or len(_orig_bases) < 1: + if ( + not (_orig_bases := getattr(cls, "__orig_bases__", None)) + or len(_orig_bases) < 1 + ): raise TypeError("MessageWrapper should have type arg") # MessageWrapper should be the last in __mro__ _typed_msg_wrapper = _orig_bases[-1] @@ -633,7 +649,7 @@ def __init_subclass__(cls) -> None: # function name, annotations, module, etc. def _dummy_init(self, /, **_): ... - setattr(cls, "__init__", update_wrapper(_dummy_init, cls.__init__)) + cls.__init__ = update_wrapper(_dummy_init, cls.__init__).__get__(cls) # noqa def __new__(cls, /, **kwargs) -> Self: _inst = super().__new__(cls) @@ -658,10 +674,10 @@ def __setitem__(self, __name: str, __value: Any): def __eq__(self, __o: object) -> bool: if self.__class__ != __o.__class__: return False - for _field_name in self._fields: - if getattr(self, _field_name) != getattr(__o, _field_name): - return False - return True + return all( + getattr(self, _field_name) == getattr(__o, _field_name) + for _field_name in self._fields + ) def __str__(self) -> str: _buffer = StringIO() diff --git a/otaclient/app/proto/_otaclient_v2_pb2_wrapper.py b/otaclient/app/proto/_otaclient_v2_pb2_wrapper.py index a4de6c2fa..cc6409d08 100644 --- a/otaclient/app/proto/_otaclient_v2_pb2_wrapper.py +++ b/otaclient/app/proto/_otaclient_v2_pb2_wrapper.py @@ -33,9 +33,14 @@ import otaclient_v2_pb2 as _v2 from typing_extensions import Self -from ._common import (Duration, EnumWrapper, MessageWrapper, - RepeatedCompositeContainer, RepeatedScalarContainer, - calculate_slots) +from ._common import ( + Duration, + EnumWrapper, + MessageWrapper, + RepeatedCompositeContainer, + RepeatedScalarContainer, + calculate_slots, +) # protocols @@ -570,13 +575,11 @@ def __init__( @cached_property def ecus_acked_update(self) -> _Set[str]: - return set( - [ - ecu_resp.ecu_id - for ecu_resp in self.ecu - if ecu_resp.result is FailureType.NO_FAILURE - ] - ) + return { + ecu_resp.ecu_id + for ecu_resp in self.ecu + if ecu_resp.result is FailureType.NO_FAILURE + } def merge_from(self, update_response: _Union[Self, _v2.UpdateResponse]): if isinstance(update_response, _v2.UpdateResponse): diff --git a/otaclient/app/proto/otaclient_v2_pb2.py b/otaclient/app/proto/otaclient_v2_pb2.py index f7dc2e51a..17a67aaa8 100644 --- a/otaclient/app/proto/otaclient_v2_pb2.py +++ b/otaclient/app/proto/otaclient_v2_pb2.py @@ -12,8 +12,7 @@ _sym_db = _symbol_database.Default() -from google.protobuf import \ - duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( b'\n\x12otaclient_v2.proto\x12\x0bOtaClientV2\x1a\x1egoogle/protobuf/duration.proto"Q\n\x10UpdateRequestEcu\x12\x0e\n\x06\x65\x63u_id\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x0b\n\x03url\x18\x03 \x01(\t\x12\x0f\n\x07\x63ookies\x18\x04 \x01(\t";\n\rUpdateRequest\x12*\n\x03\x65\x63u\x18\x01 \x03(\x0b\x32\x1d.OtaClientV2.UpdateRequestEcu"M\n\x11UpdateResponseEcu\x12\x0e\n\x06\x65\x63u_id\x18\x01 \x01(\t\x12(\n\x06result\x18\x02 \x01(\x0e\x32\x18.OtaClientV2.FailureType"=\n\x0eUpdateResponse\x12+\n\x03\x65\x63u\x18\x01 \x03(\x0b\x32\x1e.OtaClientV2.UpdateResponseEcu"$\n\x12RollbackRequestEcu\x12\x0e\n\x06\x65\x63u_id\x18\x01 \x01(\t"?\n\x0fRollbackRequest\x12,\n\x03\x65\x63u\x18\x01 \x03(\x0b\x32\x1f.OtaClientV2.RollbackRequestEcu"O\n\x13RollbackResponseEcu\x12\x0e\n\x06\x65\x63u_id\x18\x01 \x01(\t\x12(\n\x06result\x18\x02 \x01(\x0e\x32\x18.OtaClientV2.FailureType"A\n\x10RollbackResponse\x12-\n\x03\x65\x63u\x18\x01 \x03(\x0b\x32 .OtaClientV2.RollbackResponseEcu"\x0f\n\rStatusRequest"\xf6\x04\n\x0eStatusProgress\x12/\n\x05phase\x18\x01 \x01(\x0e\x32 .OtaClientV2.StatusProgressPhase\x12\x1b\n\x13total_regular_files\x18\x02 \x01(\x04\x12\x1f\n\x17regular_files_processed\x18\x03 \x01(\x04\x12\x1c\n\x14\x66iles_processed_copy\x18\x04 \x01(\x04\x12\x1c\n\x14\x66iles_processed_link\x18\x05 \x01(\x04\x12 \n\x18\x66iles_processed_download\x18\x06 \x01(\x04\x12 \n\x18\x66ile_size_processed_copy\x18\x07 \x01(\x04\x12 \n\x18\x66ile_size_processed_link\x18\x08 \x01(\x04\x12$\n\x1c\x66ile_size_processed_download\x18\t \x01(\x04\x12\x34\n\x11\x65lapsed_time_copy\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11\x65lapsed_time_link\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15\x65lapsed_time_download\x18\x0c \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x17\n\x0f\x65rrors_download\x18\r \x01(\x04\x12\x1f\n\x17total_regular_file_size\x18\x0e \x01(\x04\x12\x35\n\x12total_elapsed_time\x18\x0f \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x16\n\x0e\x64ownload_bytes\x18\x10 \x01(\x04"\xb3\x01\n\x06Status\x12&\n\x06status\x18\x01 \x01(\x0e\x32\x16.OtaClientV2.StatusOta\x12)\n\x07\x66\x61ilure\x18\x02 \x01(\x0e\x32\x18.OtaClientV2.FailureType\x12\x16\n\x0e\x66\x61ilure_reason\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12-\n\x08progress\x18\x05 \x01(\x0b\x32\x1b.OtaClientV2.StatusProgress"r\n\x11StatusResponseEcu\x12\x0e\n\x06\x65\x63u_id\x18\x01 \x01(\t\x12(\n\x06result\x18\x02 \x01(\x0e\x32\x18.OtaClientV2.FailureType\x12#\n\x06status\x18\x03 \x01(\x0b\x32\x13.OtaClientV2.Status"\x8e\x01\n\x0eStatusResponse\x12/\n\x03\x65\x63u\x18\x01 \x03(\x0b\x32\x1e.OtaClientV2.StatusResponseEcuB\x02\x18\x01\x12\x19\n\x11\x61vailable_ecu_ids\x18\x02 \x03(\t\x12\x30\n\x06\x65\x63u_v2\x18\x03 \x03(\x0b\x32 .OtaClientV2.StatusResponseEcuV2"\x87\x03\n\x13StatusResponseEcuV2\x12\x0e\n\x06\x65\x63u_id\x18\x01 \x01(\t\x12\x18\n\x10\x66irmware_version\x18\x02 \x01(\t\x12\x19\n\x11otaclient_version\x18\x03 \x01(\t\x12*\n\nota_status\x18\x0b \x01(\x0e\x32\x16.OtaClientV2.StatusOta\x12\x33\n\x0c\x66\x61ilure_type\x18\x0c \x01(\x0e\x32\x18.OtaClientV2.FailureTypeH\x00\x88\x01\x01\x12\x1b\n\x0e\x66\x61ilure_reason\x18\r \x01(\tH\x01\x88\x01\x01\x12\x1e\n\x11\x66\x61ilure_traceback\x18\x0e \x01(\tH\x02\x88\x01\x01\x12\x35\n\rupdate_status\x18\x0f \x01(\x0b\x32\x19.OtaClientV2.UpdateStatusH\x03\x88\x01\x01\x42\x0f\n\r_failure_typeB\x11\n\x0f_failure_reasonB\x14\n\x12_failure_tracebackB\x10\n\x0e_update_statusJ\x04\x08\x04\x10\x0bJ\x04\x08\x10\x10\x11"\xdd\x05\n\x0cUpdateStatus\x12\x1f\n\x17update_firmware_version\x18\x01 \x01(\t\x12%\n\x1dtotal_files_size_uncompressed\x18\x02 \x01(\x04\x12\x17\n\x0ftotal_files_num\x18\x03 \x01(\x04\x12\x1e\n\x16update_start_timestamp\x18\x04 \x01(\x04\x12\'\n\x05phase\x18\x0b \x01(\x0e\x32\x18.OtaClientV2.UpdatePhase\x12 \n\x18total_download_files_num\x18\x0c \x01(\x04\x12!\n\x19total_download_files_size\x18\r \x01(\x04\x12\x1c\n\x14\x64ownloaded_files_num\x18\x0e \x01(\x04\x12\x18\n\x10\x64ownloaded_bytes\x18\x0f \x01(\x04\x12\x1d\n\x15\x64ownloaded_files_size\x18\x10 \x01(\x04\x12\x1a\n\x12\x64ownloading_errors\x18\x11 \x01(\x04\x12\x1e\n\x16total_remove_files_num\x18\x12 \x01(\x04\x12\x19\n\x11removed_files_num\x18\x13 \x01(\x04\x12\x1b\n\x13processed_files_num\x18\x14 \x01(\x04\x12\x1c\n\x14processed_files_size\x18\x15 \x01(\x04\x12\x35\n\x12total_elapsed_time\x18\x1f \x01(\x0b\x32\x19.google.protobuf.Duration\x12@\n\x1d\x64\x65lta_generating_elapsed_time\x18 \x01(\x0b\x32\x19.google.protobuf.Duration\x12;\n\x18\x64ownloading_elapsed_time\x18! \x01(\x0b\x32\x19.google.protobuf.Duration\x12?\n\x1cupdate_applying_elapsed_time\x18" \x01(\x0b\x32\x19.google.protobuf.Duration*A\n\x0b\x46\x61ilureType\x12\x0e\n\nNO_FAILURE\x10\x00\x12\x0f\n\x0bRECOVERABLE\x10\x01\x12\x11\n\rUNRECOVERABLE\x10\x02*k\n\tStatusOta\x12\x0f\n\x0bINITIALIZED\x10\x00\x12\x0b\n\x07SUCCESS\x10\x01\x12\x0b\n\x07\x46\x41ILURE\x10\x02\x12\x0c\n\x08UPDATING\x10\x03\x12\x0f\n\x0bROLLBACKING\x10\x04\x12\x14\n\x10ROLLBACK_FAILURE\x10\x05*~\n\x13StatusProgressPhase\x12\x0b\n\x07INITIAL\x10\x00\x12\x0c\n\x08METADATA\x10\x01\x12\r\n\tDIRECTORY\x10\x02\x12\x0b\n\x07SYMLINK\x10\x03\x12\x0b\n\x07REGULAR\x10\x04\x12\x0e\n\nPERSISTENT\x10\x05\x12\x13\n\x0fPOST_PROCESSING\x10\x06*\xb1\x01\n\x0bUpdatePhase\x12\x10\n\x0cINITIALIZING\x10\x00\x12\x17\n\x13PROCESSING_METADATA\x10\x01\x12\x15\n\x11\x43\x41LCULATING_DELTA\x10\x02\x12\x19\n\x15\x44OWNLOADING_OTA_FILES\x10\x03\x12\x13\n\x0f\x41PPLYING_UPDATE\x10\x04\x12\x19\n\x15PROCESSING_POSTUPDATE\x10\x05\x12\x15\n\x11\x46INALIZING_UPDATE\x10\x06\x32\xe7\x01\n\x10OtaClientService\x12\x43\n\x06Update\x12\x1a.OtaClientV2.UpdateRequest\x1a\x1b.OtaClientV2.UpdateResponse"\x00\x12I\n\x08Rollback\x12\x1c.OtaClientV2.RollbackRequest\x1a\x1d.OtaClientV2.RollbackResponse"\x00\x12\x43\n\x06Status\x12\x1a.OtaClientV2.StatusRequest\x1a\x1b.OtaClientV2.StatusResponse"\x00\x42\x06\xa2\x02\x03OTAb\x06proto3' diff --git a/otaclient/app/proto/streamer.py b/otaclient/app/proto/streamer.py index b35943bec..fb887dc01 100644 --- a/otaclient/app/proto/streamer.py +++ b/otaclient/app/proto/streamer.py @@ -44,7 +44,7 @@ def read1_data(self) -> Optional[bytes]: return _bin = self._stream.read(data_len) - if not len(_bin) == data_len: + if len(_bin) != data_len: return # partial read detected, the stream is incompleted return _bin diff --git a/otaclient/configs/ecu_info.py b/otaclient/configs/ecu_info.py index 54a28f95e..50b4c8815 100644 --- a/otaclient/configs/ecu_info.py +++ b/otaclient/configs/ecu_info.py @@ -26,8 +26,7 @@ from pydantic import AfterValidator, BeforeValidator, Field, IPvAnyAddress from typing_extensions import Annotated -from otaclient._utils.typing import (NetworkPort, StrOrPath, - gen_strenum_validator) +from otaclient._utils.typing import NetworkPort, StrOrPath, gen_strenum_validator from otaclient.configs._common import BaseFixedConfig logger = logging.getLogger(__name__) diff --git a/otaclient/ota_proxy/orm.py b/otaclient/ota_proxy/orm.py index 023594693..7141ccffd 100644 --- a/otaclient/ota_proxy/orm.py +++ b/otaclient/ota_proxy/orm.py @@ -15,11 +15,22 @@ from __future__ import annotations -from abc import ABC from dataclasses import asdict, astuple, dataclass, fields from io import StringIO -from typing import (TYPE_CHECKING, Any, Callable, Dict, Generic, List, - Optional, Tuple, Type, TypeVar, Union, overload) +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Generic, + List, + Optional, + Tuple, + Type, + TypeVar, + Union, + overload, +) from typing_extensions import Self, dataclass_transform @@ -27,7 +38,7 @@ import sqlite3 -class NULL_TYPE(ABC): +class NULL_TYPE: """Singleton for NULL type.""" def __new__(cls, *args, **kwargs) -> None: @@ -42,7 +53,7 @@ def __new__(cls, *args, **kwargs) -> None: bool, # INTEGER 0, 1 NULL_TYPE, # NULL ] -SQLITE_DATATYPES_SET = set([int, str, float, bytes, bool, NULL_TYPE]) +SQLITE_DATATYPES_SET = {int, str, float, bytes, bool, NULL_TYPE} FV = TypeVar("FV", bound=SQLITE_DATATYPES) # field value type TYPE_CHECKER = Callable[[Any], bool] @@ -73,7 +84,7 @@ def __init__( # init type checker callable # default to check over the specific field type - self.type_guard_enabled = False if type_guard is False else True + self.type_guard_enabled = bool(type_guard) self.type_checker = lambda x: isinstance(x, field_type) if isinstance(type_guard, tuple): # check over a list of types self.type_checker = lambda x: isinstance(x, type_guard) diff --git a/otaclient/ota_proxy/ota_cache.py b/otaclient/ota_proxy/ota_cache.py index ffd738613..7a4920eb1 100644 --- a/otaclient/ota_proxy/ota_cache.py +++ b/otaclient/ota_proxy/ota_cache.py @@ -17,6 +17,7 @@ import asyncio import bisect +import contextlib import logging import os import shutil @@ -25,9 +26,21 @@ import weakref from concurrent.futures import Executor, ThreadPoolExecutor from pathlib import Path -from typing import (AsyncGenerator, AsyncIterator, Callable, Coroutine, Dict, - Generic, List, Mapping, MutableMapping, Optional, Tuple, - TypeVar, Union) +from typing import ( + AsyncGenerator, + AsyncIterator, + Callable, + Coroutine, + Dict, + Generic, + List, + Mapping, + MutableMapping, + Optional, + Tuple, + TypeVar, + Union, +) from urllib.parse import SplitResult, quote, urlsplit import aiofiles @@ -38,9 +51,13 @@ from .cache_control import OTAFileCacheControl from .config import config as cfg from .db import AIO_OTACacheDBProxy, CacheMeta, OTACacheDB -from .errors import (BaseOTACacheError, CacheMultiStreamingFailed, - CacheStreamingFailed, CacheStreamingInterrupt, - StorageReachHardLimit) +from .errors import ( + BaseOTACacheError, + CacheMultiStreamingFailed, + CacheStreamingFailed, + CacheStreamingInterrupt, + StorageReachHardLimit, +) from .utils import read_file, url_based_hash, wait_with_backoff logger = logging.getLogger(__name__) @@ -332,10 +349,8 @@ async def provider_start(self, meta: CacheMeta): async def provider_on_finished(self): if not self.writer_finished and self._cache_write_gen: - try: + with contextlib.suppress(StopAsyncIteration): await self._cache_write_gen.asend(b"") - except StopAsyncIteration: - pass self._writer_finished.set() self._ref = None @@ -343,10 +358,8 @@ async def provider_on_failed(self): """Manually fail and stop the caching.""" if not self.writer_finished and self._cache_write_gen: logger.warning(f"interrupt writer coroutine for {self.meta=}") - try: + with contextlib.suppress(StopAsyncIteration, CacheStreamingInterrupt): await self._cache_write_gen.athrow(CacheStreamingInterrupt) - except (StopAsyncIteration, CacheStreamingInterrupt): - pass self._writer_failed.set() self._writer_finished.set() diff --git a/otaclient/ota_proxy/server_app.py b/otaclient/ota_proxy/server_app.py index 4ff5a7119..2349ff284 100644 --- a/otaclient/ota_proxy/server_app.py +++ b/otaclient/ota_proxy/server_app.py @@ -24,12 +24,21 @@ from otaclient._utils.logging import BurstSuppressFilter -from ._consts import (BHEADER_AUTHORIZATION, BHEADER_CONTENT_ENCODING, - BHEADER_COOKIE, BHEADER_OTA_FILE_CACHE_CONTROL, - HEADER_AUTHORIZATION, HEADER_CONTENT_ENCODING, - HEADER_COOKIE, HEADER_OTA_FILE_CACHE_CONTROL, METHOD_GET, - REQ_TYPE_HTTP, REQ_TYPE_LIFESPAN, RESP_TYPE_BODY, - RESP_TYPE_START) +from ._consts import ( + BHEADER_AUTHORIZATION, + BHEADER_CONTENT_ENCODING, + BHEADER_COOKIE, + BHEADER_OTA_FILE_CACHE_CONTROL, + HEADER_AUTHORIZATION, + HEADER_CONTENT_ENCODING, + HEADER_COOKIE, + HEADER_OTA_FILE_CACHE_CONTROL, + METHOD_GET, + REQ_TYPE_HTTP, + REQ_TYPE_LIFESPAN, + RESP_TYPE_BODY, + RESP_TYPE_START, +) from .errors import BaseOTACacheError from .ota_cache import OTACache diff --git a/pyproject.toml b/pyproject.toml index 4ceac2e51..3569e108d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ requires = [ [project] name = "otaclient" readme = "README.md" -license = {text = "LICENSE.md"} +license = { text = "LICENSE.md" } requires-python = ">=3.8" classifiers = [ "License :: OSI Approved :: Apache Software License", @@ -29,10 +29,10 @@ dynamic = [ Source = "https://github.com/tier4/ota-client" [tool.setuptools.dynamic] -dependencies = {file = ["otaclient/requirements.txt"]} +dependencies = { file = ["otaclient/requirements.txt"] } [tool.setuptools.dynamic.optional-dependencies] -test = {file = ["tests/requirements.txt"]} +test = { file = ["tests/requirements.txt"] } [tool.setuptools.packages.find] include = ["otaclient*"] @@ -47,9 +47,13 @@ local_scheme = "no-local-version" line-length = 88 target-version = ['py38'] extend-exclude = '''( - (_pb2.pyi?|_pb2_grpc.pyi?)$ + ^.*(_pb2.pyi?|_pb2_grpc.pyi?)$ )''' +[tool.isort] +profile = "black" +extend_skip_glob = ["*_pb2.py*", "_pb2_grpc.py*"] + [tool.pytest.ini_options] asyncio_mode = "auto" log_auto_indent = true @@ -65,7 +69,7 @@ relative_files = true source = ["otaclient"] [tool.coverage.report] -omit = ["**/*_pb2.py*","**/*_pb2_grpc.py*"] +omit = ["**/*_pb2.py*", "**/*_pb2_grpc.py*"] exclude_also = [ "def __repr__", "if __name__ == .__main__.:", @@ -78,5 +82,5 @@ skip_covered = true [tool.pyright] exclude = ["**/__pycache__"] -ignore = ["**/*_pb2.py*","**/*_pb2_grpc.py*"] +ignore = ["**/*_pb2.py*", "**/*_pb2_grpc.py*"] pythonVersion = "3.8" diff --git a/tests/test_boot_control/test_grub.py b/tests/test_boot_control/test_grub.py index b835db589..b8560e837 100644 --- a/tests/test_boot_control/test_grub.py +++ b/tests/test_boot_control/test_grub.py @@ -219,8 +219,7 @@ def mock_setup( mocker: pytest_mock.MockerFixture, grub_ab_slot, ): - from otaclient.app.boot_control._common import (CMDHelperFuncs, - SlotMountHelper) + from otaclient.app.boot_control._common import CMDHelperFuncs, SlotMountHelper from otaclient.app.boot_control._grub import GrubABPartitionDetector # ------ start fsm ------ # diff --git a/tests/test_boot_control/test_jetson_cboot.py b/tests/test_boot_control/test_jetson_cboot.py index 5a4527740..001f7517d 100644 --- a/tests/test_boot_control/test_jetson_cboot.py +++ b/tests/test_boot_control/test_jetson_cboot.py @@ -25,10 +25,13 @@ import pytest from otaclient.app.boot_control import _jetson_cboot -from otaclient.app.boot_control._jetson_cboot import (BSPVersion, - FirmwareBSPVersion, - SlotID, _CBootControl, - parse_bsp_version) +from otaclient.app.boot_control._jetson_cboot import ( + BSPVersion, + FirmwareBSPVersion, + SlotID, + _CBootControl, + parse_bsp_version, +) logger = logging.getLogger(__name__) diff --git a/tests/test_common.py b/tests/test_common.py index 7919cfde7..d745faf76 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -26,12 +26,20 @@ import pytest -from otaclient.app.common import (RetryTaskMap, RetryTaskMapInterrupted, - copytree_identical, ensure_otaproxy_start, - file_sha256, get_backoff, re_symlink_atomic, - read_str_from_file, subprocess_call, - subprocess_check_output, verify_file, - write_str_to_file_sync) +from otaclient.app.common import ( + RetryTaskMap, + RetryTaskMapInterrupted, + copytree_identical, + ensure_otaproxy_start, + file_sha256, + get_backoff, + re_symlink_atomic, + read_str_from_file, + subprocess_call, + subprocess_check_output, + verify_file, + write_str_to_file_sync, +) from tests.conftest import run_http_server from tests.utils import compare_dir diff --git a/tests/test_downloader.py b/tests/test_downloader.py index e2d1bcada..b11324d53 100644 --- a/tests/test_downloader.py +++ b/tests/test_downloader.py @@ -25,11 +25,15 @@ import requests_mock from otaclient.app.common import file_sha256, urljoin_ensure_base -from otaclient.app.downloader import (ChunkStreamingError, - DestinationNotAvailableError, Downloader, - DownloadError, ExceedMaxRetryError, - HashVerificaitonError, - UnhandledHTTPError) +from otaclient.app.downloader import ( + ChunkStreamingError, + DestinationNotAvailableError, + Downloader, + DownloadError, + ExceedMaxRetryError, + HashVerificaitonError, + UnhandledHTTPError, +) from tests.conftest import TestConfiguration as cfg from tests.utils import zstd_compress_file diff --git a/tests/test_ecu_info.py b/tests/test_ecu_info.py index 1ba739e43..20354f62d 100644 --- a/tests/test_ecu_info.py +++ b/tests/test_ecu_info.py @@ -19,8 +19,13 @@ import pytest -from otaclient.configs.ecu_info import (DEFAULT_ECU_INFO, BootloaderType, - ECUContact, ECUInfo, parse_ecu_info) +from otaclient.configs.ecu_info import ( + DEFAULT_ECU_INFO, + BootloaderType, + ECUContact, + ECUInfo, + parse_ecu_info, +) @pytest.mark.parametrize( diff --git a/tests/test_ota_client.py b/tests/test_ota_client.py index 746a25a63..5496d647a 100644 --- a/tests/test_ota_client.py +++ b/tests/test_ota_client.py @@ -31,10 +31,13 @@ from otaclient.app.create_standby import StandbySlotCreatorProtocol from otaclient.app.create_standby.common import DeltaBundle, RegularDelta from otaclient.app.errors import OTAErrorRecoverable -from otaclient.app.ota_client import (OTAClient, OTAClientControlFlags, - OTAServicer, _OTAUpdater) -from otaclient.app.ota_metadata import (parse_dirs_from_txt, - parse_regulars_from_txt) +from otaclient.app.ota_client import ( + OTAClient, + OTAClientControlFlags, + OTAServicer, + _OTAUpdater, +) +from otaclient.app.ota_metadata import parse_dirs_from_txt, parse_regulars_from_txt from otaclient.app.proto import wrapper from otaclient.app.proto.wrapper import DirectoryInf, RegularInf from otaclient.configs.ecu_info import ECUInfo diff --git a/tests/test_ota_client_stub.py b/tests/test_ota_client_stub.py index b4eea874c..e16293d1a 100644 --- a/tests/test_ota_client_stub.py +++ b/tests/test_ota_client_stub.py @@ -26,9 +26,11 @@ from otaclient.app.ota_client import OTAServicer from otaclient.app.ota_client_call import OtaClientCall -from otaclient.app.ota_client_stub import (ECUStatusStorage, - OTAClientServiceStub, - OTAProxyLauncher) +from otaclient.app.ota_client_stub import ( + ECUStatusStorage, + OTAClientServiceStub, + OTAProxyLauncher, +) from otaclient.app.proto import wrapper from otaclient.configs.ecu_info import ECUInfo, parse_ecu_info from otaclient.configs.proxy_info import ProxyInfo, parse_proxy_info diff --git a/tests/test_ota_metadata.py b/tests/test_ota_metadata.py index 9cf4df3af..eb5bb502d 100644 --- a/tests/test_ota_metadata.py +++ b/tests/test_ota_metadata.py @@ -27,13 +27,15 @@ from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec -from otaclient.app.ota_metadata import (MetadataJWTPayloadInvalid, - MetadataJWTVerificationFailed, - _MetadataJWTParser, - parse_dirs_from_txt, - parse_persistents_from_txt, - parse_regulars_from_txt, - parse_symlinks_from_txt) +from otaclient.app.ota_metadata import ( + MetadataJWTPayloadInvalid, + MetadataJWTVerificationFailed, + _MetadataJWTParser, + parse_dirs_from_txt, + parse_persistents_from_txt, + parse_regulars_from_txt, + parse_symlinks_from_txt, +) HEADER = """\ {"alg": "ES256"}\ diff --git a/tests/test_ota_proxy/test_cachedb.py b/tests/test_ota_proxy/test_cachedb.py index a5966d3a7..7c3d3c3fd 100644 --- a/tests/test_ota_proxy/test_cachedb.py +++ b/tests/test_ota_proxy/test_cachedb.py @@ -33,8 +33,7 @@ class TestORM: @pytest.fixture(autouse=True) def create_table_defs(self): - from otaclient.ota_proxy.orm import (NULL_TYPE, ColumnDescriptor, - ORMBase) + from otaclient.ota_proxy.orm import NULL_TYPE, ColumnDescriptor, ORMBase @dataclass class TableCls(ORMBase): diff --git a/tests/test_ota_proxy/test_subprocess_launch_otaproxy.py b/tests/test_ota_proxy/test_subprocess_launch_otaproxy.py index f7e441485..512a2bed3 100644 --- a/tests/test_ota_proxy/test_subprocess_launch_otaproxy.py +++ b/tests/test_ota_proxy/test_subprocess_launch_otaproxy.py @@ -18,8 +18,7 @@ from pathlib import Path from typing import Any, Dict -from otaclient.ota_proxy import (OTAProxyContextProto, - subprocess_otaproxy_launcher) +from otaclient.ota_proxy import OTAProxyContextProto, subprocess_otaproxy_launcher class _DummyOTAProxyContext(OTAProxyContextProto): diff --git a/tests/test_proto/example_pb2.py b/tests/test_proto/example_pb2.py index 622331863..d826b71cf 100644 --- a/tests/test_proto/example_pb2.py +++ b/tests/test_proto/example_pb2.py @@ -12,8 +12,7 @@ _sym_db = _symbol_database.Default() -from google.protobuf import \ - duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( b'\n\rexample.proto\x1a\x1egoogle/protobuf/duration.proto"\x9e\x01\n\x0cInnerMessage\x12\x11\n\tint_field\x18\x01 \x01(\r\x12\x14\n\x0c\x64ouble_field\x18\x02 \x01(\x01\x12\x11\n\tstr_field\x18\x03 \x01(\t\x12\x31\n\x0e\x64uration_field\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x1f\n\nenum_field\x18\x05 \x01(\x0e\x32\x0b.SampleEnum"\x99\x03\n\x0cOuterMessage\x12\x1d\n\x15repeated_scalar_field\x18\x01 \x03(\t\x12/\n\x18repeated_composite_field\x18\x02 \x03(\x0b\x32\r.InnerMessage\x12!\n\nnested_msg\x18\x03 \x01(\x0b\x32\r.InnerMessage\x12\x43\n\x14mapping_scalar_field\x18\x04 \x03(\x0b\x32%.OuterMessage.MappingScalarFieldEntry\x12I\n\x17mapping_composite_field\x18\x05 \x03(\x0b\x32(.OuterMessage.MappingCompositeFieldEntry\x1a\x39\n\x17MappingScalarFieldEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1aK\n\x1aMappingCompositeFieldEntry\x12\x0b\n\x03key\x18\x01 \x01(\r\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.InnerMessage:\x02\x38\x01*3\n\nSampleEnum\x12\x0b\n\x07VALUE_0\x10\x00\x12\x0b\n\x07VALUE_1\x10\x01\x12\x0b\n\x07VALUE_2\x10\x02\x62\x06proto3' diff --git a/tests/test_proto/example_pb2_wrapper.py b/tests/test_proto/example_pb2_wrapper.py index 4e06f0b2a..45ae7cd04 100644 --- a/tests/test_proto/example_pb2_wrapper.py +++ b/tests/test_proto/example_pb2_wrapper.py @@ -18,11 +18,16 @@ from typing import Optional as _Optional from typing import Union as _Union -from otaclient.app.proto.wrapper import (Duration, EnumWrapper, - MessageMapContainer, MessageWrapper, - RepeatedCompositeContainer, - RepeatedScalarContainer, - ScalarMapContainer, calculate_slots) +from otaclient.app.proto.wrapper import ( + Duration, + EnumWrapper, + MessageMapContainer, + MessageWrapper, + RepeatedCompositeContainer, + RepeatedScalarContainer, + ScalarMapContainer, + calculate_slots, +) from . import example_pb2 as _pb2 diff --git a/tests/test_proxy_info.py b/tests/test_proxy_info.py index 2193ca79e..9fa0d1def 100644 --- a/tests/test_proxy_info.py +++ b/tests/test_proxy_info.py @@ -20,8 +20,7 @@ import pytest -from otaclient.configs.proxy_info import (DEFAULT_PROXY_INFO, ProxyInfo, - parse_proxy_info) +from otaclient.configs.proxy_info import DEFAULT_PROXY_INFO, ProxyInfo, parse_proxy_info logger = logging.getLogger(__name__) diff --git a/tests/test_update_stats.py b/tests/test_update_stats.py index 6e978a633..835d5c71a 100644 --- a/tests/test_update_stats.py +++ b/tests/test_update_stats.py @@ -18,9 +18,11 @@ import pytest -from otaclient.app.update_stats import (OTAUpdateStatsCollector, - RegInfProcessedStats, - RegProcessOperation) +from otaclient.app.update_stats import ( + OTAUpdateStatsCollector, + RegInfProcessedStats, + RegProcessOperation, +) logger = logging.getLogger(__name__) diff --git a/tools/emulator/main.py b/tools/emulator/main.py index ce61a5bf9..8c859d54e 100644 --- a/tools/emulator/main.py +++ b/tools/emulator/main.py @@ -24,8 +24,12 @@ from configs import config as cfg from configs import server_cfg from ecu import Ecu -from ota_client_service import (OtaClientServiceV2, service_start, - service_stop, service_wait_for_termination) +from ota_client_service import ( + OtaClientServiceV2, + service_start, + service_stop, + service_wait_for_termination, +) from ota_client_stub import OtaClientStub logger = log_setting.get_logger( diff --git a/tools/status_monitor/main_win.py b/tools/status_monitor/main_win.py index 06f75388a..10bc556ae 100644 --- a/tools/status_monitor/main_win.py +++ b/tools/status_monitor/main_win.py @@ -19,8 +19,13 @@ from .configs import config, key_mapping from .ecu_status_box import ECUStatusDisplayBox -from .utils import (ASCII_NUM_MAPPING, PAGE_SCROLL_KEYS, init_pad, - page_scroll_key_handler, reset_scr) +from .utils import ( + ASCII_NUM_MAPPING, + PAGE_SCROLL_KEYS, + init_pad, + page_scroll_key_handler, + reset_scr, +) class MainScreen: