Skip to content

Add typing #23

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 18 commits into from
Jul 31, 2023
Merged
Changes from 10 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
81 changes: 50 additions & 31 deletions adafruit_rgbled.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
"""
try:
from typing import Union, Optional, Type
from types import TracebackType
from circuitpython_typing.led import ColorBasedColorUnion
except ImportError:
pass

from pwmio import PWMOut

__version__ = "0.0.0+auto.0"
Expand All @@ -26,24 +33,9 @@

class RGBLED:
"""
Creates a RGBLED object given three physical pins or PWMOut objects.

:param red_pin: The physical pin connected to a red LED anode.
:type ~microcontroller.Pin: Microcontroller's red_pin.
:type pwmio.PWMOut: PWMOut object associated with red_pin.
:type PWMChannel: PCA9685 PWM channel associated with red_pin.
:param green_pin: The physical pin connected to a green LED anode.
:type ~microcontroller.Pin: Microcontroller's green_pin.
:type pwmio.PWMOut: PWMOut object associated with green_pin.
:type PWMChannel: PCA9685 PWM channel associated with green_pin.
:param blue_pin: The physical pin connected to a blue LED anode.
:type ~microcontroller.Pin: Microcontroller's blue_pin.
:type pwmio.PWMOut: PWMOut object associated with blue_pin.
:type PWMChannel: PCA9685 PWM channel associated with blue_pin.
:param bool invert_pwm: False if the RGB LED is common cathode,
true if the RGB LED is common anode.
Create an RGBLED object given three physical pins or PWMOut objects.

Example for setting a RGB LED using a RGB Tuple (Red, Green, Blue):
Example for setting an RGB LED using an RGB Tuple (Red, Green, Blue):

.. code-block:: python

Expand All @@ -58,7 +50,7 @@ class RGBLED:
led = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
led.color = (255, 0, 0)

Example for setting a RGB LED using a 24-bit integer (hex syntax):
Example for setting an RGB LED using a 24-bit integer (hex syntax):

.. code-block:: python

Expand All @@ -69,11 +61,11 @@ class RGBLED:
GREEN_LED = board.D6
BLUE_LED = board.D7

# Create a RGB LED object
# Create an RGB LED object
led = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
led.color = 0x100000

Example for setting a RGB LED using a ContextManager:
Example for setting an RGB LED using a ContextManager:

.. code-block:: python

Expand All @@ -91,9 +83,23 @@ class RGBLED:
with adafruit_rgbled.RGBLED(board.D5, board.D6, board.D7, invert_pwm=True) as rgb_led:
rgb_led.color = (0, 255, 0)

