Skip to content

Add Type Hints #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions adafruit_motor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2021 Scott Shawcroft for Adafruit Industries
#
# SPDX-License-Identifier: MIT
26 changes: 19 additions & 7 deletions adafruit_motor/motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -48,22 +55,22 @@ 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.
"""
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
Expand Down Expand Up @@ -91,23 +98,28 @@ 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:
raise ValueError(
"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
46 changes: 35 additions & 11 deletions adafruit_motor/servo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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).
Expand All @@ -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
Expand Down Expand Up @@ -85,22 +97,29 @@ 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
return None
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
Expand All @@ -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
27 changes: 21 additions & 6 deletions adafruit_motor/stepper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down