diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 58a28af0..595bcc25 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -24,9 +24,10 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies + # these extra packages are required by pylint to validate the python imports run: | python -m pip install --upgrade pip - pip install 'pylint>=3.3.1' + pip install 'pylint>=3.3.2' defusedxml requests pymavlink pillow numpy matplotlib pyserial - name: Analysing the code with pylint run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 36b08ad0..6450c90c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,7 @@ +--- +minimum_pre_commit_version: 3.3.0 +default_install_hook_types: [pre-commit, pre-push] + repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 @@ -26,21 +30,6 @@ repos: types_or: [ python ] stages: [pre-commit] - - repo: local - hooks: - - id: pylint - name: pylint - entry: pylint - language: system - types: [python] - args: - [ - "-rn", # Only display messages - "-sn", # Don't display the score - "--rcfile=.pylintrc", # Link to your config file - ] - stages: [pre-push] - - repo: https://github.com/gruntwork-io/pre-commit rev: v0.1.24 hooks: @@ -61,3 +50,19 @@ repos: # rev: v1.1.389 # hooks: # - id: pyright + + - repo: local + hooks: + - id: pylint + name: pylint + entry: pylint + language: system + types: [python] + args: + [ + "-rn", # Only display messages + "-sn", # Don't display the score + "--rcfile=.pylintrc", # Link to your config file + ] + stages: [pre-push] + diff --git a/.pylintrc b/.pylintrc index ea319e99..ebe5fb9f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -2,7 +2,7 @@ max-line-length=127 [MESSAGES CONTROL] -disable=missing-function-docstring,import-error +disable=missing-function-docstring enable=useless-suppression fail-on=useless-suppression diff --git a/.vscode/settings.json b/.vscode/settings.json index d812a810..32592270 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,5 +23,6 @@ }, "editor.defaultFormatter": "charliermarsh.ruff" }, - "ruff.lint.run": "onSave" + "ruff.lint.run": "onSave", + "files.eol": "\n" } \ No newline at end of file diff --git a/README.md b/README.md index 297fcc86..b65a411c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ SPDX-License-Identifier: GPL-3.0-or-later | ---- | ------- | ---- | ------ | -------- | | [![Pylint](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/pylint.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/pylint.yml) | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/720794ed54014c58b9eaf7a097a4e98e)](https://app.codacy.com/gh/amilcarlucas/MethodicConfigurator/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) | [![Python unit-tests](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/unit-tests.yml) | [![pages-build-deployment](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/pages/pages-build-deployment) | [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/ArduPilot/MethodicConfigurator.svg)](http://isitmaintained.com/project/ArduPilot/MethodicConfigurator) | | [![test Python cleanliness](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/ruff.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/ruff.yml) | [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9101/badge)](https://www.bestpractices.dev/projects/9101) | [![Pytest unittests](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/unittests.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/unittests.yml) | [![Upload MethodicConfigurator Package](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/python-publish.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/python-publish.yml) | [![Percentage of issues still open](http://isitmaintained.com/badge/open/ArduPilot/MethodicConfigurator.svg)](http://isitmaintained.com/project/ArduPilot/MethodicConfigurator) | -| [![mypy](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/mypy.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/mypy.yml) | [![Known Vulnerabilities](https://snyk.io/test/github/amilcarlucas/MethodicConfigurator/badge.svg)](https://snyk.io/test/github/amilcarlucas/MethodicConfigurator) | [![codecov](https://codecov.io/github/amilcarlucas/MethodicConfigurator/graph/badge.svg?token=76P928EOL2)](https://codecov.io/github/amilcarlucas/MethodicConfigurator) | [![Windows Build](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/windows_build.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/windows_build.yml) | | +| [![mypy](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/mypy.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/mypy.yml) | [![Known Vulnerabilities](https://snyk.io/test/github/amilcarlucas/MethodicConfigurator/badge.svg)](https://app.snyk.io/org/amilcarlucas/project/c8fd6e29-715b-4949-b828-64eff84f5fe1) | [![codecov](https://codecov.io/github/amilcarlucas/MethodicConfigurator/graph/badge.svg?token=76P928EOL2)](https://codecov.io/github/amilcarlucas/MethodicConfigurator) | [![Windows Build](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/windows_build.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/windows_build.yml) | | | [![markdown](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/markdown-lint.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/markdown-lint.yml) | [![Code Climate](https://codeclimate.com/github/amilcarlucas/MethodicConfigurator.png)](https://codeclimate.com/github/amilcarlucas/MethodicConfigurator) | [![Coverity Scan Build Status](https://scan.coverity.com/projects/30346/badge.svg)](https://scan.coverity.com/projects/ardupilot-methodic-configurator) | | | | [![md-link-check](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/markdown-link-check.yml/badge.svg)](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/markdown-link-check.yml) | | | | | diff --git a/ardupilot_methodic_configurator/backend_filesystem_program_settings.py b/ardupilot_methodic_configurator/backend_filesystem_program_settings.py index ccceff17..5ac10e13 100644 --- a/ardupilot_methodic_configurator/backend_filesystem_program_settings.py +++ b/ardupilot_methodic_configurator/backend_filesystem_program_settings.py @@ -121,7 +121,7 @@ def __site_config_dir() -> str: return site_config_directory @staticmethod - def __get_settings_as_dict() -> dict[str, Any]: + def __get_settings_as_dict() -> dict[str, Any]: # type: ignore[misc] settings_path = os_path.join(ProgramSettings.__user_config_dir(), "settings.json") settings = {} @@ -154,14 +154,14 @@ def __get_settings_as_dict() -> dict[str, Any]: return settings @staticmethod - def __set_settings_from_dict(settings: dict) -> None: + def __set_settings_from_dict(settings: dict) -> None: # type: ignore[misc] settings_path = os_path.join(ProgramSettings.__user_config_dir(), "settings.json") with open(settings_path, "w", encoding="utf-8") as settings_file: json_dump(settings, settings_file, indent=4) @staticmethod - def __get_settings_config() -> tuple[dict[str, Any], str, str]: + def __get_settings_config() -> tuple[dict[str, Any], str, str]: # type: ignore[misc] settings = ProgramSettings.__get_settings_as_dict() # Regular expression pattern to match single backslashes diff --git a/ardupilot_methodic_configurator/backend_flightcontroller.py b/ardupilot_methodic_configurator/backend_flightcontroller.py index 891c9740..a53497f9 100644 --- a/ardupilot_methodic_configurator/backend_flightcontroller.py +++ b/ardupilot_methodic_configurator/backend_flightcontroller.py @@ -491,7 +491,7 @@ def reset_and_reconnect( return self.__create_connection_with_retry(connection_progress_callback) @staticmethod - def __list_serial_ports() -> list[serial.tools.list_ports_common.ListPortInfo]: + def __list_serial_ports() -> list[serial.tools.list_ports_common.ListPortInfo]: # type: ignore[misc] """List all available serial ports.""" comports = serial.tools.list_ports.comports() for port in comports: diff --git a/ardupilot_methodic_configurator/backend_flightcontroller_info.py b/ardupilot_methodic_configurator/backend_flightcontroller_info.py index 1a38e486..8cb8ab5b 100644 --- a/ardupilot_methodic_configurator/backend_flightcontroller_info.py +++ b/ardupilot_methodic_configurator/backend_flightcontroller_info.py @@ -122,7 +122,7 @@ def set_capabilities(self, capabilities: int) -> None: @staticmethod def __decode_flight_sw_version(flight_sw_version: int) -> tuple[int, int, int, str]: """ - decode 32 bit flight_sw_version mavlink parameter + Decode 32 bit flight_sw_version mavlink parameter corresponds to ArduPilot encoding in GCS_MAVLINK::send_autopilot_version. """ fw_type_id = (flight_sw_version >> 0) % 256 # E221, E222 diff --git a/ardupilot_methodic_configurator/backend_mavftp.py b/ardupilot_methodic_configurator/backend_mavftp.py index 1c6643ad..1627ac9f 100755 --- a/ardupilot_methodic_configurator/backend_mavftp.py +++ b/ardupilot_methodic_configurator/backend_mavftp.py @@ -159,13 +159,13 @@ class ParamData: """A class to manage parameter values and defaults for ArduPilot configuration.""" def __init__(self) -> None: - self.params: list[tuple[str, float, type]] = [] # params as (name, value, ptype) - self.defaults: Union[None, list[tuple[str, float, type]]] = None # defaults as (name, value, ptype) + self.params: list[tuple[bytes, float, type]] = [] # params as (name, value, ptype) + self.defaults: Union[None, list[tuple[bytes, float, type]]] = None # defaults as (name, value, ptype) - def add_param(self, name, value, ptype) -> None: + def add_param(self, name: bytes, value: float, ptype: type) -> None: self.params.append((name, value, ptype)) - def add_default(self, name, value, ptype) -> None: + def add_default(self, name: bytes, value: float, ptype: type) -> None: if self.defaults is None: self.defaults = [] self.defaults.append((name, value, ptype)) @@ -1221,7 +1221,7 @@ def __decode_ftp_ack_and_nack(self, op: FTP_OP, operation_name: str = "") -> MAV ) @staticmethod - def ftp_param_decode(data) -> Union[None, ParamData]: # pylint: disable=too-many-locals + def ftp_param_decode(data: bytes) -> Union[None, ParamData]: # pylint: disable=too-many-locals """Decode parameter data, returning ParamData.""" pdata = ParamData() @@ -1302,7 +1302,7 @@ def missionplanner_sort(item: str) -> tuple[str, ...]: return tuple(item.split("_")) @staticmethod - def extract_params(pdata, sort_type) -> dict[str, tuple[float, str]]: + def extract_params(pdata: list[tuple[bytes, float, type]], sort_type: str) -> dict[str, tuple[float, type]]: """Extract parameter values to an optionally sorted dictionary of name->(value, type).""" pdict = {} if pdata: diff --git a/ardupilot_methodic_configurator/battery_cell_voltages.py b/ardupilot_methodic_configurator/battery_cell_voltages.py index 4475455b..8585b24c 100644 --- a/ardupilot_methodic_configurator/battery_cell_voltages.py +++ b/ardupilot_methodic_configurator/battery_cell_voltages.py @@ -66,7 +66,7 @@ class BatteryCell: """ @staticmethod - def chemistries() -> list: + def chemistries() -> list[str]: return list(battery_cell_voltages.keys()) @staticmethod diff --git a/ardupilot_methodic_configurator/frontend_tkinter_component_editor.py b/ardupilot_methodic_configurator/frontend_tkinter_component_editor.py index d3527feb..2ec6ad49 100755 --- a/ardupilot_methodic_configurator/frontend_tkinter_component_editor.py +++ b/ardupilot_methodic_configurator/frontend_tkinter_component_editor.py @@ -303,7 +303,9 @@ def set_vehicle_configuration_template(self, configuration_template: str) -> Non self.data["Configuration template"] = configuration_template @staticmethod - def reverse_key_search(doc: dict, param_name: str, values: list, fallbacks: list) -> list: + def reverse_key_search( + doc: dict[str, dict[str, dict[str, float]]], param_name: str, values: list[float], fallbacks: list[int] + ) -> list[int]: retv = [int(key) for key, value in doc[param_name]["values"].items() if value in values] if len(values) != len(fallbacks): logging_error(_("Length of values %u and fallbacks %u differ for %s"), len(values), len(fallbacks), param_name) diff --git a/ardupilot_methodic_configurator/frontend_tkinter_entry_dynamic.py b/ardupilot_methodic_configurator/frontend_tkinter_entry_dynamic.py index 555ff739..daad3cfa 100755 --- a/ardupilot_methodic_configurator/frontend_tkinter_entry_dynamic.py +++ b/ardupilot_methodic_configurator/frontend_tkinter_entry_dynamic.py @@ -193,7 +193,7 @@ def _set_var(self, text: str) -> None: def update_entry_from_listbox(self, _event: Union[None, tk.Event]) -> str: if self._listbox is not None: - current_selection = self._listbox.curselection() + current_selection = self._listbox.curselection() # type: ignore[no-untyped-call] if current_selection: text = self._listbox.get(current_selection) @@ -210,7 +210,7 @@ def update_entry_from_listbox(self, _event: Union[None, tk.Event]) -> str: def _previous(self, _event: Union[None, tk.Event]) -> str: if self._listbox is not None: - current_selection = self._listbox.curselection() + current_selection = self._listbox.curselection() # type: ignore[no-untyped-call] if len(current_selection) == 0: self._listbox.selection_set(0) @@ -232,7 +232,7 @@ def _previous(self, _event: Union[None, tk.Event]) -> str: def _next(self, _event: Union[None, tk.Event]) -> str: if self._listbox is not None: - current_selection = self._listbox.curselection() + current_selection = self._listbox.curselection() # type: ignore[no-untyped-call] if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) diff --git a/ardupilot_methodic_configurator/mavftp_example.py b/ardupilot_methodic_configurator/mavftp_example.py index fb212ee9..90d8236f 100755 --- a/ardupilot_methodic_configurator/mavftp_example.py +++ b/ardupilot_methodic_configurator/mavftp_example.py @@ -18,12 +18,12 @@ from logging import info as logging_info from typing import Any -import backend_mavftp as mavftp - # import time import requests from pymavlink import mavutil +import ardupilot_methodic_configurator.backend_mavftp as mavftp + old_mavftp_member_variable_values: dict[str, Any] = {} @@ -128,13 +128,13 @@ def delete_local_file_if_exists(filename: str) -> None: os.remove(filename) -def get_list_dir(mav_ftp: mavftp, directory: str) -> None: +def get_list_dir(mav_ftp: mavftp.MAVFTP, directory: str) -> None: ret = mav_ftp.cmd_list([directory]) ret.display_message() debug_class_member_variable_changes(mav_ftp) -def get_file(mav_ftp: mavftp, remote_filename: str, local_filename: str, timeout: float = 5) -> None: +def get_file(mav_ftp: mavftp.MAVFTP, remote_filename: str, local_filename: str, timeout: float = 5) -> None: # session = mav_ftp.session # save the session to restore it after the file transfer mav_ftp.cmd_get([remote_filename, local_filename]) ret = mav_ftp.process_ftp_reply("OpenFileRO", timeout=timeout) @@ -144,7 +144,7 @@ def get_file(mav_ftp: mavftp, remote_filename: str, local_filename: str, timeout # time.sleep(0.2) -def get_last_log(mav_ftp: mavftp) -> None: +def get_last_log(mav_ftp: mavftp.MAVFTP) -> None: try: with open("LASTLOG.TXT", encoding="UTF-8") as file: file_contents = file.readline() @@ -171,19 +171,19 @@ def download_script(url: str, local_filename: str) -> None: logging_error("Failed to download the file") -def create_directory(mav_ftp: mavftp, remote_directory: str) -> None: +def create_directory(mav_ftp: mavftp.MAVFTP, remote_directory: str) -> None: ret = mav_ftp.cmd_mkdir([remote_directory]) ret.display_message() debug_class_member_variable_changes(mav_ftp) -def remove_directory(mav_ftp: mavftp, remote_directory: str) -> None: +def remove_directory(mav_ftp: mavftp.MAVFTP, remote_directory: str) -> None: ret = mav_ftp.cmd_rmdir([remote_directory]) ret.display_message() debug_class_member_variable_changes(mav_ftp) -def upload_script(mav_ftp: mavftp, remote_directory: str, local_filename: str, timeout: float) -> None: +def upload_script(mav_ftp: mavftp.MAVFTP, remote_directory: str, local_filename: str, timeout: float) -> None: # Upload it from the PC to the flight controller mav_ftp.cmd_put([local_filename, remote_directory + "/" + local_filename]) ret = mav_ftp.process_ftp_reply("CreateFile", timeout=timeout) diff --git a/ardupilot_methodic_configurator/tempcal_imu.py b/ardupilot_methodic_configurator/tempcal_imu.py index 3573215f..bdf925ba 100755 --- a/ardupilot_methodic_configurator/tempcal_imu.py +++ b/ardupilot_methodic_configurator/tempcal_imu.py @@ -119,7 +119,7 @@ def correction(self, coeff: dict, imu: int, temperature: float, axis: str, cal_t def correction_accel(self, imu: int, temperature: float) -> Vector3: """ - calculate accel correction from temperature calibration from + Calculate accel correction from temperature calibration from log data using parameters. """ cal_temp = self.atcal.get(imu, TEMP_REF) @@ -131,7 +131,7 @@ def correction_accel(self, imu: int, temperature: float) -> Vector3: def correction_gyro(self, imu: int, temperature: float) -> Vector3: """ - calculate gyro correction from temperature calibration from + Calculate gyro correction from temperature calibration from log data using parameters. """ cal_temp = self.gtcal.get(imu, TEMP_REF) diff --git a/get_release_stats.py b/get_release_stats.py index c28b1431..34781bb2 100755 --- a/get_release_stats.py +++ b/get_release_stats.py @@ -14,7 +14,7 @@ import re from operator import itemgetter -from github import Github +from github import Github # pylint: disable=import-error def compute_average(issues_date: list[tuple[int, int]]) -> float: diff --git a/pyproject.toml b/pyproject.toml index c63c9a4c..5449a7ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -191,13 +191,13 @@ indent-width = 4 ignore_missing_imports = true disallow_any_unimported = false disallow_any_expr = false -disallow_any_decorated = false +disallow_any_decorated = true disallow_any_generics = false disallow_any_explicit = false -disallow_subclassing_any = false +disallow_subclassing_any = true # Disallow untyped definitions and calls -disallow_untyped_calls = false +disallow_untyped_calls = true disallow_untyped_defs = false disallow_incomplete_defs = false check_untyped_defs = true