diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 00000000..6a36bc25 --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,20 @@ +name: reviewdog +on: [pull_request] +jobs: + mypy: + name: runner / mypy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tsuyoshicho/action-mypy@v5 + with: + github_token: ${{ secrets.github_token }} + # Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review]. + reporter: github-pr-review + # Change reporter level if you need. + # GitHub Status Check won't become failure with warning. + level: warning + # Change the current directory to run mypy command. + # mypy command reads setup.cfg or other settings file in this path. + workdir: pytrinamic + mypy_flags: --disable-error-code=import-untyped diff --git a/examples/modules/TMCM3110/TMCL/rotate_demo.py b/examples/modules/TMCM3110/TMCL/rotate_demo.py index f55d95ff..17f34dfe 100644 --- a/examples/modules/TMCM3110/TMCL/rotate_demo.py +++ b/examples/modules/TMCM3110/TMCL/rotate_demo.py @@ -41,12 +41,12 @@ print(motor_2.drive_settings) # preparing linear ramp settings - motor_0.max_acceleration = 1000 - motor_0.max_velocity = 1000 - motor_1.max_acceleration = 1000 - motor_1.max_velocity = 1000 - motor_2.max_acceleration = 1000 - motor_2.max_velocity = 1000 + motor_0.linear_ramp.max_acceleration = 1000 + motor_0.linear_ramp.max_velocity = 1000 + motor_1.linear_ramp.max_acceleration = 1000 + motor_1.linear_ramp.max_velocity = 1000 + motor_2.linear_ramp.max_acceleration = 1000 + motor_2.linear_ramp.max_velocity = 1000 # reset actual position motor_0.actual_position = 0 diff --git a/pyproject.toml b/pyproject.toml index 77753f6b..866be263 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,4 +37,10 @@ tmclfwupload = "pytrinamic.cli.tmclfwupload:main" find = {} [tool.setuptools.dynamic] -version = { attr = "pytrinamic.version.__version__" } \ No newline at end of file +version = { attr = "pytrinamic.version.__version__" } + +[tool.ruff.lint.isort] +force-single-line = true + +[tool.isort] +force_single_line = true diff --git a/pytrinamic/connections/can_tmcl_interface.py b/pytrinamic/connections/can_tmcl_interface.py index a781c308..997c6370 100644 --- a/pytrinamic/connections/can_tmcl_interface.py +++ b/pytrinamic/connections/can_tmcl_interface.py @@ -27,20 +27,11 @@ def __init__(self, channel, datarate, host_id, default_module_id, timeout_s): self.logger = logging.getLogger(f"{self.__class__.__name__}.{self._channel}") - def __enter__(self): - return self - - def __exit__(self, exit_type, value, traceback): - """ - Close the connection at the end of a with-statement block. - """ - del exit_type, value, traceback - self.close() - def close(self): self.logger.info("Shutdown.") - self._connection.shutdown() + if self._connection is not None: + self._connection.shutdown() def _send(self, host_id, module_id, data): """ diff --git a/pytrinamic/connections/connection_manager.py b/pytrinamic/connections/connection_manager.py index 306a6bf7..9c63307d 100644 --- a/pytrinamic/connections/connection_manager.py +++ b/pytrinamic/connections/connection_manager.py @@ -6,21 +6,23 @@ # This software is proprietary to Analog Devices, Inc. and its licensors. ################################################################################ -import sys -import logging import argparse +import logging +import sys from dataclasses import dataclass +from typing import Type from ..connections import DummyTmclInterface -from ..connections import PcanTmclInterface -from ..connections import SocketcanTmclInterface +from ..connections import IxxatTmclInterface from ..connections import KvaserTmclInterface +from ..connections import PcanTmclInterface from ..connections import SerialTmclInterface -from ..connections import UartIcInterface -from ..connections import UsbTmclInterface from ..connections import SlcanTmclInterface -from ..connections import IxxatTmclInterface +from ..connections import SocketcanTmclInterface from ..connections import SocketTmclInterface +from ..connections import UartIcInterface +from ..connections import UsbTmclInterface +from .tmcl_interface import TmclInterface logger = logging.getLogger(__name__) @@ -101,7 +103,7 @@ class which allows repeated connect() and disconnect() calls. @dataclass class _Interface: name: str - class_type: None + class_type: Type[TmclInterface] default_datarate: int # All available interfaces @@ -113,6 +115,7 @@ class _Interface: _Interface("slcan_tmcl", SlcanTmclInterface, 1000000), _Interface("socketcan_tmcl", SocketcanTmclInterface, 1000000), _Interface("serial_tmcl", SerialTmclInterface, 9600), + # FIXME: UartIcInterface is not a subclass of TmclInterface _Interface("uart_ic", UartIcInterface, 9600), _Interface("usb_tmcl", UsbTmclInterface, 115200), _Interface("ixxat_tmcl", IxxatTmclInterface, 1000000), @@ -217,7 +220,7 @@ def __init__(self, arg_list=None, connection_type="any"): self.__module_id, ) - def connect(self): + def connect(self) -> TmclInterface: """ Attempt to connect to a module with the stored connection parameters. diff --git a/pytrinamic/connections/dummy_tmcl_interface.py b/pytrinamic/connections/dummy_tmcl_interface.py index 1d50e512..6432b2a1 100644 --- a/pytrinamic/connections/dummy_tmcl_interface.py +++ b/pytrinamic/connections/dummy_tmcl_interface.py @@ -26,22 +26,6 @@ def __init__(self, port, datarate=115200, host_id=2, module_id=1, timeout_s=5): self.logger.debug("Opening port (baudrate=%s).", datarate) - def __enter__(self): - return self - - def __exit__(self, exit_type, value, traceback): - """ - Close the connection at the end of a with-statement block. - """ - del exit_type, value, traceback - self.close() - - def close(self): - """ - Closes the dummy TMCL connection - """ - - def _send(self, host_id, module_id, data): """ Send the bytearray parameter [data]. diff --git a/pytrinamic/connections/serial_tmcl_interface.py b/pytrinamic/connections/serial_tmcl_interface.py index c73b059b..8b0eec35 100644 --- a/pytrinamic/connections/serial_tmcl_interface.py +++ b/pytrinamic/connections/serial_tmcl_interface.py @@ -35,16 +35,6 @@ def __init__(self, com_port, datarate=115200, host_id=2, module_id=1, timeout_s= except SerialException as e: raise ConnectionError from e - def __enter__(self): - return self - - def __exit__(self, exit_type, value, traceback): - """ - Close the connection at the end of a with-statement block. - """ - del exit_type, value, traceback - self.close() - def close(self): self.logger.info("Closing port.") self._serial.close() diff --git a/pytrinamic/connections/socket_tmcl_interface.py b/pytrinamic/connections/socket_tmcl_interface.py index d1dc958b..7ecf5f9a 100755 --- a/pytrinamic/connections/socket_tmcl_interface.py +++ b/pytrinamic/connections/socket_tmcl_interface.py @@ -73,19 +73,10 @@ def _check_socket(self): "Failed to (re-)connect to Socket connection" ) from e - def __enter__(self): - return self - - def __exit__(self, exitType, value, traceback): - """ - Close the connection at the end of a with-statement block. - """ - del exitType, value, traceback - self.close() - def close(self): self.logger.info("Closing Socket connection.") - self._socket.close() + if self._socket is not None: + self._socket.close() def _send(self, host_id, module_id, data): """ diff --git a/pytrinamic/connections/tmcl_interface.py b/pytrinamic/connections/tmcl_interface.py index 02916d5b..01d83164 100644 --- a/pytrinamic/connections/tmcl_interface.py +++ b/pytrinamic/connections/tmcl_interface.py @@ -59,6 +59,19 @@ def __init__(self, host_id=2, default_module_id=1, default_ap_index_bit_width=8, self._default_ap_index_bit_width = default_ap_index_bit_width self._default_register_address_bit_width = default_register_address_bit_width + def __enter__(self): + return self + + def __exit__(self, exit_type, value, traceback): + """ + Close the connection at the end of a with-statement block. + """ + del exit_type, value, traceback + self.close() + + def close(self): + pass + def _send(self, host_id, module_id, data): """ Send the bytearray [data] representing a TMCL command. The length of diff --git a/pytrinamic/datalogger.py b/pytrinamic/datalogger.py index 250b247a..112d396d 100644 --- a/pytrinamic/datalogger.py +++ b/pytrinamic/datalogger.py @@ -13,16 +13,24 @@ """ from __future__ import annotations -from typing import Union, List, Dict, Tuple -from dataclasses import dataclass -from enum import Enum, auto -import warnings + import math +import warnings +from dataclasses import dataclass +from enum import Enum +from enum import auto +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from typing import Union -from pytrinamic.rd import Rd -from pytrinamic.modules.tmcl_module import ParameterGroup, Parameter -from pytrinamic.ic.tmc_ic import Register, Field from pytrinamic.helpers import to_signed_32 +from pytrinamic.ic.tmc_ic import Field +from pytrinamic.ic.tmc_ic import Register +from pytrinamic.modules.tmcl_module import Parameter +from pytrinamic.modules.tmcl_module import ParameterGroup +from pytrinamic.rd import Rd class DataLoggerConfigError(Exception): @@ -186,10 +194,10 @@ class Trigger: pretrigger_samples_per_channel: int = 0 samples_per_channel: int log_data: dict - down_sampling_factor: int = None - sample_rate_hz: float = None + down_sampling_factor: Optional[int] = None + sample_rate_hz: Optional[float] = None allow_sample_rate_round_down: bool = False - trigger: Trigger = None + trigger: Optional[Trigger] = None def set_sample_rate(self, rate_hz: float, *, round_down=False) -> float: """ diff --git a/pytrinamic/modules/TMCM3110.py b/pytrinamic/modules/TMCM3110.py index 02227e8d..5eb00ec3 100644 --- a/pytrinamic/modules/TMCM3110.py +++ b/pytrinamic/modules/TMCM3110.py @@ -6,18 +6,20 @@ # This software is proprietary to Analog Devices, Inc. and its licensors. ################################################################################ +from ..connections.tmcl_interface import TmclInterface +from ..features import CoolStepModule +from ..features import DriveSettingModule +from ..features import LinearRampModule +from ..features import MotorControlModule +from ..features import StallGuard2Module from ..modules import TMCLModule -# features -from ..features import MotorControlModule, DriveSettingModule, LinearRampModule -from ..features import StallGuard2Module, CoolStepModule - class TMCM3110(TMCLModule): """ The TMCM-3110 is a three axis stepper motor controller/driver module for sensorless load dependent current control. """ - def __init__(self, connection, module_id=1): + def __init__(self, connection: TmclInterface, module_id=1): super().__init__(connection, module_id) self.name = "TMCM-3110" self.desc = self.__doc__ diff --git a/pytrinamic/modules/tmcl_module.py b/pytrinamic/modules/tmcl_module.py index cdb42a50..44d2d4e5 100644 --- a/pytrinamic/modules/tmcl_module.py +++ b/pytrinamic/modules/tmcl_module.py @@ -9,13 +9,15 @@ import enum import inspect import warnings +from typing import Optional +from typing import Union -from typing import Optional, Union +from ..connections.tmcl_interface import TmclInterface class TMCLModule(object): - def __init__(self, connection, module_id=1, ap_index_bit_width=8): + def __init__(self, connection: TmclInterface, module_id=1, ap_index_bit_width=8): """ Constructor for the module instance. @@ -242,7 +244,7 @@ def category(self): class ParameterApiDevice: - def get_parameter(self, get_target: Union[Parameter]): + def get_parameter(self, get_target: Parameter): """Get the value of a parameter using the GAP or GGP command.""" if isinstance(get_target, Parameter): parameter = get_target