:param Union["microcontroller.Pin", PWMOut, "PWMChannel"] red_pin:
The connection to the red LED.
:param Union["microcontroller.Pin", PWMOut, "PWMChannel"] green_pin:
The connection to the green LED.
:param Union["microcontroller.Pin", PWMOut, "PWMChannel"] blue_pin:
The connection to the blue LED.
:param bool invert_pwm: False if the RGB LED is common cathode,
True if the RGB LED is common anode. Defaults to False.
"""

def __init__(self, red_pin, green_pin, blue_pin, invert_pwm=False):
def __init__(
self,
red_pin: Union["microcontroller.Pin", PWMOut, "PWMChannel"],
Copy link
Member

Choose a reason for hiding this comment

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

Is there any reason you're using quotes instead of importing within the try/except block?

Copy link
Author

Choose a reason for hiding this comment

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

For the "microcontroller.Pin" I was getting "Not Implemented" exceptions in my PyCharm environment. That now happens with the from pwmio import PWMOut which makes this moot. I've changed this to use from microcontroller import Pin

For the "PWMChannel" I couldn't find a library with PWMChannel that I can import on my laptop. The #circuitpython-dev Discord channel suggested using quotes. If you know of a library that I can use, I'm happy to make that change too.

Copy link
Author

@BiffoBear BiffoBear Apr 12, 2023

Choose a reason for hiding this comment

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

…and the CI is failing with

  File "/home/runner/work/Adafruit_CircuitPython_RGBLED/Adafruit_CircuitPython_RGBLED/adafruit_rgbled.py", line 98, in RGBLED
    red_pin: Union[microcontroller.Pin, PWMOut, "PWMChannel"],
                   ^^^^^^^^^^^^^^^
NameError: name 'microcontroller' is not defined

Error: Process completed with exit code 2.

Which may be why I used the quotes. Can't remember TBH!

adafruit-blinka is in requirements.txt and that's where I thought the microcontroller module lives.
I'll push a commit with quotes to have something that passes the CI.

Copy link
Member

Choose a reason for hiding this comment

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

It looks like it's failing because it isn't actually imported in the file. If you add it as an import to the try/except block, it should work fine without quotes. Same thing for PWMChannel.

Copy link
Author

Choose a reason for hiding this comment

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

With import statements in the try/except block I can get Pin to pass CI but not PWMChannel.

try:
    from typing import Union, Optional, Type
    from types import TracebackType
    from microcontroller import Pin
    from adafruit_pca9685 import PWMChannel
    from circuitpython_typing.led import ColorBasedColorUnion
except ImportError:
    pass
...
def __init__(
    self,
    red_pin: Union[Pin, PWMOut, PWMChannel],
    green_pin: Union[Pin, PWMOut, PWMChannel],
    blue_pin: Union[Pin, PWMOut, PWMChannel],
    invert_pwm: bool = False,
) -> None:

I've added adafruit-pca9685 to requirements.txt but this did not help.

Warning, treated as error:
autodoc: failed to import module 'adafruit_rgbled'; the following exception was raised:
Traceback (most recent call last):
  File "/opt/hostedtoolcache/Python/3.11.3/x64/lib/python3.11/site-packages/sphinx/ext/autodoc/importer.py", line 60, in import_module
    return importlib.import_module(modname)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.3/x64/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/runner/work/Adafruit_CircuitPython_RGBLED/Adafruit_CircuitPython_RGBLED/adafruit_rgbled.py", line 36, in <module>
    class RGBLED:
  File "/home/runner/work/Adafruit_CircuitPython_RGBLED/Adafruit_CircuitPython_RGBLED/adafruit_rgbled.py", line 100, in RGBLED
    red_pin: Union[Pin, PWMOut, PWMChannel],
                                ^^^^^^^^^^
NameError: name 'PWMChannel' is not defined

Error: Process completed with exit code 2.```

green_pin: Union["microcontroller.Pin", PWMOut, "PWMChannel"],
blue_pin: Union["microcontroller.Pin", PWMOut, "PWMChannel"],
invert_pwm: bool = False,
) -> None:
self._rgb_led_pins = [red_pin, green_pin, blue_pin]
for i in range( # pylint: disable=consider-using-enumerate
len(self._rgb_led_pins)
Expand All @@ -109,28 +115,41 @@ def __init__(self, red_pin, green_pin, blue_pin, invert_pwm=False):
self._current_color = (0, 0, 0)
self.color = self._current_color

def __enter__(self):
def __enter__(self) -> "RGBLED":
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.deinit()

def deinit(self):
def deinit(self) -> None:
"""Turn the LEDs off, deinit pwmout and release hardware resources."""
for pin in self._rgb_led_pins:
pin.deinit() # pylint: disable=no-member
self._current_color = (0, 0, 0)

@property
def color(self):
"""Returns the RGB LED's current color."""
def color(self) -> ColorBasedColorUnion:
"""
Sets the RGB LED to a desired color.

:param ColorBasedColorUnion value: RGB LED desired value - can be a RGB
tuple of values 0 - 255 or a 24-bit integer. e.g. (255, 64, 35) and 0xff4023
are equivalent.

:returns Union[int, Tuple[int, int, int]]: The current LED color setting.

:raises ValueError: If the input is an int > 0xffffff.
:raises TypeError: If the input is not an integer or a tuple.
"""
return self._current_color

@color.setter
def color(self, value):
"""Sets the RGB LED to a desired color.
:param type value: RGB LED desired value - can be a RGB tuple or a 24-bit integer.
"""
def color(self, value: ColorBasedColorUnion):
self._current_color = value
if isinstance(value, tuple):
for i in range(0, 3):
Expand All @@ -151,4 +170,4 @@ def color(self, value):
rgb[color] -= 65535
self._rgb_led_pins[color].duty_cycle = abs(rgb[color])
else:
raise ValueError("Color must be a tuple or 24-bit integer value.")
raise TypeError("Color must be a tuple or 24-bit integer value.")