diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80940d0f..6d074b0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,10 +16,10 @@ jobs: - name: Translate Repo Name For Build Tools filename_prefix id: repo-name run: echo "repo-name=Adafruit-Blinka" >> $GITHUB_OUTPUT - - name: Set up Python 3.8 + - name: Set up Python 3.10 uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: '3.10' - name: Versions run: | python3 --version diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a09b8af4..22f3b7f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,11 +8,11 @@ repos: hooks: - id: black - repo: https://github.com/fsfe/reuse-tool - rev: v1.1.2 + rev: v6.2.0 hooks: - id: reuse - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v6.0.0 hooks: - id: check-yaml - id: check-json diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..d5a006fe --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Melissa LeBlanc-Williams for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +[build-system] +requires = [ + "setuptools", + "wheel", + "setuptools-scm", +] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +git_describe_command = "git describe --tags --long" +local_scheme = "no-local-version" diff --git a/requirements.txt b/requirements.txt index aec79110..fbf93dc0 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,14 @@ - # requirements.txt - # - # installs dependencies from ./setup.py, and the package itself, - # in editable mode - # -e . - - # (the -e above is optional). you could also just install the package - # normally with just the line below (after uncommenting) - . +Adafruit-PlatformDetect>=3.89.0 +Adafruit-PureIO>=1.1.7 +binho-host-adapter>=0.1.6 +pyftdi>=0.40.0 +adafruit-circuitpython-typing +sysv_ipc>=1.1.0;sys_platform=='linux' and platform_machine!='mips' +toml>=0.10.2;python_version<'3.11' +Jetson.GPIO;sys_platform=='linux' and platform_machine=='aarch64' +rpi_ws281x>=4.0.0;sys_platform=='linux' and (platform_machine=='armv6l' or platform_machine=='armv7l' or platform_machine=='aarch64') +lgpio>=0.2.2.0; sys_platform=='linux' and python_version<'3.13' and (platform_machine=='aarch64' or platform_machine=='armv7l') +adafruit-lgpio>=0.2.2.0; sys_platform=='linux' and python_version>='3.13' and (platform_machine=='aarch64' or platform_machine=='armv7l') +RPi.GPIO;sys_platform=='linux' and (platform_machine=='armv6l' or platform_machine=='armv7l' or platform_machine=='aarch64') +Adafruit-Blinka-Raspberry-Pi5-Neopixel;sys_platform=='linux' and platform_machine=='aarch64' and python_version>='3.11' +Adafruit_BBIO>=1.2.4;sys_platform=='linux' and platform_machine=='armv7l' diff --git a/setup.py b/setup.py index 4fd9a421..cc38407d 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ import io import os -import glob +import sys from setuptools import setup, find_packages @@ -22,12 +22,6 @@ with io.open(os.path.join(here, "README.rst"), encoding="utf-8") as f: long_description = "\n" + f.read() -if not glob.glob("//usr//include//python3.*//Python.h"): - raise RuntimeError( - "This package requires a Python development environment. " - "Please install the python3-dev package for your distribution." - ) - board_reqs = [] if os.path.exists("/proc/device-tree/compatible"): with open("/proc/device-tree/compatible", "rb") as f: @@ -44,7 +38,12 @@ or b"brcm,bcm2711" in compat or b"brcm,bcm2712" in compat ): - lgpio_req = "lgpio>=0.2.2.0" + _pyver = (sys.version_info.major, sys.version_info.minor) + # adafruit-lgpio provides pre-built wheels for Python 3.13+ (aarch64 + armv7l) + if _pyver >= (3, 13): + lgpio_req = "adafruit-lgpio>=0.2.2.0" + else: + lgpio_req = "lgpio>=0.2.2.0" try: import lgpio except ImportError: @@ -52,8 +51,8 @@ "\n*** lgpio is not installed. On Raspberry Pi OS, install it with:\n" " sudo apt-get install -y python3-lgpio\n" " Then recreate your virtual environment with --system-site-packages\n" - " or install the wheel from:\n" - " https://github.com/adafruit/lgpio-python-wheels\n" + " or install adafruit-lgpio from PyPI:\n" + " pip install adafruit-lgpio\n" ) board_reqs = [ "rpi_ws281x>=4.0.0", @@ -72,13 +71,12 @@ "git_describe_command": "git describe --tags --long", "local_scheme": "no-local-version", }, - setup_requires=["setuptools_scm"], description="CircuitPython APIs for non-CircuitPython versions of Python such as CPython on Linux and MicroPython.", long_description=long_description, long_description_content_type="text/x-rst", author="Adafruit Industries", author_email="circuitpython@adafruit.com", - python_requires=">=3.7.0", + python_requires=">=3.9.0", url="https://github.com/adafruit/Adafruit_Blinka", package_dir={"": "src"}, packages=find_packages("src") + ["micropython-stubs"], @@ -131,7 +129,7 @@ "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: MicroPython", ], ) diff --git a/src/adafruit_blinka/importing.py b/src/adafruit_blinka/importing.py index acb50800..1995b70a 100644 --- a/src/adafruit_blinka/importing.py +++ b/src/adafruit_blinka/importing.py @@ -25,6 +25,36 @@ def import_module(module_name: str, package: str = None): from adafruit_blinka.agnostic import detector +PLATFORM_DEPENDENCY_INSTALLS = { + "Adafruit_BBIO": "pip install Adafruit_BBIO", + "Jetson": "pip install Jetson.GPIO", + "RPi": "pip install RPi.GPIO", + "_rpi_ws281x": "pip install rpi_ws281x", + "adafruit_raspberry_pi5_neopixel_write": ( + "pip install Adafruit-Blinka-Raspberry-Pi5-Neopixel" + ), + "lgpio": "pip install adafruit-lgpio", +} + + +def raise_for_missing_platform_dependency(error: ModuleNotFoundError): + """Raise a helpful message for known optional platform dependencies.""" + install_command = PLATFORM_DEPENDENCY_INSTALLS.get(error.name) + if install_command is None: + raise error + + message = ( + f"The platform library '{error.name}' was not found. " + f"To install, try typing: {install_command}" + ) + if error.name == "lgpio": + message += ( + "\nOn Raspberry Pi OS, you can also try: " + "sudo apt-get install -y python3-lgpio" + ) + raise RuntimeError(message) from error + + def get_import_file(json_file_name, script_file_location): """Get the full path to the microcontroller imports file.""" try: @@ -43,20 +73,23 @@ def get_import_file(json_file_name, script_file_location): def import_mod(caller_globals, module_name: str, package_name: str = "*"): """Function to allow importing with * or specific package name.""" - if package_name == "*": - module = import_module(module_name) - caller_globals.update( - {name: getattr(module, name) for name in module.__all__} - if hasattr(module, "__all__") - else { - key: value - for (key, value) in module.__dict__.items() - if not key.startswith("_") - } - ) - else: - module = import_module(module_name, package=package_name) - caller_globals[package_name] = getattr(module, package_name) + try: + if package_name == "*": + module = import_module(module_name) + caller_globals.update( + {name: getattr(module, name) for name in module.__all__} + if hasattr(module, "__all__") + else { + key: value + for (key, value) in module.__dict__.items() + if not key.startswith("_") + } + ) + else: + module = import_module(module_name, package=package_name) + caller_globals[package_name] = getattr(module, package_name) + except ModuleNotFoundError as error: + raise_for_missing_platform_dependency(error) def import_microcontroller( diff --git a/src/adafruit_blinka/microcontroller/am335x/pin.py b/src/adafruit_blinka/microcontroller/am335x/pin.py index c6659ca2..a33320da 100644 --- a/src/adafruit_blinka/microcontroller/am335x/pin.py +++ b/src/adafruit_blinka/microcontroller/am335x/pin.py @@ -2,13 +2,12 @@ # # SPDX-License-Identifier: MIT """AM335x pin names""" +from adafruit_blinka.importing import raise_for_missing_platform_dependency + try: from Adafruit_BBIO import GPIO -except ImportError as error: - raise RuntimeError( - "The library 'Adafruit_BBIO' was not found. To install, try typing: " - "pip install Adafruit_BBIO" - ) from error +except ModuleNotFoundError as error: + raise_for_missing_platform_dependency(error) class Pin: diff --git a/src/board.py b/src/board.py index a499d59a..91f3a519 100644 --- a/src/board.py +++ b/src/board.py @@ -48,16 +48,14 @@ elif board_id is None: import platform - import pkg_resources + from importlib.metadata import version - package = str( - pkg_resources.get_distribution("adafruit_platformdetect") - ).split() + package_version = version("adafruit_platformdetect") raise NotImplementedError( f""" - {package[0]} version {package[1]} was unable to identify the board and/or - microcontroller running the {platform.system()} platform. Please be sure you - have the latest packages by running: + adafruit-platformdetect version {package_version} was unable to identify the board + and/or microcontroller running the {platform.system()} platform. + Please be sure you have the latest packages by running: 'pip3 install --upgrade adafruit-blinka adafruit-platformdetect' If you are running the latest package, your board may not yet be supported. Please diff --git a/src/neopixel_write.py b/src/neopixel_write.py index cc9835a7..34e6d66c 100644 --- a/src/neopixel_write.py +++ b/src/neopixel_write.py @@ -14,34 +14,38 @@ import sys from adafruit_blinka.agnostic import detector +from adafruit_blinka.importing import raise_for_missing_platform_dependency -if detector.board.any_raspberry_pi: - if detector.board.any_raspberry_pi_5_board: - import adafruit_raspberry_pi5_neopixel_write as _neopixel +try: + if detector.board.any_raspberry_pi: + if detector.board.any_raspberry_pi_5_board: + import adafruit_raspberry_pi5_neopixel_write as _neopixel + else: + from adafruit_blinka.microcontroller.bcm283x import neopixel as _neopixel + elif detector.board.pico_u2if: + from adafruit_blinka.microcontroller.rp2040_u2if import neopixel as _neopixel + elif detector.board.OS_AGNOSTIC_BOARD: + from adafruit_blinka.microcontroller.generic_agnostic_board import ( + neopixel as _neopixel, + ) + elif ( + detector.board.feather_u2if + or detector.board.feather_can_u2if + or detector.board.feather_epd_u2if + or detector.board.feather_rfm_u2if + or detector.board.qtpy_u2if + or detector.board.itsybitsy_u2if + or detector.board.macropad_u2if + or detector.board.qt2040_trinkey_u2if + or detector.board.kb2040_u2if + ): + from adafruit_blinka.microcontroller.rp2040_u2if import neopixel as _neopixel + elif "sphinx" in sys.modules: + pass else: - from adafruit_blinka.microcontroller.bcm283x import neopixel as _neopixel -elif detector.board.pico_u2if: - from adafruit_blinka.microcontroller.rp2040_u2if import neopixel as _neopixel -elif detector.board.OS_AGNOSTIC_BOARD: - from adafruit_blinka.microcontroller.generic_agnostic_board import ( - neopixel as _neopixel, - ) -elif ( - detector.board.feather_u2if - or detector.board.feather_can_u2if - or detector.board.feather_epd_u2if - or detector.board.feather_rfm_u2if - or detector.board.qtpy_u2if - or detector.board.itsybitsy_u2if - or detector.board.macropad_u2if - or detector.board.qt2040_trinkey_u2if - or detector.board.kb2040_u2if -): - from adafruit_blinka.microcontroller.rp2040_u2if import neopixel as _neopixel -elif "sphinx" in sys.modules: - pass -else: - raise NotImplementedError("Board not supported") + raise NotImplementedError("Board not supported") +except ModuleNotFoundError as error: + raise_for_missing_platform_dependency(error) def neopixel_write(gpio, buf): diff --git a/src/pwmio.py b/src/pwmio.py index 4b3f4c86..afd5190e 100644 --- a/src/pwmio.py +++ b/src/pwmio.py @@ -13,63 +13,67 @@ import sys from adafruit_blinka.agnostic import detector +from adafruit_blinka.importing import raise_for_missing_platform_dependency # pylint: disable=unused-import -if detector.board.any_raspberry_pi_5_board: - from adafruit_blinka.microcontroller.generic_linux.lgpio_pwmout import PWMOut -elif detector.board.any_raspberry_pi: - # Pi 4 or lower - from adafruit_blinka.microcontroller.generic_linux.rpi_gpio_pwmout import PWMOut -elif detector.board.any_bananapi: - from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut -elif detector.board.any_coral_board: - from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut -elif detector.board.any_giant_board: - from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut -elif detector.board.any_pcduino_board: - from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut -elif detector.board.any_beaglebone: - from adafruit_blinka.microcontroller.am335x.sysfs_pwmout import PWMOut -elif detector.board.any_lemaker: - from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut -elif detector.board.any_siemens_simatic_iot2000: - from adafruit_blinka.microcontroller.am65xx.pwmout import PWMOut -elif detector.board.any_odroid_40_pin: - from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut -elif detector.board.any_rock_pi_board: - from adafruit_blinka.microcontroller.rockchip.PWMOut import PWMOut -elif detector.board.binho_nova: - from adafruit_blinka.microcontroller.nova.pwmout import PWMOut -elif detector.board.greatfet_one: - from adafruit_blinka.microcontroller.nxp_lpc4330.pwmout import PWMOut -elif detector.board.any_lubancat: - from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut -elif detector.board.pico_u2if: - from adafruit_blinka.microcontroller.rp2040_u2if.pwmio import PWMOut -elif detector.board.any_jetson_board: - from adafruit_blinka.microcontroller.tegra.PWMOut import PWMOut -elif detector.board.any_luckfox_pico_board: - from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut -elif detector.board.any_starfive_id: - from adafruit_blinka.microcontroller.starfive.JH7110.pwmio import PWMOut -elif detector.board.any_horizon_board: - from adafruit_blinka.microcontroller.horizon.pwmio.PWMOut import PWMOut -elif detector.board.OS_AGNOSTIC_BOARD: - from adafruit_blinka.microcontroller.generic_agnostic_board.PWMOut import PWMOut -elif ( - detector.board.feather_u2if - or detector.board.feather_can_u2if - or detector.board.feather_epd_u2if - or detector.board.feather_rfm_u2if - or detector.board.qtpy_u2if - or detector.board.itsybitsy_u2if - or detector.board.macropad_u2if - or detector.board.qt2040_trinkey_u2if - or detector.board.kb2040_u2if -): - from adafruit_blinka.microcontroller.rp2040_u2if.pwmio import PWMOut -elif "sphinx" in sys.modules: - pass -else: - raise NotImplementedError("pwmio not supported for this board.") +try: + if detector.board.any_raspberry_pi_5_board: + from adafruit_blinka.microcontroller.generic_linux.lgpio_pwmout import PWMOut + elif detector.board.any_raspberry_pi: + # Pi 4 or lower + from adafruit_blinka.microcontroller.generic_linux.rpi_gpio_pwmout import PWMOut + elif detector.board.any_bananapi: + from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut + elif detector.board.any_coral_board: + from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut + elif detector.board.any_giant_board: + from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut + elif detector.board.any_pcduino_board: + from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut + elif detector.board.any_beaglebone: + from adafruit_blinka.microcontroller.am335x.sysfs_pwmout import PWMOut + elif detector.board.any_lemaker: + from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut + elif detector.board.any_siemens_simatic_iot2000: + from adafruit_blinka.microcontroller.am65xx.pwmout import PWMOut + elif detector.board.any_odroid_40_pin: + from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut + elif detector.board.any_rock_pi_board: + from adafruit_blinka.microcontroller.rockchip.PWMOut import PWMOut + elif detector.board.binho_nova: + from adafruit_blinka.microcontroller.nova.pwmout import PWMOut + elif detector.board.greatfet_one: + from adafruit_blinka.microcontroller.nxp_lpc4330.pwmout import PWMOut + elif detector.board.any_lubancat: + from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut + elif detector.board.pico_u2if: + from adafruit_blinka.microcontroller.rp2040_u2if.pwmio import PWMOut + elif detector.board.any_jetson_board: + from adafruit_blinka.microcontroller.tegra.PWMOut import PWMOut + elif detector.board.any_luckfox_pico_board: + from adafruit_blinka.microcontroller.generic_linux.sysfs_pwmout import PWMOut + elif detector.board.any_starfive_id: + from adafruit_blinka.microcontroller.starfive.JH7110.pwmio import PWMOut + elif detector.board.any_horizon_board: + from adafruit_blinka.microcontroller.horizon.pwmio.PWMOut import PWMOut + elif detector.board.OS_AGNOSTIC_BOARD: + from adafruit_blinka.microcontroller.generic_agnostic_board.PWMOut import PWMOut + elif ( + detector.board.feather_u2if + or detector.board.feather_can_u2if + or detector.board.feather_epd_u2if + or detector.board.feather_rfm_u2if + or detector.board.qtpy_u2if + or detector.board.itsybitsy_u2if + or detector.board.macropad_u2if + or detector.board.qt2040_trinkey_u2if + or detector.board.kb2040_u2if + ): + from adafruit_blinka.microcontroller.rp2040_u2if.pwmio import PWMOut + elif "sphinx" in sys.modules: + pass + else: + raise NotImplementedError("pwmio not supported for this board.") +except ModuleNotFoundError as error: + raise_for_missing_platform_dependency(error)