Skip to content

Add type hints #46

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 2 commits into from
Dec 10, 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
54 changes: 30 additions & 24 deletions adafruit_lsm6ds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,18 @@
from adafruit_register.i2c_bits import RWBits
from adafruit_register.i2c_bit import RWBit

try:
from typing import Tuple, Optional
from busio import I2C
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late comment -- but I am confused by this.
If the import of typing fails, won't this then ignore the import of I2C?
Shouldn't the typing import be the last import.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jerryneedell No worries. You are correct. if typing fails to import then I2C will get ignored.

In this case the only usage of the I2C object that is imported here was for adding type information to some function arguments. CircuitPython will not use that typing information anyway (and will not crash if there are missing imports related to it).

So we use the import typing first in order to determine if the library is currently being used on a microcontroller essentially. If typing import fails we think it is a microcontroller and therefore we don't need the rest of the imports that are used only for typing either. Putting them after import typing and thus skipping them in environments without typing will save a little bit of memory I think. If I2C did get imported on microcontrollers it would consume a little memory and not actually do anything in this scope.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the explanation.

except ImportError:
pass


class CV:
"""struct helper"""

@classmethod
def add_values(cls, value_tuples):
def add_values(cls, value_tuples: Tuple[str, int, float, Optional[float]]) -> None:
"creates CV entires"
cls.string = {}
cls.lsb = {}
Expand All @@ -81,7 +87,7 @@ def add_values(cls, value_tuples):
cls.lsb[value] = lsb

@classmethod
def is_valid(cls, value):
def is_valid(cls, value: int) -> bool:
"""Returns true if the given value is a member of the CV"""
return value in cls.string

