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) 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 61f6253..63323d7 100644 --- a/adafruit_motor/motor.py +++ b/adafruit_motor/motor.py @@ -20,6 +20,13 @@ * Author(s): Scott Shawcroft """ +try: + from typing import Optional, Type + from types import TracebackType + from pwmio import PWMOut +except ImportError: + pass + __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Motor.git" @@ -48,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, negative_pwm): + 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. @@ -63,7 +70,7 @@ def throttle(self): return self._throttle @throttle.setter - def throttle(self, value): + 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 @@ -91,14 +98,14 @@ def throttle(self, value): 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=FAST_DECAY): + def decay_mode(self, mode: int = FAST_DECAY) -> None: if mode in (FAST_DECAY, SLOW_DECAY): self._decay_mode = mode else: @@ -106,8 +113,13 @@ def decay_mode(self, mode=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__(self, exception_type, exception_value, traceback): + def __exit__( + self, + 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 86e8d80..87d75a0 100644 --- a/adafruit_motor/servo.py +++ b/adafruit_motor/servo.py @@ -12,6 +12,14 @@ * Author(s): Scott Shawcroft """ +try: + from typing import Optional, Type + from types import TracebackType + from pwmio import PWMOut +except ImportError: + pass + + __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Motor.git" @@ -24,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, *, min_pulse=750, max_pulse=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=750, max_pulse=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). @@ -45,7 +57,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]) -> None: if value is None: self._pwm_out.duty_cycle = 0 # disable the motor return @@ -85,14 +97,21 @@ 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 + ) -> 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 @@ -100,7 +119,7 @@ def angle(self): return self.actuation_range * self.fraction @angle.setter - def angle(self, new_angle): + def angle(self, new_angle: Optional[int]) -> None: if new_angle is None: # disable the servo by sending 0 signal self.fraction = None return @@ -116,22 +135,27 @@ 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): + 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__(self, exception_type, exception_value, traceback): + def __exit__( + self, + 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 83b702d..4bd8059 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,15 @@ 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 + ) -> None: if microsteps is None: # # Digital IO Pins @@ -107,7 +122,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) -> None: if self._microsteps is None: # # Digital IO Pins @@ -144,7 +159,7 @@ def _update_coils(self, *, microstepping=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: @@ -153,9 +168,9 @@ def release(self): else: coil.duty_cycle = 0 - def onestep( - self, *, direction=FORWARD, style=SINGLE - ): # pylint: disable=too-many-branches + def onestep( # pylint: disable=too-many-branches + self, *, direction: int = FORWARD, style: int = SINGLE + ) -> 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. 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.