From 0c9849f7ddbd444a714e6e6ad889b369a3154778 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Fri, 5 Nov 2021 13:21:11 -0400 Subject: [PATCH 1/7] Add typehints Still missing typehint for traceback argument in DCMotor.__exit__() --- adafruit_motor/motor.py | 14 ++++++++++---- adafruit_motor/servo.py | 19 +++++++++++++------ adafruit_motor/stepper.py | 13 ++++++++++--- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/adafruit_motor/motor.py b/adafruit_motor/motor.py index 61f6253..73dc5e5 100644 --- a/adafruit_motor/motor.py +++ b/adafruit_motor/motor.py @@ -20,6 +20,12 @@ * Author(s): Scott Shawcroft """ +try: + from typing import Optional, Type + from pwmio import PWMOut +except ImportError: + pass + __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Motor.git" @@ -48,7 +54,7 @@ class DCMotor: :param ~pwmio.PWMOut negative_pwm: The motor input that causes the motor to spin backwards when high and the other is low.""" - def __init__(self, positive_pwm, negative_pwm): + def __init__(self, positive_pwm: PWMOut, negative_pwm: PWMOut): self._positive = positive_pwm self._negative = negative_pwm self._throttle = None @@ -63,7 +69,7 @@ def throttle(self): return self._throttle @throttle.setter - def throttle(self, value): + def throttle(self, value: Optional[float]): if value is not None and (value > 1.0 or value < -1.0): raise ValueError("Throttle must be None or between -1.0 and +1.0") self._throttle = value @@ -98,7 +104,7 @@ def decay_mode(self): return self._decay_mode @decay_mode.setter - def decay_mode(self, mode=FAST_DECAY): + def decay_mode(self, mode: int = FAST_DECAY): if mode in (FAST_DECAY, SLOW_DECAY): self._decay_mode = mode else: @@ -109,5 +115,5 @@ def decay_mode(self, mode=FAST_DECAY): def __enter__(self): return self - def __exit__(self, exception_type, exception_value, traceback): + def __exit__(self, exception_type: Optional[Type[type]], exception_value: Optional[BaseException], traceback): # TODO: Add traceback typing self.throttle = None diff --git a/adafruit_motor/servo.py b/adafruit_motor/servo.py index 86e8d80..725cfae 100644 --- a/adafruit_motor/servo.py +++ b/adafruit_motor/servo.py @@ -12,6 +12,13 @@ * Author(s): Scott Shawcroft """ +try: + from typing import Optional + from pwmio import PWMOut +except ImportError: + pass + + __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Motor.git" @@ -24,11 +31,11 @@ class _BaseServo: # pylint: disable-msg=too-few-public-methods :param int min_pulse: The minimum pulse length of the servo in microseconds. :param int max_pulse: The maximum pulse length of the servo in microseconds.""" - def __init__(self, pwm_out, *, min_pulse=750, max_pulse=2250): + def __init__(self, pwm_out: PWMOut, *, min_pulse: int = 750, max_pulse: int = 2250): self._pwm_out = pwm_out self.set_pulse_width_range(min_pulse, max_pulse) - def set_pulse_width_range(self, min_pulse=750, max_pulse=2250): + def set_pulse_width_range(self, min_pulse: int = 750, max_pulse: int = 2250): """Change min and max pulse widths.""" self._min_duty = int((min_pulse * self._pwm_out.frequency) / 1000000 * 0xFFFF) max_duty = (max_pulse * self._pwm_out.frequency) / 1000000 * 0xFFFF @@ -45,7 +52,7 @@ def fraction(self): return (self._pwm_out.duty_cycle - self._min_duty) / self._duty_range @fraction.setter - def fraction(self, value): + def fraction(self, value: Optional[float]): if value is None: self._pwm_out.duty_cycle = 0 # disable the motor return @@ -85,7 +92,7 @@ class Servo(_BaseServo): Test carefully to find the safe minimum and maximum. """ - def __init__(self, pwm_out, *, actuation_range=180, min_pulse=750, max_pulse=2250): + def __init__(self, pwm_out: PWMOut, *, actuation_range: int = 180, min_pulse: int = 750, max_pulse: int = 2250): super().__init__(pwm_out, min_pulse=min_pulse, max_pulse=max_pulse) self.actuation_range = actuation_range """The physical range of motion of the servo in degrees.""" @@ -100,7 +107,7 @@ def angle(self): return self.actuation_range * self.fraction @angle.setter - def angle(self, new_angle): + def angle(self, new_angle: Optional[int]): if new_angle is None: # disable the servo by sending 0 signal self.fraction = None return @@ -123,7 +130,7 @@ def throttle(self): return self.fraction * 2 - 1 @throttle.setter - def throttle(self, value): + def throttle(self, value: float): if value > 1.0 or value < -1.0: raise ValueError("Throttle must be between -1.0 and 1.0") if value is None: diff --git a/adafruit_motor/stepper.py b/adafruit_motor/stepper.py index 83b702d..164f8e9 100755 --- a/adafruit_motor/stepper.py +++ b/adafruit_motor/stepper.py @@ -18,6 +18,13 @@ from micropython import const +try: + from typing import Union, Optional + from pwmio import PWMOut + from digitalio import DigitalInOut +except ImportError: + pass + __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Motor.git" @@ -79,7 +86,7 @@ class StepperMotor: :param microsteps: set to `None` """ - def __init__(self, ain1, ain2, bin1, bin2, *, microsteps=16): + def __init__(self, ain1: Union[PWMOut, DigitalInOut], ain2: Union[PWMOut, DigitalInOut], bin1: Union[PWMOut, DigitalInOut], bin2: Union[PWMOut, DigitalInOut], *, microsteps: Optional[int] = 16): if microsteps is None: # # Digital IO Pins @@ -107,7 +114,7 @@ def __init__(self, ain1, ain2, bin1, bin2, *, microsteps=16): self._microsteps = microsteps self._update_coils() - def _update_coils(self, *, microstepping=False): + def _update_coils(self, *, microstepping: bool = False): if self._microsteps is None: # # Digital IO Pins @@ -154,7 +161,7 @@ def release(self): coil.duty_cycle = 0 def onestep( - self, *, direction=FORWARD, style=SINGLE + self, *, direction: int = FORWARD, style: int = SINGLE ): # pylint: disable=too-many-branches """Performs one step of a particular style. The actual rotation amount will vary by style. `SINGLE` and `DOUBLE` will normal cause a full step rotation. `INTERLEAVE` will normally From 6df99712f2d87b26cbc63a90b91c33dc7091c3c4 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Fri, 5 Nov 2021 13:41:34 -0400 Subject: [PATCH 2/7] Add type hint for DCMotor.__exit__() traceback argument --- adafruit_motor/motor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/adafruit_motor/motor.py b/adafruit_motor/motor.py index 73dc5e5..5196f13 100644 --- a/adafruit_motor/motor.py +++ b/adafruit_motor/motor.py @@ -22,6 +22,7 @@ try: from typing import Optional, Type + from types import TracebackType from pwmio import PWMOut except ImportError: pass @@ -115,5 +116,5 @@ def decay_mode(self, mode: int = FAST_DECAY): def __enter__(self): return self - def __exit__(self, exception_type: Optional[Type[type]], exception_value: Optional[BaseException], traceback): # TODO: Add traceback typing + def __exit__(self, exception_type: Optional[Type[type]], exception_value: Optional[BaseException], traceback: Optional[TracebackType]): self.throttle = None From ee4a2acfc563342914c5b3b63c237722ec46c053 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Fri, 5 Nov 2021 13:46:30 -0400 Subject: [PATCH 3/7] Added type hints for Servo.__exit__() --- adafruit_motor/servo.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/adafruit_motor/servo.py b/adafruit_motor/servo.py index 725cfae..d4f0c97 100644 --- a/adafruit_motor/servo.py +++ b/adafruit_motor/servo.py @@ -13,7 +13,8 @@ """ try: - from typing import Optional + from typing import Optional, Type + from types import TracebackType from pwmio import PWMOut except ImportError: pass @@ -140,5 +141,5 @@ def throttle(self, value: float): def __enter__(self): return self - def __exit__(self, exception_type, exception_value, traceback): + def __exit__(self, exception_type: Optional[Type[type]], exception_value: Optional[BaseException], traceback: Optional[TracebackType]): self.throttle = 0 From 844ce74f6cd0b83c6d9fe91d0b632b0a0832fc6a Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Fri, 5 Nov 2021 13:52:59 -0400 Subject: [PATCH 4/7] Reformatting via pre-commit --- adafruit_motor/motor.py | 7 ++++++- adafruit_motor/servo.py | 16 ++++++++++++++-- adafruit_motor/stepper.py | 10 +++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/adafruit_motor/motor.py b/adafruit_motor/motor.py index 5196f13..f5867e2 100644 --- a/adafruit_motor/motor.py +++ b/adafruit_motor/motor.py @@ -116,5 +116,10 @@ def decay_mode(self, mode: int = FAST_DECAY): def __enter__(self): return self - def __exit__(self, exception_type: Optional[Type[type]], exception_value: Optional[BaseException], traceback: Optional[TracebackType]): + def __exit__( + self, + exception_type: Optional[Type[type]], + exception_value: Optional[BaseException], + traceback: Optional[TracebackType], + ): self.throttle = None diff --git a/adafruit_motor/servo.py b/adafruit_motor/servo.py index d4f0c97..fdb7138 100644 --- a/adafruit_motor/servo.py +++ b/adafruit_motor/servo.py @@ -93,7 +93,14 @@ class Servo(_BaseServo): Test carefully to find the safe minimum and maximum. """ - def __init__(self, pwm_out: PWMOut, *, actuation_range: int = 180, min_pulse: int = 750, max_pulse: int = 2250): + def __init__( + self, + pwm_out: PWMOut, + *, + actuation_range: int = 180, + min_pulse: int = 750, + max_pulse: int = 2250 + ): super().__init__(pwm_out, min_pulse=min_pulse, max_pulse=max_pulse) self.actuation_range = actuation_range """The physical range of motion of the servo in degrees.""" @@ -141,5 +148,10 @@ def throttle(self, value: float): def __enter__(self): return self - def __exit__(self, exception_type: Optional[Type[type]], exception_value: Optional[BaseException], traceback: Optional[TracebackType]): + def __exit__( + self, + exception_type: Optional[Type[type]], + exception_value: Optional[BaseException], + traceback: Optional[TracebackType], + ): self.throttle = 0 diff --git a/adafruit_motor/stepper.py b/adafruit_motor/stepper.py index 164f8e9..57416c9 100755 --- a/adafruit_motor/stepper.py +++ b/adafruit_motor/stepper.py @@ -86,7 +86,15 @@ class StepperMotor: :param microsteps: set to `None` """ - def __init__(self, ain1: Union[PWMOut, DigitalInOut], ain2: Union[PWMOut, DigitalInOut], bin1: Union[PWMOut, DigitalInOut], bin2: Union[PWMOut, DigitalInOut], *, microsteps: Optional[int] = 16): + def __init__( + self, + ain1: Union[PWMOut, DigitalInOut], + ain2: Union[PWMOut, DigitalInOut], + bin1: Union[PWMOut, DigitalInOut], + bin2: Union[PWMOut, DigitalInOut], + *, + microsteps: Optional[int] = 16 + ): if microsteps is None: # # Digital IO Pins From 1c87a8418b3f7cc138edc112ccd6550920471569 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Fri, 5 Nov 2021 15:06:14 -0400 Subject: [PATCH 5/7] Add duplicate code disable to pre-commit config --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1b9fadc..43d1385 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: name: pylint (library code) types: [python] args: - - --disable=consider-using-f-string + - --disable=consider-using-f-string,duplicate-code exclude: "^(docs/|examples/|tests/|setup.py$)" - id: pylint name: pylint (example code) From 0bfd4d64fd1167f4ec6d930658f53024596488e0 Mon Sep 17 00:00:00 2001 From: tekktrik <89490472+tekktrik@users.noreply.github.com> Date: Fri, 12 Nov 2021 09:09:01 -0500 Subject: [PATCH 6/7] Add pwmio to autodoc_mock_import --- docs/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 7b7bf65..e628522 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,6 +43,9 @@ copyright = "2017 Scott Shawcroft" author = "Scott Shawcroft" +# Ignore imports of these modules, which sphinx will not know about. +autodoc_mock_imports = ["pwmio"] + # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. From c917de90a307f45a0bf1d0db4c70d733c0168590 Mon Sep 17 00:00:00 2001 From: tekktrik <89490472+tekktrik@users.noreply.github.com> Date: Fri, 12 Nov 2021 11:04:42 -0500 Subject: [PATCH 7/7] Added remaining typing, formatted per pre-commit Also added license to adafruit_motor/__init__.py since there wasn't one --- adafruit_motor/__init__.py | 3 +++ adafruit_motor/motor.py | 14 +++++++------- adafruit_motor/servo.py | 26 +++++++++++++++----------- adafruit_motor/stepper.py | 10 +++++----- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/adafruit_motor/__init__.py b/adafruit_motor/__init__.py index e69de29..639cfc7 100644 --- a/adafruit_motor/__init__.py +++ b/adafruit_motor/__init__.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2021 Scott Shawcroft for Adafruit Industries +# +# SPDX-License-Identifier: MIT diff --git a/adafruit_motor/motor.py b/adafruit_motor/motor.py index f5867e2..63323d7 100644 --- a/adafruit_motor/motor.py +++ b/adafruit_motor/motor.py @@ -55,14 +55,14 @@ class DCMotor: :param ~pwmio.PWMOut negative_pwm: The motor input that causes the motor to spin backwards when high and the other is low.""" - def __init__(self, positive_pwm: PWMOut, negative_pwm: PWMOut): + def __init__(self, positive_pwm: PWMOut, negative_pwm: PWMOut) -> None: self._positive = positive_pwm self._negative = negative_pwm self._throttle = None self._decay_mode = FAST_DECAY @property - def throttle(self): + def throttle(self) -> Optional[float]: """Motor speed, ranging from -1.0 (full speed reverse) to 1.0 (full speed forward), or ``None`` (controller off). If ``None``, both PWMs are turned full off. If ``0.0``, both PWMs are turned full on. @@ -70,7 +70,7 @@ def throttle(self): return self._throttle @throttle.setter - def throttle(self, value: Optional[float]): + def throttle(self, value: Optional[float]) -> None: if value is not None and (value > 1.0 or value < -1.0): raise ValueError("Throttle must be None or between -1.0 and +1.0") self._throttle = value @@ -98,14 +98,14 @@ def throttle(self, value: Optional[float]): self._negative.duty_cycle = 0 @property - def decay_mode(self): + def decay_mode(self) -> int: """Motor controller recirculation current decay mode. A value of ``motor.FAST_DECAY`` sets the motor controller to the default fast recirculation current decay mode (coasting); ``motor.SLOW_DECAY`` sets slow decay (braking) mode.""" return self._decay_mode @decay_mode.setter - def decay_mode(self, mode: int = FAST_DECAY): + def decay_mode(self, mode: int = FAST_DECAY) -> None: if mode in (FAST_DECAY, SLOW_DECAY): self._decay_mode = mode else: @@ -113,7 +113,7 @@ def decay_mode(self, mode: int = FAST_DECAY): "Decay mode value must be either motor.FAST_DECAY or motor.SLOW_DECAY" ) - def __enter__(self): + def __enter__(self) -> "DCMotor": return self def __exit__( @@ -121,5 +121,5 @@ def __exit__( exception_type: Optional[Type[type]], exception_value: Optional[BaseException], traceback: Optional[TracebackType], - ): + ) -> None: self.throttle = None diff --git a/adafruit_motor/servo.py b/adafruit_motor/servo.py index fdb7138..87d75a0 100644 --- a/adafruit_motor/servo.py +++ b/adafruit_motor/servo.py @@ -32,18 +32,22 @@ class _BaseServo: # pylint: disable-msg=too-few-public-methods :param int min_pulse: The minimum pulse length of the servo in microseconds. :param int max_pulse: The maximum pulse length of the servo in microseconds.""" - def __init__(self, pwm_out: PWMOut, *, min_pulse: int = 750, max_pulse: int = 2250): + def __init__( + self, pwm_out: PWMOut, *, min_pulse: int = 750, max_pulse: int = 2250 + ) -> None: self._pwm_out = pwm_out self.set_pulse_width_range(min_pulse, max_pulse) - def set_pulse_width_range(self, min_pulse: int = 750, max_pulse: int = 2250): + def set_pulse_width_range( + self, min_pulse: int = 750, max_pulse: int = 2250 + ) -> None: """Change min and max pulse widths.""" self._min_duty = int((min_pulse * self._pwm_out.frequency) / 1000000 * 0xFFFF) max_duty = (max_pulse * self._pwm_out.frequency) / 1000000 * 0xFFFF self._duty_range = int(max_duty - self._min_duty) @property - def fraction(self): + def fraction(self) -> Optional[float]: """Pulse width expressed as fraction between 0.0 (`min_pulse`) and 1.0 (`max_pulse`). For conventional servos, corresponds to the servo position as a fraction of the actuation range. Is None when servo is diabled (pulsewidth of 0ms). @@ -53,7 +57,7 @@ def fraction(self): return (self._pwm_out.duty_cycle - self._min_duty) / self._duty_range @fraction.setter - def fraction(self, value: Optional[float]): + def fraction(self, value: Optional[float]) -> None: if value is None: self._pwm_out.duty_cycle = 0 # disable the motor return @@ -100,14 +104,14 @@ def __init__( actuation_range: int = 180, min_pulse: int = 750, max_pulse: int = 2250 - ): + ) -> None: super().__init__(pwm_out, min_pulse=min_pulse, max_pulse=max_pulse) self.actuation_range = actuation_range """The physical range of motion of the servo in degrees.""" self._pwm = pwm_out @property - def angle(self): + def angle(self) -> Optional[float]: """The servo angle in degrees. Must be in the range ``0`` to ``actuation_range``. Is None when servo is disabled.""" if self.fraction is None: # special case for disabled servos @@ -115,7 +119,7 @@ def angle(self): return self.actuation_range * self.fraction @angle.setter - def angle(self, new_angle: Optional[int]): + def angle(self, new_angle: Optional[int]) -> None: if new_angle is None: # disable the servo by sending 0 signal self.fraction = None return @@ -131,21 +135,21 @@ class ContinuousServo(_BaseServo): :param int max_pulse: The maximum pulse width of the servo in microseconds.""" @property - def throttle(self): + def throttle(self) -> float: """How much power is being delivered to the motor. Values range from ``-1.0`` (full throttle reverse) to ``1.0`` (full throttle forwards.) ``0`` will stop the motor from spinning.""" return self.fraction * 2 - 1 @throttle.setter - def throttle(self, value: float): + def throttle(self, value: float) -> None: if value > 1.0 or value < -1.0: raise ValueError("Throttle must be between -1.0 and 1.0") if value is None: raise ValueError("Continuous servos cannot spin freely") self.fraction = (value + 1) / 2 - def __enter__(self): + def __enter__(self) -> "ContinuousServo": return self def __exit__( @@ -153,5 +157,5 @@ def __exit__( exception_type: Optional[Type[type]], exception_value: Optional[BaseException], traceback: Optional[TracebackType], - ): + ) -> None: self.throttle = 0 diff --git a/adafruit_motor/stepper.py b/adafruit_motor/stepper.py index 57416c9..4bd8059 100755 --- a/adafruit_motor/stepper.py +++ b/adafruit_motor/stepper.py @@ -94,7 +94,7 @@ def __init__( bin2: Union[PWMOut, DigitalInOut], *, microsteps: Optional[int] = 16 - ): + ) -> None: if microsteps is None: # # Digital IO Pins @@ -122,7 +122,7 @@ def __init__( self._microsteps = microsteps self._update_coils() - def _update_coils(self, *, microstepping: bool = False): + def _update_coils(self, *, microstepping: bool = False) -> None: if self._microsteps is None: # # Digital IO Pins @@ -159,7 +159,7 @@ def _update_coils(self, *, microstepping: bool = False): for i in range(4): self._coil[i].duty_cycle = duty_cycles[i] - def release(self): + def release(self) -> None: """Releases all the coils so the motor can free spin, also won't use any power""" # De-energize coils: for coil in self._coil: @@ -168,9 +168,9 @@ def release(self): else: coil.duty_cycle = 0 - def onestep( + def onestep( # pylint: disable=too-many-branches self, *, direction: int = FORWARD, style: int = SINGLE - ): # pylint: disable=too-many-branches + ) -> None: """Performs one step of a particular style. The actual rotation amount will vary by style. `SINGLE` and `DOUBLE` will normal cause a full step rotation. `INTERLEAVE` will normally do a half step rotation. `MICROSTEP` will perform the smallest configured step.