Expand Down Expand Up @@ -187,7 +193,7 @@ class LSM6DS: # pylint: disable=too-many-instance-attributes
before calling. Use `pedometer_reset` to reset the number of steps"""
CHIP_ID = None

def __init__(self, i2c_bus, address=LSM6DS_DEFAULT_ADDRESS):
def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None:
self._cached_accel_range = None
self._cached_gyro_range = None

Expand All @@ -210,14 +216,14 @@ def __init__(self, i2c_bus, address=LSM6DS_DEFAULT_ADDRESS):
self.accelerometer_range = AccelRange.RANGE_4G # pylint: disable=no-member
self.gyro_range = GyroRange.RANGE_250_DPS # pylint: disable=no-member

def reset(self):
def reset(self) -> None:
"Resets the sensor's configuration into an initial state"
self._sw_reset = True
while self._sw_reset:
sleep(0.001)

@staticmethod
def _add_gyro_ranges():
def _add_gyro_ranges() -> None:
GyroRange.add_values(
(
("RANGE_125_DPS", 125, 125, 4.375),
Expand All @@ -229,7 +235,7 @@ def _add_gyro_ranges():
)

@staticmethod
def _add_accel_ranges():
def _add_accel_ranges() -> None:
AccelRange.add_values(
(
("RANGE_2G", 0, 2, 0.061),
Expand All @@ -240,7 +246,7 @@ def _add_accel_ranges():
)

@property
def acceleration(self):
def acceleration(self) -> Tuple[float, float, float]:
"""The x, y, z acceleration values returned in a 3-tuple and are in m / s ^ 2."""
raw_accel_data = self._raw_accel_data

Expand All @@ -251,49 +257,49 @@ def acceleration(self):
return (x, y, z)

@property
def gyro(self):
def gyro(self) -> Tuple[float, float, float]:
"""The x, y, z angular velocity values returned in a 3-tuple and are in radians / second"""
raw_gyro_data = self._raw_gyro_data
x, y, z = [radians(self._scale_gyro_data(i)) for i in raw_gyro_data]
return (x, y, z)

def _scale_xl_data(self, raw_measurement):
def _scale_xl_data(self, raw_measurement: int) -> float:
return (
raw_measurement
* AccelRange.lsb[self._cached_accel_range]
* _MILLI_G_TO_ACCEL
)

def _scale_gyro_data(self, raw_measurement):
def _scale_gyro_data(self, raw_measurement: int) -> float:
return raw_measurement * GyroRange.lsb[self._cached_gyro_range] / 1000

@property
def accelerometer_range(self):
def accelerometer_range(self) -> int:
"""Adjusts the range of values that the sensor can measure, from +/- 2G to +/-16G
Note that larger ranges will be less accurate. Must be an ``AccelRange``"""
return self._cached_accel_range

# pylint: disable=no-member
@accelerometer_range.setter
def accelerometer_range(self, value):
def accelerometer_range(self, value: int) -> None:
if not AccelRange.is_valid(value):
raise AttributeError("range must be an `AccelRange`")
self._accel_range = value
self._cached_accel_range = value
sleep(0.2) # needed to let new range settle

@property
def gyro_range(self):
def gyro_range(self) -> int:
"""Adjusts the range of values that the sensor can measure, from 125 Degrees/s to 2000
degrees/s. Note that larger ranges will be less accurate. Must be a ``GyroRange``."""
return self._cached_gyro_range

@gyro_range.setter
def gyro_range(self, value):
def gyro_range(self, value: int) -> None:
self._set_gyro_range(value)
sleep(0.2)

def _set_gyro_range(self, value):
def _set_gyro_range(self, value: int) -> None:
if not GyroRange.is_valid(value):
raise AttributeError("range must be a `GyroRange`")

Expand All @@ -308,12 +314,12 @@ def _set_gyro_range(self, value):
self._cached_gyro_range = value # needed to let new range settle

@property
def accelerometer_data_rate(self):
def accelerometer_data_rate(self) -> int:
"""Select the rate at which the accelerometer takes measurements. Must be a ``Rate``"""
return self._accel_data_rate

@accelerometer_data_rate.setter
def accelerometer_data_rate(self, value):
def accelerometer_data_rate(self, value: int) -> None:

if not Rate.is_valid(value):
raise AttributeError("accelerometer_data_rate must be a `Rate`")
Expand All @@ -322,42 +328,42 @@ def accelerometer_data_rate(self, value):
# sleep(.2) # needed to let new range settle

@property
def gyro_data_rate(self):
def gyro_data_rate(self) -> int:
"""Select the rate at which the gyro takes measurements. Must be a ``Rate``"""
return self._gyro_data_rate

@gyro_data_rate.setter
def gyro_data_rate(self, value):
def gyro_data_rate(self, value: int) -> None:
if not Rate.is_valid(value):
raise AttributeError("gyro_data_rate must be a `Rate`")

self._gyro_data_rate = value
# sleep(.2) # needed to let new range settle

@property
def pedometer_enable(self):
def pedometer_enable(self) -> bool:
""" Whether the pedometer function on the accelerometer is enabled"""
return self._ped_enable and self._func_enable

@pedometer_enable.setter
def pedometer_enable(self, enable):
def pedometer_enable(self, enable: bool) -> None:
self._ped_enable = enable
self._func_enable = enable
self._pedometer_reset = enable

@property
def high_pass_filter(self):
def high_pass_filter(self) -> int:
"""The high pass filter applied to accelerometer data"""
return self._high_pass_filter

@high_pass_filter.setter
def high_pass_filter(self, value):
def high_pass_filter(self, value: int) -> None:
if not AccelHPF.is_valid(value):
raise AttributeError("range must be an `AccelHPF`")
self._high_pass_filter = value

@property
def temperature(self):
def temperature(self) -> float:
"""Temperature in Celsius"""
# Data from Datasheet Table 4.3
# Temp range -40 to 85 Celsius
Expand Down
12 changes: 9 additions & 3 deletions adafruit_lsm6ds/ism330dhcx.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
from time import sleep
from . import LSM6DS, LSM6DS_DEFAULT_ADDRESS, GyroRange, RWBit, const

try:
import typing # pylint: disable=unused-import
from busio import I2C
except ImportError:
pass

_LSM6DS_CTRL2_G = const(0x11)


Expand Down Expand Up @@ -49,7 +55,7 @@ class ISM330DHCX(LSM6DS): # pylint: disable=too-many-instance-attributes
CHIP_ID = 0x6B
_gyro_range_4000dps = RWBit(_LSM6DS_CTRL2_G, 0)

def __init__(self, i2c_bus, address=LSM6DS_DEFAULT_ADDRESS):
def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None:
GyroRange.add_values(
(
("RANGE_125_DPS", 125, 125, 4.375),
Expand All @@ -66,14 +72,14 @@ def __init__(self, i2c_bus, address=LSM6DS_DEFAULT_ADDRESS):
self._i3c_disable = True

@property
def gyro_range(self):
def gyro_range(self) -> int:
"""Adjusts the range of values that the sensor can measure, from 125 Degrees/s to 4000
degrees/s. Note that larger ranges will be less accurate. Must be a ``GyroRange``. 4000 DPS
is only available for the ISM330DHCX"""
return self._cached_gyro_range

@gyro_range.setter
def gyro_range(self, value):
def gyro_range(self, value: int) -> None:
super()._set_gyro_range(value)

# range uses the `FS_4000` bit
Expand Down
10 changes: 8 additions & 2 deletions adafruit_lsm6ds/lsm6dso32.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
"""
from . import LSM6DS, LSM6DS_CHIP_ID, LSM6DS_DEFAULT_ADDRESS, AccelRange

try:
import typing # pylint: disable=unused-import
from busio import I2C
except ImportError:
pass


class LSM6DSO32(LSM6DS): # pylint: disable=too-many-instance-attributes

Expand Down Expand Up @@ -45,12 +51,12 @@ class LSM6DSO32(LSM6DS): # pylint: disable=too-many-instance-attributes

CHIP_ID = LSM6DS_CHIP_ID

def __init__(self, i2c_bus, address=LSM6DS_DEFAULT_ADDRESS):
def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None:
super().__init__(i2c_bus, address)
self._i3c_disable = True
self.accelerometer_range = AccelRange.RANGE_8G # pylint:disable=no-member

def _add_accel_ranges(self):
def _add_accel_ranges(self) -> None:
AccelRange.add_values(
(
("RANGE_4G", 0, 4, 0.122),
Expand Down
8 changes: 7 additions & 1 deletion adafruit_lsm6ds/lsm6dsox.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
"""
from . import LSM6DS, LSM6DS_DEFAULT_ADDRESS, LSM6DS_CHIP_ID

try:
import typing # pylint: disable=unused-import
from busio import I2C
except ImportError:
pass


class LSM6DSOX(LSM6DS): # pylint: disable=too-many-instance-attributes

Expand Down Expand Up @@ -44,6 +50,6 @@ class LSM6DSOX(LSM6DS): # pylint: disable=too-many-instance-attributes

CHIP_ID = LSM6DS_CHIP_ID

def __init__(self, i2c_bus, address=LSM6DS_DEFAULT_ADDRESS):
def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None:
super().__init__(i2c_bus, address)
self._i3c_disable = True