From ec52e82ad9398ed4a7cc6182794f8e55b69b5da6 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Sat, 22 Aug 2026 18:56:42 +0100 Subject: [PATCH 1/3] Repackage against boilerplate-python Move library/plasma and library/tests to the repo root. Replace setup.py/setup.cfg with a hatchling pyproject.toml, add tox.ini, Makefile, pre-commit and the boilerplate install/uninstall scripts. fx/plasmafx and fx/plasmafx_plugin_cycle get their own pyproject.toml; the cycle plugin declares its entry point in [project.entry-points]. Workflows rewritten as test/qa/build/install, uv-driven, Python 3.9-3.13. Delete packaging/ (Python 2 debian, dead since the 2.0.0 port) and sphinx/ (built via packaging/makedoc.sh); install.sh generates docs with pdoc instead. plasmafx.plugins now loads entry points via importlib.metadata; pkg_resources is absent on 3.12+. --- .editorconfig | 14 + .github/workflows/build.yml | 42 ++ .github/workflows/install.yml | 39 ++ .github/workflows/qa.yml | 39 ++ .github/workflows/test.yml | 34 +- .gitignore | 8 +- .pre-commit-config.yaml | 22 + library/CHANGELOG.txt => CHANGELOG.md | 3 +- Makefile | 79 ++++ README.md | 8 +- daemon/etc/plasma/README.txt | 2 +- daemon/install.sh | 2 +- daemon/usr/bin/plasma | 11 +- documentation/LEGACY.md | 68 +-- documentation/REFERENCE.md | 2 +- examples/larson_hue.py | 3 +- examples/led-strip-mini-hat-ws2812.yml | 1 - examples/rainbow.py | 1 - examples/setup.cfg | 7 - examples/solid-colour.py | 1 - fx/plasmafx/CHANGELOG.md | 6 + fx/plasmafx/CHANGELOG.txt | 0 fx/plasmafx/MANIFEST.in | 5 - fx/plasmafx/README.md | 2 +- fx/plasmafx/plasmafx/plugins.py | 10 +- fx/plasmafx/pyproject.toml | 65 +++ fx/plasmafx/setup.cfg | 42 -- fx/plasmafx/setup.py | 27 -- fx/plasmafx/tox.ini | 23 -- fx/plasmafx_plugin_cycle/CHANGELOG.md | 6 + fx/plasmafx_plugin_cycle/CHANGELOG.txt | 0 fx/plasmafx_plugin_cycle/MANIFEST.in | 5 - fx/plasmafx_plugin_cycle/README.md | 2 +- .../plasmafx_plugin_cycle/__init__.py | 3 +- fx/plasmafx_plugin_cycle/pyproject.toml | 72 ++++ fx/plasmafx_plugin_cycle/setup.cfg | 46 --- fx/plasmafx_plugin_cycle/setup.py | 27 -- fx/plasmafx_plugin_cycle/tox.ini | 23 -- fx/test.py | 5 +- install.sh | 389 +++++++++++++++++- library/.coveragerc | 4 - library/LICENSE.txt | 21 - library/MANIFEST.in | 5 - library/README.md | 123 ------ library/setup.cfg | 45 -- library/setup.py | 27 -- library/tox.ini | 26 -- packaging/CHANGELOG | 5 - packaging/debian/README | 6 - packaging/debian/changelog | 5 - packaging/debian/clean | 1 - packaging/debian/compat | 1 - packaging/debian/control | 31 -- packaging/debian/copyright | 26 -- packaging/debian/docs | 0 packaging/debian/rules | 12 - packaging/debian/source/format | 1 - packaging/debian/source/options | 3 - packaging/makeall.sh | 165 -------- packaging/makedeb.sh | 38 -- packaging/makedoc.sh | 37 -- packaging/makelog.sh | 82 ---- {library/plasma => plasma}/__init__.py | 2 +- {library/plasma => plasma}/apa102.py | 1 + {library/plasma => plasma}/core.py | 0 {library/plasma => plasma}/gpio.py | 0 {library/plasma => plasma}/legacy.py | 1 - {library/plasma => plasma}/matrix.py | 3 +- {library/plasma => plasma}/serial.py | 3 +- {library/plasma => plasma}/usb.py | 0 {library/plasma => plasma}/ws281x.py | 0 pyproject.toml | 130 ++++++ sphinx/_static/custom.css | 39 -- sphinx/_templates/breadcrumbs.html | 0 sphinx/_templates/layout.html | 4 - sphinx/conf.py | 389 ------------------ sphinx/favicon.png | Bin 26049 -> 0 bytes sphinx/index.rst | 90 ---- sphinx/shop-logo.png | Bin 19652 -> 0 bytes {library/tests => tests}/conftest.py | 6 +- {library/tests => tests}/test_apa102.py | 6 +- {library/tests => tests}/test_auto.py | 3 +- {library/tests => tests}/test_fx.py | 8 +- {library/tests => tests}/test_get_device.py | 0 {library/tests => tests}/test_legacy_gpio.py | 0 {library/tests => tests}/test_matrix.py | 0 {library/tests => tests}/test_serial.py | 1 - {library/tests => tests}/test_setup.py | 0 {library/tests => tests}/test_ws281x.py | 1 - tox.ini | 27 ++ uninstall.sh | 72 ++++ 91 files changed, 1095 insertions(+), 1499 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/install.yml create mode 100644 .github/workflows/qa.yml create mode 100644 .pre-commit-config.yaml rename library/CHANGELOG.txt => CHANGELOG.md (98%) create mode 100644 Makefile delete mode 100644 examples/setup.cfg create mode 100644 fx/plasmafx/CHANGELOG.md delete mode 100644 fx/plasmafx/CHANGELOG.txt delete mode 100644 fx/plasmafx/MANIFEST.in create mode 100644 fx/plasmafx/pyproject.toml delete mode 100644 fx/plasmafx/setup.cfg delete mode 100644 fx/plasmafx/setup.py delete mode 100644 fx/plasmafx/tox.ini create mode 100644 fx/plasmafx_plugin_cycle/CHANGELOG.md delete mode 100644 fx/plasmafx_plugin_cycle/CHANGELOG.txt delete mode 100644 fx/plasmafx_plugin_cycle/MANIFEST.in create mode 100644 fx/plasmafx_plugin_cycle/pyproject.toml delete mode 100644 fx/plasmafx_plugin_cycle/setup.cfg delete mode 100644 fx/plasmafx_plugin_cycle/setup.py delete mode 100644 fx/plasmafx_plugin_cycle/tox.ini delete mode 100644 library/.coveragerc delete mode 100644 library/LICENSE.txt delete mode 100644 library/MANIFEST.in delete mode 100644 library/README.md delete mode 100644 library/setup.cfg delete mode 100755 library/setup.py delete mode 100644 library/tox.ini delete mode 100644 packaging/CHANGELOG delete mode 100644 packaging/debian/README delete mode 100644 packaging/debian/changelog delete mode 100644 packaging/debian/clean delete mode 100644 packaging/debian/compat delete mode 100644 packaging/debian/control delete mode 100644 packaging/debian/copyright delete mode 100644 packaging/debian/docs delete mode 100755 packaging/debian/rules delete mode 100644 packaging/debian/source/format delete mode 100644 packaging/debian/source/options delete mode 100755 packaging/makeall.sh delete mode 100755 packaging/makedeb.sh delete mode 100755 packaging/makedoc.sh delete mode 100755 packaging/makelog.sh rename {library/plasma => plasma}/__init__.py (100%) rename {library/plasma => plasma}/apa102.py (99%) rename {library/plasma => plasma}/core.py (100%) rename {library/plasma => plasma}/gpio.py (100%) rename {library/plasma => plasma}/legacy.py (99%) rename {library/plasma => plasma}/matrix.py (99%) rename {library/plasma => plasma}/serial.py (99%) rename {library/plasma => plasma}/usb.py (100%) rename {library/plasma => plasma}/ws281x.py (100%) create mode 100644 pyproject.toml delete mode 100644 sphinx/_static/custom.css delete mode 100644 sphinx/_templates/breadcrumbs.html delete mode 100644 sphinx/_templates/layout.html delete mode 100644 sphinx/conf.py delete mode 100644 sphinx/favicon.png delete mode 100644 sphinx/index.rst delete mode 100644 sphinx/shop-logo.png rename {library/tests => tests}/conftest.py (99%) rename {library/tests => tests}/test_apa102.py (99%) rename {library/tests => tests}/test_auto.py (99%) rename {library/tests => tests}/test_fx.py (88%) rename {library/tests => tests}/test_get_device.py (100%) rename {library/tests => tests}/test_legacy_gpio.py (100%) rename {library/tests => tests}/test_matrix.py (100%) rename {library/tests => tests}/test_serial.py (98%) rename {library/tests => tests}/test_setup.py (100%) rename {library/tests => tests}/test_ws281x.py (98%) create mode 100644 tox.ini create mode 100755 uninstall.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3537c50 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[Makefile] +indent_style = tab + +[*.{py,cfg,ini,toml,yaml,yml,md,sh}] +indent_style = space +indent_size = 4 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..e138a95 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,42 @@ +name: Build + +on: + pull_request: + push: + branches: + - master + +permissions: + contents: read + +jobs: + test: + name: Build (Python ${{ matrix.python }}) + runs-on: ubuntu-latest + strategy: + matrix: + python: ['3.9', '3.10', '3.11', '3.12', '3.13'] + + env: + TERM: xterm-256color + RELEASE_FILE: ${{ github.event.repository.name }}-${{ github.event.release.tag_name || github.sha }}-py${{ matrix.python }} + + steps: + - name: Checkout Code + uses: actions/checkout@v5 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + python-version: ${{ matrix.python }} + + - name: Build Packages + run: | + uv run make build + + - name: Upload Packages + uses: actions/upload-artifact@v4 + with: + name: ${{ env.RELEASE_FILE }} + path: dist/ diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml new file mode 100644 index 0000000..d4da262 --- /dev/null +++ b/.github/workflows/install.yml @@ -0,0 +1,39 @@ +name: Install Test + +on: + pull_request: + push: + branches: + - master + +permissions: + contents: read + +jobs: + test: + name: Install (Python ${{ matrix.python }}) + runs-on: ubuntu-latest + env: + TERM: xterm-256color + strategy: + matrix: + python: ['3.9', '3.10', '3.11', '3.12', '3.13'] + + steps: + - name: Checkout Code + uses: actions/checkout@v5 + + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python }} + + - name: Stub files & Patch install.sh + run: | + mkdir -p boot/firmware + touch boot/firmware/config.txt + sed -i "s|/boot/firmware|`pwd`/boot/firmware|g" install.sh + + - name: Run install.sh + run: | + ./install.sh --unstable --force diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml new file mode 100644 index 0000000..fb0e72c --- /dev/null +++ b/.github/workflows/qa.yml @@ -0,0 +1,39 @@ +name: QA + +on: + pull_request: + push: + branches: + - master + +permissions: + contents: read + +jobs: + test: + name: Linting & Spelling + runs-on: ubuntu-latest + env: + TERM: xterm-256color + + steps: + - name: Checkout Code + uses: actions/checkout@v5 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + python-version: '3.11' + + - name: Run Quality Assurance + run: | + uv run make qa + + - name: Run Code Checks + run: | + uv run make check + + - name: Run Pre-Commit + run: | + uv run pre-commit run --all-files diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f8c398c..c775cbc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: Python Tests +name: Tests on: pull_request: @@ -6,32 +6,36 @@ on: branches: - master +permissions: + contents: read + jobs: test: + name: Test (Python ${{ matrix.python }}) runs-on: ubuntu-latest + env: + TERM: xterm-256color strategy: matrix: - python: [3.7, 3.9] + python: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 + - name: Checkout Code + uses: actions/checkout@v5 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 with: + enable-cache: true python-version: ${{ matrix.python }} - - name: Install Dependencies - run: | - python -m pip install --upgrade setuptools tox + - name: Run Tests - working-directory: library run: | - tox -e py + uv run make pytest + - name: Coverage + if: ${{ matrix.python == '3.9' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - working-directory: library run: | - python -m pip install coveralls - coveralls --service=github - if: ${{ matrix.python == '3.9' }} - + uvx coveralls --service=github diff --git a/.gitignore b/.gitignore index 04cef20..066e378 100644 --- a/.gitignore +++ b/.gitignore @@ -13,10 +13,10 @@ __pycache__ *.build *.changes *.orig.* -packaging/*tar.xz -library/debian/ .coverage -.tox/ +.pytest_cache +.tox +.venv .vscode/ -.pytest_cache/ venv/ +uv.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..53210a8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: mixed-line-ending + args: ['--fix=lf'] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.20 + hooks: + - id: ruff-check + args: ['--fix'] + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + additional_dependencies: ['tomli'] + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.10.0.1 + hooks: + - id: shellcheck diff --git a/library/CHANGELOG.txt b/CHANGELOG.md similarity index 98% rename from library/CHANGELOG.txt rename to CHANGELOG.md index 4c3f4c6..2a54e0e 100644 --- a/library/CHANGELOG.txt +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + 2.0.2 ----- @@ -31,4 +33,3 @@ ----- * Initial Release - diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..15f193c --- /dev/null +++ b/Makefile @@ -0,0 +1,79 @@ +LIBRARY_NAME := $(shell hatch project metadata name 2> /dev/null) +LIBRARY_VERSION := $(shell hatch version 2> /dev/null) + +.PHONY: usage version install uninstall dev-deps check pre-commit qa pytest nopost tag build clean testdeploy deploy +usage: +ifdef LIBRARY_NAME + @echo "Library: ${LIBRARY_NAME}" + @echo "Version: ${LIBRARY_VERSION}\n" +else + @echo "WARNING: You should 'make dev-deps'\n" +endif + @echo "Usage: make , where target is one of:\n" + @echo "install: install the library locally from source" + @echo "uninstall: uninstall the local library" + @echo "dev-deps: install Python dev dependencies" + @echo "check: verify CHANGELOG.md has an entry for the current version" + @echo "qa: run package QA (check-manifest, build, twine)" + @echo "pre-commit: run pre-commit hooks (lint, whitespace) on all files" + @echo "pytest: run Python test fixtures" + @echo "clean: clean Python build and dist directories" + @echo "build: build Python distribution files" + @echo "testdeploy: build and upload to test PyPi" + @echo "deploy: build and upload to PyPi" + @echo "tag: tag the repository with the current version\n" + +version: + @hatch version + +install: + ./install.sh --unstable + +uninstall: + ./uninstall.sh + +dev-deps: + python3 -m pip install --group dev + pre-commit install + +check: + @LIBRARY_VERSION=`hatch version | awk -F '.' '{print $$1"."$$2"."$$3}'`; \ + if grep -q "^$$LIBRARY_VERSION" CHANGELOG.md; then \ + echo "Changes found for version $$LIBRARY_VERSION."; \ + else \ + echo "Changes missing for version $$LIBRARY_VERSION! Please update CHANGELOG.md."; \ + exit 1; \ + fi + +pre-commit: + pre-commit run --all-files + +qa: + tox -e qa + +pytest: + tox -e py + +nopost: + @POST_VERSION=`hatch version | awk -F '.' '{print $$4}'`; \ + if [ -n "$$POST_VERSION" ]; then \ + echo "Found .$$POST_VERSION on library version; only use these for testpypi releases."; \ + exit 1; \ + fi + +tag: version + git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}" + +build: check + uv build + uv build --out-dir dist fx/plasmafx + uv build --out-dir dist fx/plasmafx_plugin_cycle + +clean: + -rm -r dist + +testdeploy: build + twine upload --repository testpypi dist/* + +deploy: nopost build + twine upload dist/* diff --git a/README.md b/README.md index 2f9b35c..23f42d6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Plasma is an LED/Light sequencing suite written to harmonise a variety of LED st Plasma also includes plasmad, a system daemon for sequencing light strips using PNG images to provide animation frames. -[![Build Status](https://shields.io/github/workflow/status/pimoroni/plasma/Python%20Tests.svg)](https://github.com/pimoroni/plasma/actions/workflows/test.yml) +[![Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/plasma/test.yml?branch=master)](https://github.com/pimoroni/plasma/actions/workflows/test.yml) [![Coverage Status](https://coveralls.io/repos/github/pimoroni/plasma/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/plasma?branch=master) [![PyPi Package](https://img.shields.io/pypi/v/plasmalights.svg)](https://pypi.python.org/pypi/plasmalights) [![Python Versions](https://img.shields.io/pypi/pyversions/plasmalights.svg)](https://pypi.python.org/pypi/plasmalights) @@ -40,7 +40,7 @@ If you choose to download examples you'll find them in `/home/pi/Pimoroni/plasma ### Manual install: ```bash -sudo pip3 install plasmalights +python3 -m pip install plasmalights ``` ### Using Plasma Daemon @@ -73,10 +73,10 @@ The Plasma daemon installer installs two programs onto your Raspberry Pi. `plasm ### Development: -If you want to contribute, or like living on the edge of your seat by having the latest code, you should clone this repository, `cd` to the library directory, and run: +If you want to contribute, or like living on the edge of your seat by having the latest code, you should clone this repository and run: ```bash -sudo python3 setup.py install +./install.sh --unstable ``` ## Documentation & Support diff --git a/daemon/etc/plasma/README.txt b/daemon/etc/plasma/README.txt index ae5303b..d77cf9c 100644 --- a/daemon/etc/plasma/README.txt +++ b/daemon/etc/plasma/README.txt @@ -14,4 +14,4 @@ The width of that PNG file should be: The height of the PNG file in pixels corresponds to the number of frames in the animation. -Since Plasma is animated at 60fps, a 60 pixel high animation will last for 1 second. +Since Plasma is animated at 60fps, a 60 pixel high animation will last for 1 second. diff --git a/daemon/install.sh b/daemon/install.sh index 2fdce5c..ce9ddda 100755 --- a/daemon/install.sh +++ b/daemon/install.sh @@ -1,6 +1,6 @@ #!/bin/bash -if [ $(id -u) -ne 0 ]; then +if [ "$(id -u)" -ne 0 ]; then printf "Script must be run as root. Try 'sudo ./install.sh'\n" exit 1 fi diff --git a/daemon/usr/bin/plasma b/daemon/usr/bin/plasma index c6ba82a..4f10090 100755 --- a/daemon/usr/bin/plasma +++ b/daemon/usr/bin/plasma @@ -1,13 +1,14 @@ #!/usr/bin/env python3 -import png -import time -import signal +import argparse import os +import signal import sys import threading +import time from datetime import datetime -import argparse + +import png # Application Defaults CONFIG_FILE = "/etc/plasma/plasma.conf" @@ -55,7 +56,7 @@ class FIFO(): return buf if len(c) == 1: buf += c - except BlockingIOError as err: + except BlockingIOError: continue return None diff --git a/documentation/LEGACY.md b/documentation/LEGACY.md index 5a82f2f..aefdd7e 100644 --- a/documentation/LEGACY.md +++ b/documentation/LEGACY.md @@ -1,34 +1,34 @@ -# Plasma Function Reference - -Before getting started you should tell Plasma how may lights you have: - -``` -import plasma -plasma.set_light_count(10) -``` - -The two Plasma methods you'll most commonly use are `set_pixel` and `show`. Here's a simple example: - -``` -from plasma import set_pixel, show - -set_pixel(0,255,0,0) -show() -``` - -`set_pixel` takes an optional fifth parameter; the brightness from 0.0 to 1.0. - -`set_pixel(pixel_no, red, green, blue, brightness)` - -You can also change the brightness with `set_brightness` from 0.0 to 1.0, for example: - -``` -from plasma import set_brightness - -set_brightness(0.5) -show() -``` - -Additionally, there exists a helper function `set_all` which allows you to set all pixels to the same color and brightness. The `brightness` parameter is just like the one in `set_pixel`: it's optional and ranges from 0.0 to 1.0. - -`set_all(red, green, blue, brightness)` +# Plasma Function Reference + +Before getting started you should tell Plasma how may lights you have: + +``` +import plasma +plasma.set_light_count(10) +``` + +The two Plasma methods you'll most commonly use are `set_pixel` and `show`. Here's a simple example: + +``` +from plasma import set_pixel, show + +set_pixel(0,255,0,0) +show() +``` + +`set_pixel` takes an optional fifth parameter; the brightness from 0.0 to 1.0. + +`set_pixel(pixel_no, red, green, blue, brightness)` + +You can also change the brightness with `set_brightness` from 0.0 to 1.0, for example: + +``` +from plasma import set_brightness + +set_brightness(0.5) +show() +``` + +Additionally, there exists a helper function `set_all` which allows you to set all pixels to the same color and brightness. The `brightness` parameter is just like the one in `set_pixel`: it's optional and ranges from 0.0 to 1.0. + +`set_all(red, green, blue, brightness)` diff --git a/documentation/REFERENCE.md b/documentation/REFERENCE.md index 5b35389..a7240b1 100644 --- a/documentation/REFERENCE.md +++ b/documentation/REFERENCE.md @@ -106,4 +106,4 @@ And to drive 20 WS281X LEDs connected to pin 18 you would use: ``` -### Configuring using a config file \ No newline at end of file +### Configuring using a config file diff --git a/examples/larson_hue.py b/examples/larson_hue.py index e59f5ae..af40dae 100755 --- a/examples/larson_hue.py +++ b/examples/larson_hue.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 +import colorsys import math import time -import colorsys from plasma import auto - NUM_PIXELS = 10 * 4 FALLOFF = 1.9 SCAN_SPEED = 4 diff --git a/examples/led-strip-mini-hat-ws2812.yml b/examples/led-strip-mini-hat-ws2812.yml index 26db581..82a6459 100644 --- a/examples/led-strip-mini-hat-ws2812.yml +++ b/examples/led-strip-mini-hat-ws2812.yml @@ -12,4 +12,3 @@ devices: gpio_pin: 13 pixels: 25 offset: 25 - diff --git a/examples/rainbow.py b/examples/rainbow.py index 7180c9a..4c4ecdc 100755 --- a/examples/rainbow.py +++ b/examples/rainbow.py @@ -5,7 +5,6 @@ from plasma import auto - NUM_PIXELS = 10 * 4 # Original Plasma light boards have 4 pixels per light diff --git a/examples/setup.cfg b/examples/setup.cfg deleted file mode 100644 index 0c4ecd5..0000000 --- a/examples/setup.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[flake8] -ignore= - D100 # Ignore various docstring reqs - D101 - D102 - D103 - E501 # Ignore long lines \ No newline at end of file diff --git a/examples/solid-colour.py b/examples/solid-colour.py index 469c1a0..61106c6 100755 --- a/examples/solid-colour.py +++ b/examples/solid-colour.py @@ -5,7 +5,6 @@ from plasma import auto - NUM_PIXELS = 10 * 4 # Original Plasma light boards have 4 pixels per light diff --git a/fx/plasmafx/CHANGELOG.md b/fx/plasmafx/CHANGELOG.md new file mode 100644 index 0000000..7caad91 --- /dev/null +++ b/fx/plasmafx/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +1.0.0 +----- + +* Initial Release diff --git a/fx/plasmafx/CHANGELOG.txt b/fx/plasmafx/CHANGELOG.txt deleted file mode 100644 index e69de29..0000000 diff --git a/fx/plasmafx/MANIFEST.in b/fx/plasmafx/MANIFEST.in deleted file mode 100644 index b26c269..0000000 --- a/fx/plasmafx/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include CHANGELOG.txt -include LICENSE.txt -include README.md -include setup.py -recursive-include plasmafx *.py diff --git a/fx/plasmafx/README.md b/fx/plasmafx/README.md index fa36d4c..d82ca21 100644 --- a/fx/plasmafx/README.md +++ b/fx/plasmafx/README.md @@ -4,4 +4,4 @@ PlasmaFX allows you to split a chain of Plasma LEDs into chunks, each handled by For example, you can assign a plugin to a Plasma Light board so it can control the 4 onboard LEDs in interesting ways. -Or, you could assign a plugin to an Adafruit NeoPixel ring wired in series with other NeoPixel boards. \ No newline at end of file +Or, you could assign a plugin to an Adafruit NeoPixel ring wired in series with other NeoPixel boards. diff --git a/fx/plasmafx/plasmafx/plugins.py b/fx/plasmafx/plasmafx/plugins.py index 0c590c7..9162b39 100644 --- a/fx/plasmafx/plasmafx/plugins.py +++ b/fx/plasmafx/plasmafx/plugins.py @@ -1,12 +1,18 @@ """Plasma: Light FX Sequencer - Built-in plugins.""" -import pkg_resources +import sys +from importlib.metadata import entry_points + from .core import Plugin +if sys.version_info >= (3, 10): + effect_plugins = entry_points(group="plasmafx.effect_plugins") +else: + effect_plugins = entry_points().get("plasmafx.effect_plugins", []) plasma_fx_plugins = {} -for entry_point in pkg_resources.iter_entry_points("plasmafx.effect_plugins"): +for entry_point in effect_plugins: effect_handle = entry_point.name plasma_fx_plugins[effect_handle] = entry_point.load() globals()[f"FX{effect_handle}"] = entry_point.load() diff --git a/fx/plasmafx/pyproject.toml b/fx/plasmafx/pyproject.toml new file mode 100644 index 0000000..ab411c0 --- /dev/null +++ b/fx/plasmafx/pyproject.toml @@ -0,0 +1,65 @@ +[build-system] +requires = ["hatchling", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[project] +name = "plasmafx" +version = "1.0.0" +dynamic = ["readme"] +description = "LED effects sequencer for Plasma LED Driver" +license = {file = "LICENSE.txt"} +requires-python = ">= 3.9" +authors = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +maintainers = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +keywords = [ + "Pi", + "Raspberry", + "LED", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Hardware", +] +dependencies = [] + +[project.urls] +GitHub = "https://www.github.com/pimoroni/plasma" +Homepage = "https://www.pimoroni.com" + +[tool.hatch.build] +include = [ + "plasmafx" +] + +[tool.hatch.build.targets.sdist] +include = [ + "*" +] +exclude = [ + ".*", + "dist" +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" +fragments = [ + { path = "README.md" }, + { text = "\n" }, + { path = "CHANGELOG.md" } +] diff --git a/fx/plasmafx/setup.cfg b/fx/plasmafx/setup.cfg deleted file mode 100644 index 2e9755a..0000000 --- a/fx/plasmafx/setup.cfg +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- -[metadata] -name = plasmafx -version = 1.0.0 -author = Philip Howard -author_email = phil@pimoroni.com -description = LED effects sequencer for Plasma LED Driver -long_description = file: README.md -long_description_content_type = text/markdown -keywords = Rasperry Pi LED -url = https://www.pimoroni.com -project_urls = - GitHub=https://www.github.com/pimoroni/plasma -license = MIT -license_files = LICENSE.txt -classifiers = - Development Status :: 4 - Beta - Operating System :: POSIX :: Linux - License :: OSI Approved :: MIT License - Intended Audience :: Developers - Programming Language :: Python :: 3.7 - Topic :: Software Development - Topic :: Software Development :: Libraries - Topic :: System :: Hardware - -[options] -packages = plasmafx -python_requires = >= 3.7 -install_requires = - -[flake8] -exclude = - test.py - tests/ - .tox, - .eggs, - .git, - __pycache__, - build, - dist -ignore = - E501 diff --git a/fx/plasmafx/setup.py b/fx/plasmafx/setup.py deleted file mode 100644 index d4e5d73..0000000 --- a/fx/plasmafx/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2020 Pimoroni. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -from setuptools import setup - -setup() diff --git a/fx/plasmafx/tox.ini b/fx/plasmafx/tox.ini deleted file mode 100644 index de5b651..0000000 --- a/fx/plasmafx/tox.ini +++ /dev/null @@ -1,23 +0,0 @@ -[tox] -envlist = py{37},qa -skip_missing_interpreters = True - -[testenv] -commands = - python setup.py install - coverage run -m py.test -v -r wsx - coverage report -deps = - mock - pytest>=3.1 - pytest-cov - -[testenv:qa] -commands = - flake8 - python setup.py sdist bdist_wheel - twine check dist/* -deps = - flake8 - flake8-docstrings - twine diff --git a/fx/plasmafx_plugin_cycle/CHANGELOG.md b/fx/plasmafx_plugin_cycle/CHANGELOG.md new file mode 100644 index 0000000..7caad91 --- /dev/null +++ b/fx/plasmafx_plugin_cycle/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +1.0.0 +----- + +* Initial Release diff --git a/fx/plasmafx_plugin_cycle/CHANGELOG.txt b/fx/plasmafx_plugin_cycle/CHANGELOG.txt deleted file mode 100644 index e69de29..0000000 diff --git a/fx/plasmafx_plugin_cycle/MANIFEST.in b/fx/plasmafx_plugin_cycle/MANIFEST.in deleted file mode 100644 index 4335fd5..0000000 --- a/fx/plasmafx_plugin_cycle/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include CHANGELOG.txt -include LICENSE.txt -include README.md -include setup.py -recursive-include plasmafx_plugin_cycle *.py diff --git a/fx/plasmafx_plugin_cycle/README.md b/fx/plasmafx_plugin_cycle/README.md index f444530..0a38382 100644 --- a/fx/plasmafx_plugin_cycle/README.md +++ b/fx/plasmafx_plugin_cycle/README.md @@ -1 +1 @@ -# PlasmaFX - LED Colour Cycling Plugin \ No newline at end of file +# PlasmaFX - LED Colour Cycling Plugin diff --git a/fx/plasmafx_plugin_cycle/plasmafx_plugin_cycle/__init__.py b/fx/plasmafx_plugin_cycle/plasmafx_plugin_cycle/__init__.py index c21a43b..c4c4b66 100644 --- a/fx/plasmafx_plugin_cycle/plasmafx_plugin_cycle/__init__.py +++ b/fx/plasmafx_plugin_cycle/plasmafx_plugin_cycle/__init__.py @@ -1,7 +1,8 @@ """Plasma: Light FX Sequencer - Colour Cycling Plugin.""" -from plasmafx.core import Plugin from colorsys import hsv_to_rgb +from plasmafx.core import Plugin + class Cycle(Plugin): """Plasma: Light FX Sequencer - Colour Cycling Plugin.""" diff --git a/fx/plasmafx_plugin_cycle/pyproject.toml b/fx/plasmafx_plugin_cycle/pyproject.toml new file mode 100644 index 0000000..34a96b0 --- /dev/null +++ b/fx/plasmafx_plugin_cycle/pyproject.toml @@ -0,0 +1,72 @@ +[build-system] +requires = ["hatchling", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[project] +name = "plasmafx-plugin-cycle" +version = "1.0.0" +dynamic = ["readme"] +description = "Colour cycle plugin for plasmafx" +license = {file = "LICENSE.txt"} +requires-python = ">= 3.9" +authors = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +maintainers = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +keywords = [ + "Pi", + "Raspberry", + "plugin", + "effects", + "lighting", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Hardware", +] +dependencies = [ + "plasmafx", +] + +[project.entry-points."plasmafx.effect_plugins"] +Cycle = "plasmafx_plugin_cycle:Cycle" + +[project.urls] +GitHub = "https://www.github.com/pimoroni/plasma" +Homepage = "https://www.pimoroni.com" + +[tool.hatch.build] +include = [ + "plasmafx_plugin_cycle" +] + +[tool.hatch.build.targets.sdist] +include = [ + "*" +] +exclude = [ + ".*", + "dist" +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" +fragments = [ + { path = "README.md" }, + { text = "\n" }, + { path = "CHANGELOG.md" } +] diff --git a/fx/plasmafx_plugin_cycle/setup.cfg b/fx/plasmafx_plugin_cycle/setup.cfg deleted file mode 100644 index 7e0bebd..0000000 --- a/fx/plasmafx_plugin_cycle/setup.cfg +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -[metadata] -name = plasmafx-plugin-cycle -version = 1.0.0 -author = Philip Howard -author_email = phil@pimoroni.com -description = Colour cycle plugin for plasmafx -long_description = file: README.md -long_description_content_type = text/markdown -keywords = plugin, effects, lighting -url = https://www.pimoroni.com -project_urls = - GitHub=https://www.github.com/pimoroni/plasma -license = MIT -license_files = LICENSE.txt -classifiers = - Development Status :: 4 - Beta - Operating System :: POSIX :: Linux - License :: OSI Approved :: MIT License - Intended Audience :: Developers - Programming Language :: Python :: 3.7 - Topic :: Software Development - Topic :: Software Development :: Libraries - Topic :: System :: Hardware - -[options] -packages = plasmafx_plugin_cycle -python_requires = >= 3.7 -install_requires = - -[options.entry_points] -plasmafx.effect_plugins = - Cycle = plasmafx_plugin_cycle:Cycle - -[flake8] -exclude = - test.py - tests/ - .tox, - .eggs, - .git, - __pycache__, - build, - dist -ignore = - E501 diff --git a/fx/plasmafx_plugin_cycle/setup.py b/fx/plasmafx_plugin_cycle/setup.py deleted file mode 100644 index d4e5d73..0000000 --- a/fx/plasmafx_plugin_cycle/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2020 Pimoroni. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -from setuptools import setup - -setup() diff --git a/fx/plasmafx_plugin_cycle/tox.ini b/fx/plasmafx_plugin_cycle/tox.ini deleted file mode 100644 index de5b651..0000000 --- a/fx/plasmafx_plugin_cycle/tox.ini +++ /dev/null @@ -1,23 +0,0 @@ -[tox] -envlist = py{37},qa -skip_missing_interpreters = True - -[testenv] -commands = - python setup.py install - coverage run -m py.test -v -r wsx - coverage report -deps = - mock - pytest>=3.1 - pytest-cov - -[testenv:qa] -commands = - flake8 - python setup.py sdist bdist_wheel - twine check dist/* -deps = - flake8 - flake8-docstrings - twine diff --git a/fx/test.py b/fx/test.py index 32c765f..653435a 100644 --- a/fx/test.py +++ b/fx/test.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -from plasma import auto +import time + import plasmafx from plasmafx import plugins -import time +from plasma import auto FPS = 60 PIXEL_COUNT = 10 diff --git a/install.sh b/install.sh index b85f0c4..31da6bd 100755 --- a/install.sh +++ b/install.sh @@ -1,21 +1,386 @@ #!/bin/bash +LIBRARY_NAME=$(grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}') +MODULE_NAME="plasma" +CONFIG_FILE=config.txt +CONFIG_DIR="/boot/firmware" +DATESTAMP=$(date "+%Y-%m-%d-%H-%M-%S") +CONFIG_BACKUP=false +APT_HAS_UPDATED=false +RESOURCES_TOP_DIR="$HOME/Pimoroni" +VENV_BASH_SNIPPET="$RESOURCES_TOP_DIR/auto_venv.sh" +VENV_DIR="$HOME/.virtualenvs/pimoroni" +USAGE="./install.sh (--unstable)" +POSITIONAL_ARGS=() +FORCE=false +UNSTABLE=false +PYTHON="python" +CMD_ERRORS=false -if [ $(id -u) -ne 0 ]; then - printf "Script must be run as root. Try 'sudo ./install.sh'\n" + +user_check() { + if [ "$(id -u)" -eq 0 ]; then + fatal "Script should not be run as root. Try './install.sh'\n" + fi +} + +confirm() { + if $FORCE; then + true + else + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi + fi +} + +success() { + echo -e "$(tput setaf 2)$1$(tput sgr0)" +} + +inform() { + echo -e "$(tput setaf 6)$1$(tput sgr0)" +} + +warning() { + echo -e "$(tput setaf 1)⚠ WARNING:$(tput sgr0) $1" +} + +fatal() { + echo -e "$(tput setaf 1)⚠ FATAL:$(tput sgr0) $1" exit 1 +} + +find_config() { + if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then + CONFIG_DIR="/boot" + if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then + fatal "Could not find $CONFIG_FILE!" + fi + fi + inform "Using $CONFIG_FILE in $CONFIG_DIR" +} + +venv_bash_snippet() { + inform "Checking for $VENV_BASH_SNIPPET\n" + if [ ! -f "$VENV_BASH_SNIPPET" ]; then + inform "Creating $VENV_BASH_SNIPPET\n" + mkdir -p "$RESOURCES_TOP_DIR" + cat << EOF > "$VENV_BASH_SNIPPET" +# Add "source $VENV_BASH_SNIPPET" to your ~/.bashrc to activate +# the Pimoroni virtual environment automagically! +VENV_DIR="$VENV_DIR" +if [ ! -f \$VENV_DIR/bin/activate ]; then + printf "Creating user Python environment in \$VENV_DIR, please wait...\n" + mkdir -p \$VENV_DIR + python3 -m venv --system-site-packages \$VENV_DIR +fi +printf " ↓ ↓ ↓ ↓ Hello, we've activated a Python venv for you. To exit, type \"deactivate\".\n" +source \$VENV_DIR/bin/activate +EOF + fi +} + +venv_check() { + PYTHON_BIN=$(which "$PYTHON") + if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then + printf "This script should be run in a virtual Python environment.\n" + if confirm "Would you like us to create and/or use a default one?"; then + printf "\n" + if [ ! -f "$VENV_DIR/bin/activate" ]; then + inform "Creating a new virtual Python environment in $VENV_DIR, please wait...\n" + mkdir -p "$VENV_DIR" + /usr/bin/python3 -m venv "$VENV_DIR" --system-site-packages + venv_bash_snippet + # shellcheck disable=SC1091 + source "$VENV_DIR/bin/activate" + else + inform "Activating existing virtual Python environment in $VENV_DIR\n" + printf "source \"%s/bin/activate\"\n" "$VENV_DIR" + # shellcheck disable=SC1091 + source "$VENV_DIR/bin/activate" + fi + else + printf "\n" + fatal "Please create and/or activate a virtual Python environment and try again!\n" + fi + fi + printf "\n" +} + +check_for_error() { + if [ $? -ne 0 ]; then + CMD_ERRORS=true + warning "^^^ 😬 previous command did not exit cleanly!" + fi +} + +function do_config_backup { + if [ ! $CONFIG_BACKUP == true ]; then + CONFIG_BACKUP=true + FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt" + inform "Backing up $CONFIG_DIR/$CONFIG_FILE to $CONFIG_DIR/$FILENAME\n" + sudo cp "$CONFIG_DIR/$CONFIG_FILE" "$CONFIG_DIR/$FILENAME" + mkdir -p "$RESOURCES_TOP_DIR/config-backups/" + cp $CONFIG_DIR/$CONFIG_FILE "$RESOURCES_TOP_DIR/config-backups/$FILENAME" + if [ -f "$UNINSTALLER" ]; then + echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG_DIR/$CONFIG_FILE" >> "$UNINSTALLER" + fi + fi +} + +function apt_pkg_install { + PACKAGES_NEEDED=() + PACKAGES_IN=("$@") + # Check the list of packages and only run update/install if we need to + for ((i = 0; i < ${#PACKAGES_IN[@]}; i++)); do + PACKAGE="${PACKAGES_IN[$i]}" + if [ "$PACKAGE" == "" ]; then continue; fi + printf "Checking for %s\n" "$PACKAGE" + dpkg -L "$PACKAGE" > /dev/null 2>&1 + if [ "$?" == "1" ]; then + PACKAGES_NEEDED+=("$PACKAGE") + fi + done + PACKAGES="${PACKAGES_NEEDED[*]}" + if ! [ "$PACKAGES" == "" ]; then + printf "\n" + inform "Installing missing packages: $PACKAGES" + if [ ! $APT_HAS_UPDATED ]; then + sudo apt update + APT_HAS_UPDATED=true + fi + # shellcheck disable=SC2086 + sudo apt install -y $PACKAGES + check_for_error + if [ -f "$UNINSTALLER" ]; then + echo "apt uninstall -y $PACKAGES" >> "$UNINSTALLER" + fi + fi +} + +function pip_pkg_install { + # A null Keyring prevents pip stalling in the background + PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring $PYTHON -m pip install --upgrade "$@" + check_for_error +} + +function pip_requirements_install { + # A null Keyring prevents pip stalling in the background + PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring $PYTHON -m pip install -r "$@" + check_for_error +} + +while [[ $# -gt 0 ]]; do + K="$1" + case $K in + -u|--unstable) + UNSTABLE=true + shift + ;; + -f|--force) + FORCE=true + shift + ;; + -p|--python) + PYTHON=$2 + shift + shift + ;; + *) + if [[ $1 == -* ]]; then + printf "Unrecognised option: %s\n" "$1"; + printf "Usage: %s\n" "$USAGE"; + exit 1 + fi + POSITIONAL_ARGS+=("$1") + shift + esac +done + +printf "Installing %s...\n\n" "$LIBRARY_NAME" + +user_check +venv_check + +if [ ! -f "$(which "$PYTHON")" ]; then + fatal "Python path %s not found!\n" "$PYTHON" +fi + +PYTHON_VER=$($PYTHON --version) + +inform "Checking Dependencies. Please wait..." + +# Install toml and try to read pyproject.toml into bash variables + +pip_pkg_install toml + +CONFIG_VARS=$( + $PYTHON - < "$UNINSTALLER" +printf "It's recommended you run these steps manually.\n" +printf "If you want to run the full script, open it in\n" +printf "an editor and remove 'exit 1' from below.\n" +exit 1 +source $VIRTUAL_ENV/bin/activate +EOF -apt update -apt install -y python-pip -pip install pypng pyserial +printf "\n" -cd $WORKING_DIR/library -sudo python setup.py install -cd $WORKING_DIR +inform "Installing for $PYTHON_VER...\n" -cd $WORKING_DIR/daemon -./install.sh -cd $WORKING_DIR +# Install apt packages from pyproject.toml / tool.pimoroni.apt_packages +apt_pkg_install "${APT_PACKAGES[@]}" + +printf "\n" + +if $UNSTABLE; then + warning "Installing unstable library from source.\n" + pip_pkg_install . +else + inform "Installing stable library from pypi.\n" + pip_pkg_install "$LIBRARY_NAME" +fi + +# shellcheck disable=SC2181 # One of two commands run, depending on --unstable flag +if [ $? -eq 0 ]; then + success "Done!\n" + echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> "$UNINSTALLER" +fi + +find_config + +printf "\n" + +# Run the setup commands from pyproject.toml / tool.pimoroni.commands + +inform "Running setup commands...\n" +for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do + CMD="${SETUP_CMDS[$i]}" + # Attempt to catch anything that touches config.txt and trigger a backup + if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG_DIR/$CONFIG_FILE"* ]] || [[ "$CMD" == *"\$CONFIG_DIR/\$CONFIG_FILE"* ]]; then + do_config_backup + fi + if [[ ! "$CMD" == printf* ]]; then + printf "Running: \"%s\"\n" "$CMD" + fi + eval "$CMD" + check_for_error +done + +printf "\n" + +# Add the config.txt entries from pyproject.toml / tool.pimoroni.configtxt + +for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do + CONFIG_LINE="${CONFIG_TXT[$i]}" + if ! [ "$CONFIG_LINE" == "" ]; then + do_config_backup + inform "Adding $CONFIG_LINE to $CONFIG_DIR/$CONFIG_FILE" + sudo sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG_DIR/$CONFIG_FILE + if ! grep -q "^$CONFIG_LINE" $CONFIG_DIR/$CONFIG_FILE; then + printf "%s \n" "$CONFIG_LINE" | sudo tee --append $CONFIG_DIR/$CONFIG_FILE + fi + fi +done + +printf "\n" + +# Just a straight copy of the examples/ dir into ~/Pimoroni/board/examples + +if [ -d "examples" ]; then + if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then + inform "Copying examples to $RESOURCES_DIR" + cp -r examples/ "$RESOURCES_DIR" + echo "rm -r $RESOURCES_DIR" >> "$UNINSTALLER" + success "Done!" + fi +fi + +printf "\n" + +if [ -f "requirements-examples.txt" ]; then + if confirm "Would you like to install example dependencies?"; then + inform "Installing dependencies from requirements-examples.txt..." + pip_requirements_install requirements-examples.txt + fi +fi + +printf "\n" + +# Use pdoc to generate basic documentation from the installed module + +if confirm "Would you like to generate documentation?"; then + inform "Installing pdoc. Please wait..." + pip_pkg_install pdoc + inform "Generating documentation.\n" + if $PYTHON -m pdoc "$MODULE_NAME" -o "$RESOURCES_DIR/docs" > /dev/null; then + inform "Documentation saved to $RESOURCES_DIR/docs" + success "Done!" + else + warning "Error: Failed to generate documentation." + fi +fi + +printf "\n" + +if [ "$CMD_ERRORS" = true ]; then + warning "One or more setup commands appear to have failed." + printf "This might prevent things from working properly.\n" + printf "Make sure your OS is up to date and try re-running this installer.\n" + printf "If things still don't work, report this or find help at %s.\n\n" "$GITHUB_URL" +else + success "\nAll done!" +fi + +printf "If this is your first time installing you should reboot for hardware changes to take effect.\n" +printf "Find uninstall steps in %s\n\n" "$UNINSTALLER" + +if [ "$CMD_ERRORS" = true ]; then + exit 1 +else + exit 0 +fi diff --git a/library/.coveragerc b/library/.coveragerc deleted file mode 100644 index 9d4181c..0000000 --- a/library/.coveragerc +++ /dev/null @@ -1,4 +0,0 @@ -[run] -source = plasma -omit = - .tox/* diff --git a/library/LICENSE.txt b/library/LICENSE.txt deleted file mode 100644 index 8e366dc..0000000 --- a/library/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2020 Pimoroni Ltd - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/library/MANIFEST.in b/library/MANIFEST.in deleted file mode 100644 index f6f14e1..0000000 --- a/library/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include CHANGELOG.txt -include LICENSE.txt -include README.md -include setup.py -recursive-include plasma *.py diff --git a/library/README.md b/library/README.md deleted file mode 100644 index 00dabc0..0000000 --- a/library/README.md +++ /dev/null @@ -1,123 +0,0 @@ -# Plasma: LED Sequencing - -Plasma is an LED/Light sequencing suite written to harmonise a variety of LED strand/board types and interfaces into a standard API for write-once-run-anyway lighting code. - -Plasma also includes plasmad, a system daemon for sequencing light strips using PNG images to provide animation frames. - -[![Build Status](https://travis-ci.com/pimoroni/plasma.svg?branch=master)](https://travis-ci.com/pimoroni/plasma) -[![Coverage Status](https://coveralls.io/repos/github/pimoroni/plasma/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/plasma?branch=master) -[![PyPi Package](https://img.shields.io/pypi/v/plasmalights.svg)](https://pypi.python.org/pypi/plasmalights) -[![Python Versions](https://img.shields.io/pypi/pyversions/plasmalights.svg)](https://pypi.python.org/pypi/plasmalights) - -## Compatible Products - -Plasma was originally written to provide an easy way to sequence lights and swap out patterns for the Pimoroni Plasma kit. - -- https://shop.pimoroni.com/products/picade-plasma-kit-illuminated-arcade-buttons -- https://shop.pimoroni.com/products/player-x-usb-games-controller-pcb -- https://shop.pimoroni.com/products/blinkt -- https://shop.pimoroni.com/products/unicorn-hat -- https://shop.pimoroni.com/products/unicorn-phat - -## Installing - -### Full install (recommended): - -We've created an easy installation script that will install all pre-requisites and get your Plasma Arcade Button Lights -up and running with minimal efforts. To run it, fire up Terminal which you'll find in Menu -> Accessories -> Terminal -on your Raspberry Pi desktop, as illustrated below: - -![Finding the terminal](http://get.pimoroni.com/resources/github-repo-terminal.png) - -In the new terminal window type the command exactly as it appears below (check for typos) and follow the on-screen instructions: - -```bash -curl https://get.pimoroni.com/plasma | bash -``` - -If you choose to download examples you'll find them in `/home/pi/Pimoroni/plasma/`. - -### Manual install: - -```bash -sudo pip3 install plasmalights -``` - -### Using Plasma Daemon - -To install the Plasma daemon you should clone this repository, navigate to the "daemon" directory and run the installer: - -``` -git clone https://github.com/pimoroni/plasma -cd plasma/daemon -sudo ./install -``` - ---- - -Note: If you're using Picade Player X you should edit daemon/etc/systemd/system/plasma.service and change the output device option from `-o GPIO:15:14` to `-o SERIAL:/dev/ttyACM0`. If you're using Unicorn HAT or pHAT you should use `-o WS281X:WS2812:18:0`. - -If you're using GPIO on a Picade HAT you can adjust the pins accordingly using `-o GPIO::` where data and clock are valid BCM pins. If you're using the old Plasma/Hack header you may need to swap from `-o GPIO:15:14` to `-o GPIO:14:15` depending on how your connections are wired. - ---- - -The Plasma daemon installer installs two programs onto your Raspberry Pi. `plasma` itself and a tool called `plasmactl` you can use to install and switch lighting effects. Plasma runs as a service on your system. - -`plasmactl` commands: - -* `plasmactl 255 0 0` - Set Plasma lights to R, G, B colour. Red in this case. -* `plasmactl ` - Set Plasma lights to pattern image -* `plasmactl fps ` - Change plasma effect framerate (default is 30, lower FPS = less CPU) -* `plasmactl --list` - List all available patterns -* `sudo plasmactl --install ` - Install a new pattern, where `` is the filename of a 24bit PNG image file - -### Development: - -If you want to contribute, or like living on the edge of your seat by having the latest code, you should clone this repository, `cd` to the library directory, and run: - -```bash -sudo python3 setup.py install -``` - -## Documentation & Support - -* Guides and tutorials - https://learn.pimoroni.com/plasma -* Function reference - http://docs.pimoroni.com/plasma/ -* Get help - http://forums.pimoroni.com/c/support - -## Changelog - -2.0.2 ------ - -* Pass brightness values through plasma Matrix to underlying LED drivers - -2.0.1 ------ - -* New `auto()` can accept a config file path directly (for plasma daemon) -* New `Matrix` and `Core` now accept list and dict type sequences in `set_sequence` -* New `Matrix` config supports an "enabled" option for easy config switching -* Bugfix `Matrix` catches KeyError when trying to `del` non-required options - -2.0.0 ------ - -* Port to Python >=3.7, drop Python 2.7 support -* Significant refactoring and restructuring -* Config file support for configuring LED types/relationships -* WS382X support -* Chip-Select support for APA102 -* PlasmaMatrix to combine multiple output devices - -1.0.0 ------ - -* API refactor, use plasma.legacy for old API -* USB support for Picade Player X - -0.0.1 ------ - -* Initial Release - diff --git a/library/setup.cfg b/library/setup.cfg deleted file mode 100644 index ac83f02..0000000 --- a/library/setup.cfg +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- -[metadata] -name = plasmalights -version = 2.0.2 -author = Philip Howard -author_email = phil@pimoroni.com -description = Python library for driving Pimoroni Plasma and other LED devices -long_description = file: README.md -long_description_content_type = text/markdown -keywords = Rasperry Pi LED -url = https://www.pimoroni.com -project_urls = - GitHub=https://www.github.com/pimoroni/plasma -license = MIT -license_files = LICENSE.txt -classifiers = - Development Status :: 4 - Beta - Operating System :: POSIX :: Linux - License :: OSI Approved :: MIT License - Intended Audience :: Developers - Programming Language :: Python :: 3.7 - Topic :: Software Development - Topic :: Software Development :: Libraries - Topic :: System :: Hardware - -[options] -packages = plasma -python_requires = >= 3.7 -install_requires = - pyyaml - pyserial - -[flake8] -exclude = - test.py - tests/ - .tox, - .eggs, - .git, - __pycache__, - build, - dist -ignore = - E501 - D100 diff --git a/library/setup.py b/library/setup.py deleted file mode 100755 index d4e5d73..0000000 --- a/library/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2020 Pimoroni. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -from setuptools import setup - -setup() diff --git a/library/tox.ini b/library/tox.ini deleted file mode 100644 index eba4f4c..0000000 --- a/library/tox.ini +++ /dev/null @@ -1,26 +0,0 @@ -[tox] -envlist = py{37,39},qa -skip_missing_interpreters = True - -[testenv] -commands = - python setup.py install - pip install ../fx/plasmafx/ - pip install ../fx/plasmafx_plugin_cycle/ - coverage run -m py.test -v -r wsx - coverage report -deps = - mock - pytest>=3.1 - pytest-cov - -[testenv:qa] -commands = - flake8 - flake8 ../examples/ - python setup.py sdist bdist_wheel - twine check dist/* -deps = - flake8 - flake8-docstrings - twine diff --git a/packaging/CHANGELOG b/packaging/CHANGELOG deleted file mode 100644 index 3ee67c7..0000000 --- a/packaging/CHANGELOG +++ /dev/null @@ -1,5 +0,0 @@ -plasmalights (0.0.1) stable; urgency=low - - * Initial Release - - -- Phil Howard Wed, 07 Nov 2018 00:00:00 +0000 diff --git a/packaging/debian/README b/packaging/debian/README deleted file mode 100644 index bed7135..0000000 --- a/packaging/debian/README +++ /dev/null @@ -1,6 +0,0 @@ -README - -Plasma is an arcade button lighting system for your Raspberry Pi. - -Learn more: https://shop.pimoroni.com/products/plasma -For examples run: `curl -sS get.pimoroni.com/plasma | bash` diff --git a/packaging/debian/changelog b/packaging/debian/changelog deleted file mode 100644 index 3ee67c7..0000000 --- a/packaging/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -plasmalights (0.0.1) stable; urgency=low - - * Initial Release - - -- Phil Howard Wed, 07 Nov 2018 00:00:00 +0000 diff --git a/packaging/debian/clean b/packaging/debian/clean deleted file mode 100644 index 45149aa..0000000 --- a/packaging/debian/clean +++ /dev/null @@ -1 +0,0 @@ -*.egg-info/* diff --git a/packaging/debian/compat b/packaging/debian/compat deleted file mode 100644 index ec63514..0000000 --- a/packaging/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/packaging/debian/control b/packaging/debian/control deleted file mode 100644 index 603b17c..0000000 --- a/packaging/debian/control +++ /dev/null @@ -1,31 +0,0 @@ -Source: plasmalights -Maintainer: Phil Howard -Homepage: https://github.com/pimoroni/plasma -Section: python -Priority: extra -Build-Depends: debhelper (>= 9.0.0), dh-python, python-all (>= 2.7), python-setuptools, python3-all (>= 3.4), python3-setuptools -Standards-Version: 3.9.6 -X-Python-Version: >= 2.7 -X-Python3-Version: >= 3.4 - -Package: python-plasmalights -Architecture: all -Section: python -Depends: ${misc:Depends}, ${python:Depends}, python-rpi.gpio -Suggests: -Description: Python library for the Plasma Arcade Lights. - Plasma allows you to control a chain of RGB LED lights. - Ideal for adding illuminated buttons to your Raspberry Pi. - . - This is the Python 2 version of the package. - -Package: python3-plasmalights -Architecture: all -Section: python -Depends: ${misc:Depends}, ${python3:Depends}, python3-rpi.gpio -Suggests: -Description: Python library for the Plasma Arcade Lights. - Plasma allows you to control a chain of RGB LED lights. - Ideal for adding illuminated buttons to your Raspberry Pi. - . - This is the Python 3 version of the package. diff --git a/packaging/debian/copyright b/packaging/debian/copyright deleted file mode 100644 index 368ea61..0000000 --- a/packaging/debian/copyright +++ /dev/null @@ -1,26 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: plasma -Source: https://github.com/pimoroni/plasma - -Files: * -Copyright: 2016 Pimoroni Ltd -License: MIT - -License: MIT - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - . - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - . - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. diff --git a/packaging/debian/docs b/packaging/debian/docs deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/debian/rules b/packaging/debian/rules deleted file mode 100755 index e107767..0000000 --- a/packaging/debian/rules +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- - -#export DH_VERBOSE=1 -export DH_OPTIONS - -%: - dh $@ --with python2,python3 --buildsystem=python_distutils - -override_dh_auto_install: - python setup.py install --root debian/python-plasmalights --install-layout=deb - python3 setup.py install --root debian/python3-plasmalights --install-layout=deb diff --git a/packaging/debian/source/format b/packaging/debian/source/format deleted file mode 100644 index 89ae9db..0000000 --- a/packaging/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (native) diff --git a/packaging/debian/source/options b/packaging/debian/source/options deleted file mode 100644 index af670c8..0000000 --- a/packaging/debian/source/options +++ /dev/null @@ -1,3 +0,0 @@ -extend-diff-ignore = "^[^/]+\.egg-info/" -tar-ignore -tar-ignore=.tox diff --git a/packaging/makeall.sh b/packaging/makeall.sh deleted file mode 100755 index bc168e6..0000000 --- a/packaging/makeall.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/bash - -# script control variables - -reponame="" # leave this blank for auto-detection -libname="plasma" # leave this blank for auto-detection -packagename="plasmalights" # leave this blank for auto-selection - -debianlog="debian/changelog" -debcontrol="debian/control" -debcopyright="debian/copyright" -debrules="debian/rules" -debreadme="debian/README" - -debdir="$(pwd)" -rootdir="$(dirname $debdir)" -libdir="$rootdir/library" - -FLAG=false - -# function define - -success() { - echo "$(tput setaf 2)$1$(tput sgr0)" -} - -inform() { - echo "$(tput setaf 6)$1$(tput sgr0)" -} - -warning() { - echo "$(tput setaf 1)$1$(tput sgr0)" -} - -newline() { - echo "" -} - -# assessing repo and library variables - -if [ -z "$reponame" ] || [ -z "$libname" ]; then - inform "detecting reponame and libname..." -else - inform "using reponame and libname overrides" -fi - -if [ -z "$reponame" ]; then - if [[ $rootdir == *"python"* ]]; then - repodir="$(dirname $rootdir)" - reponame="$(basename $repodir)" - else - repodir="$rootdir" - reponame="$(basename $repodir)" - fi - reponame=$(echo "$reponame" | tr "[A-Z]" "[a-z]") -fi - -if [ -z "$libname" ]; then - cd "$libdir" - libname=$(grep "name" setup.py | tr -d "[:space:]" | cut -c 7- | rev | cut -c 3- | rev) - libname=$(echo "$libname" | tr "[A-Z]" "[a-z]") && cd "$debdir" -fi - -if [ -z "$packagename" ]; then - packagename="$libname" -fi - -echo "reponame is $reponame and libname is $libname" -echo "output packages will be python-$packagename and python3-$packagename" - -# checking generating changelog file - -./makelog.sh -version=$(head -n 1 "$libdir/CHANGELOG.txt") -echo "building $libname version $version" - -# checking debian/changelog file - -inform "checking debian/changelog file..." - -if ! head -n 1 $debianlog | grep "$libname" &> /dev/null; then - warning "library not mentioned in header!" && FLAG=true -elif head -n 1 $debianlog | grep "UNRELEASED"; then - warning "this changelog is not going to generate a release!" - warning "change distribution to 'stable'" && FLAG=true -fi - -# checking debian/copyright file - -inform "checking debian/copyright file..." - -if ! grep "^Source" $debcopyright | grep "$reponame" &> /dev/null; then - warning "$(grep "^Source" $debcopyright)" && FLAG=true -fi - -if ! grep "^Upstream-Name" $debcopyright | grep "$libname" &> /dev/null; then - warning "$(grep "^Upstream-Name" $debcopyright)" && FLAG=true -fi - -# checking debian/control file - -inform "checking debian/control file..." - -if ! grep "^Source" $debcontrol | grep "$libname" &> /dev/null; then - warning "$(grep "^Source" $debcontrol)" && FLAG=true -fi - -if ! grep "^Homepage" $debcontrol | grep "$reponame" &> /dev/null; then - warning "$(grep "^Homepage" $debcontrol)" && FLAG=true -fi - -if ! grep "^Package: python-$packagename" $debcontrol &> /dev/null; then - warning "$(grep "^Package: python-" $debcontrol)" && FLAG=true -fi - -if ! grep "^Package: python3-$packagename" $debcontrol &> /dev/null; then - warning "$(grep "^Package: python3-" $debcontrol)" && FLAG=true -fi - -if ! grep "^Priority: extra" $debcontrol &> /dev/null; then - warning "$(grep "^Priority" $debcontrol)" && FLAG=true -fi - - -# checking debian/rules file - -inform "checking debian/rules file..." - -if ! grep "debian/python-$packagename" $debrules &> /dev/null; then - warning "$(grep "debian/python-" $debrules)" && FLAG=true -fi - -if ! grep "debian/python3-$packagename" $debrules &> /dev/null; then - warning "$(grep "debian/python3-" $debrules)" && FLAG=true -fi - -# checking debian/README file - -inform "checking debian/readme file..." - -if ! grep -e "$libname" -e "$reponame" $debreadme &> /dev/null; then - warning "README does not seem to mention product, repo or lib!" && FLAG=true -fi - -# summary of checks pre build - -if $FLAG; then - warning "Check all of the above and correct!" && exit 1 -else - inform "we're good to go... bulding!" -fi - -# building deb and final checks - -./makedeb.sh - -inform "running lintian..." -lintian -v $(find -name "python*$version*.deb") -lintian -v $(find -name "python3*$version*.deb") - -inform "checking signatures..." -gpg --verify $(find -name "*$version*changes") -gpg --verify $(find -name "*$version*dsc") - -exit 0 diff --git a/packaging/makedeb.sh b/packaging/makedeb.sh deleted file mode 100755 index 03ebac7..0000000 --- a/packaging/makedeb.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -gettools="no" # if set to yes downloads the tools required -setup="yes" # if set to yes populates library folder -buildeb="yes" # if set to yes builds the deb files -cleanup="yes" # if set to yes cleans up build files -pkgfiles=( "build" "changes" "deb" "dsc" "tar.xz" ) - -if [ $gettools == "yes" ]; then - sudo apt-get update && sudo apt-get install build-essential debhelper devscripts dh-make dh-python dput gnupg - sudo apt-get install python-all python-setuptools python3-all python3-setuptools - sudo apt-get install python-mock python-sphinx python-sphinx-rtd-theme - sudo pip install Sphinx --upgrade && sudo pip install sphinx_rtd_theme --upgrade -fi - -if [ $setup == "yes" ]; then - rm -R ../library/build ../library/debian &> /dev/null - cp -R ./debian ../library/ && cp -R ../sphinx ../library/doc -fi - -cd ../library - -if [ $buildeb == "yes" ]; then - debuild -aarmhf - for file in ${pkgfiles[@]}; do - rm ../packaging/*.$file &> /dev/null - mv ../*.$file ../packaging - done - rm -R ../documentation/html &> /dev/null - cp -R ./build/sphinx/html ../documentation -fi - -if [ $cleanup == "yes" ]; then - debuild clean - rm -R ./build ./debian ./doc &> /dev/null -fi - -exit 0 diff --git a/packaging/makedoc.sh b/packaging/makedoc.sh deleted file mode 100755 index 244e992..0000000 --- a/packaging/makedoc.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -gettools="no" # if set to yes downloads the tools required -setup="yes" # if set to yes populates library folder -buildoc="yes" # if set to yes builds the deb files -cleanup="yes" # if set to yes cleans up build files -pkgfiles=( "build" "changes" "deb" "dsc" "tar.xz" ) - -if [ $gettools == "yes" ]; then - sudo apt-get update && sudo apt-get install build-essential debhelper devscripts dh-make dh-python - sudo apt-get install python-all python-setuptools python3-all python3-setuptools - sudo apt-get install python-mock python-sphinx python-sphinx-rtd-theme - sudo pip install Sphinx --upgrade && sudo pip install sphinx_rtd_theme --upgrade -fi - -if [ $setup == "yes" ]; then - rm -R ../library/build ../library/debian &> /dev/null - cp -R ./debian ../library/ && cp -R ../sphinx ../library/doc -fi - -cd ../library - -if [ $buildoc == "yes" ]; then - debuild - for file in ${pkgfiles[@]}; do - rm ../*.$file &> /dev/null - done - rm -R ../documentation/html &> /dev/null - cp -R ./build/sphinx/html ../documentation -fi - -if [ $cleanup == "yes" ]; then - debuild clean - rm -R ./build ./debian ./doc &> /dev/null -fi - -exit 0 diff --git a/packaging/makelog.sh b/packaging/makelog.sh deleted file mode 100755 index c4c2f9e..0000000 --- a/packaging/makelog.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -# script control variables - -libname="plasma" # leave this blank for auto-detection -sibname=() # name of sibling in packages list -versionwarn="yes" # set to anything but 'yes' to turn off warning - -debdir="$(pwd)" -rootdir="$(dirname $debdir)" -libdir="$rootdir/library" - -mainlog="CHANGELOG" -debianlog="debian/changelog" -pypilog="$libdir/CHANGELOG.txt" - -# function define - -success() { - echo "$(tput setaf 2)$1$(tput sgr0)" -} - -inform() { - echo "$(tput setaf 6)$1$(tput sgr0)" -} - -warning() { - echo "$(tput setaf 1)$1$(tput sgr0)" -} - -newline() { - echo "" -} - -# generate debian changelog - -cat $mainlog > $debianlog -inform "seeded debian changelog" - -# generate pypi changelog - -sed -e "/--/d" -e "s/ \*/\*/" \ - -e "s/.*\([0-9].[0-9].[0-9]\).*/\1/" \ - -e '/[0-9].[0-9].[0-9]/ a\ ------' $mainlog | cat -s > $pypilog - -version=$(head -n 1 $pypilog) -inform "pypi changelog generated" - -# touch up version in setup.py file - -if [ -n $(grep version "$libdir/setup.py" &> /dev/null) ]; then - inform "touched up version in setup.py" - sed -i "s/'[0-9].[0-9].[0-9]'/'$version'/" "$libdir/setup.py" -else - warning "couldn't touch up version in setup, no match found" -fi - -# touch up version in lib or package siblings - -if [ -z "$libname" ]; then - cd "$libdir" - libname=$(grep "name" setup.py | tr -d "[:space:]" | cut -c 7- | rev | cut -c 3- | rev) - libname=$(echo "$libname" | tr "[A-Z]" "[a-z]") && cd "$debdir" - sibname+=( "$libname" ) -elif [ "$libname" != "package" ]; then - sibname+=( "$libname" ) -fi - -for sibling in ${sibname[@]}; do - if grep -e "__version__" "$libdir/$sibling.py" &> /dev/null; then - sed -i "s/__version__ = '[0-9].[0-9].[0-9]'/__version__ = '$version'/" "$libdir/$sibling.py" - inform "touched up version in $sibling.py" - elif grep -e "__version__" "$libdir/$sibling/__init__.py" &> /dev/null; then - sed -i "s/__version__ = '[0-9].[0-9].[0-9]'/__version__ = '$version'/" "$libdir/$sibling/__init__.py" - inform "touched up version in $sibling/__init__.py" - elif [ "$versionwarn" == "yes" ]; then - warning "couldn't touch up __version__ in $sibling, no match found" - fi -done - -exit 0 diff --git a/library/plasma/__init__.py b/plasma/__init__.py similarity index 100% rename from library/plasma/__init__.py rename to plasma/__init__.py index 3618909..76441c9 100644 --- a/library/plasma/__init__.py +++ b/plasma/__init__.py @@ -1,6 +1,6 @@ """Plasma multi device LED driver.""" -import sys import pathlib +import sys __version__ = '2.0.2' diff --git a/library/plasma/apa102.py b/plasma/apa102.py similarity index 99% rename from library/plasma/apa102.py rename to plasma/apa102.py index 5852534..560bd44 100644 --- a/library/plasma/apa102.py +++ b/plasma/apa102.py @@ -1,5 +1,6 @@ """Plasma support for APA102 style pixels.""" import time + from .core import Plasma diff --git a/library/plasma/core.py b/plasma/core.py similarity index 100% rename from library/plasma/core.py rename to plasma/core.py diff --git a/library/plasma/gpio.py b/plasma/gpio.py similarity index 100% rename from library/plasma/gpio.py rename to plasma/gpio.py diff --git a/library/plasma/legacy.py b/plasma/legacy.py similarity index 99% rename from library/plasma/legacy.py rename to plasma/legacy.py index af7650c..e0941fe 100644 --- a/library/plasma/legacy.py +++ b/plasma/legacy.py @@ -4,7 +4,6 @@ import RPi.GPIO as GPIO - __version__ = '0.0.1' DAT = 15 diff --git a/library/plasma/matrix.py b/plasma/matrix.py similarity index 99% rename from library/plasma/matrix.py rename to plasma/matrix.py index 83c495e..5450d2e 100644 --- a/library/plasma/matrix.py +++ b/plasma/matrix.py @@ -1,5 +1,6 @@ """Combine multiple LED strip types into a single logical strip.""" import pathlib + import yaml @@ -81,7 +82,7 @@ def get_device(self, index): if index in self._devices.keys(): return self._devices[index].get('device') - if type(index) == str: + if isinstance(index, str): for n, d in self._devices.items(): if d.get('type') == index: return d.get('device') diff --git a/library/plasma/serial.py b/plasma/serial.py similarity index 99% rename from library/plasma/serial.py rename to plasma/serial.py index 5339ae3..28b75ae 100644 --- a/library/plasma/serial.py +++ b/plasma/serial.py @@ -1,7 +1,8 @@ """Serial class for Plasma light devices over USB Serial/UART.""" -from .core import Plasma from serial import Serial +from .core import Plasma + class PlasmaSerial(Plasma): """Serial class for Plasma light devices over USB Serial/UART.""" diff --git a/library/plasma/usb.py b/plasma/usb.py similarity index 100% rename from library/plasma/usb.py rename to plasma/usb.py diff --git a/library/plasma/ws281x.py b/plasma/ws281x.py similarity index 100% rename from library/plasma/ws281x.py rename to plasma/ws281x.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e3e0279 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,130 @@ +[build-system] +requires = ["hatchling", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[project] +name = "plasmalights" +dynamic = ["version", "readme"] +description = "Python library for driving Pimoroni Plasma and other LED devices" +license = {file = "LICENSE"} +requires-python = ">= 3.9" +authors = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +maintainers = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +keywords = [ + "Pi", + "Raspberry", + "LED", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Hardware", +] +dependencies = [ + "pyyaml", + "pyserial", +] + +[project.urls] +GitHub = "https://www.github.com/pimoroni/plasma" +Homepage = "https://www.pimoroni.com" + +[dependency-groups] +dev = [ + "check-manifest", + "ruff", + "codespell", + "pre-commit", + "twine", + "hatch", + "hatch-fancy-pypi-readme", + "tox", + "pdoc", +] + +[tool.hatch.version] +path = "plasma/__init__.py" + +[tool.hatch.build] +include = [ + "plasma" +] + +[tool.hatch.build.targets.sdist] +include = [ + "*" +] +exclude = [ + ".*", + "dist", + "uv.lock" +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" +fragments = [ + { path = "README.md" }, + { text = "\n" }, + { path = "CHANGELOG.md" } +] + +[tool.ruff] +exclude = [ + '.tox', + '.egg', + '.git', + '__pycache__', + 'build', + 'dist' +] +line-length = 200 + +[tool.ruff.lint] +extend-select = ["I"] + +[tool.codespell] +ignore-words-list = "reenable" +skip = """ +./.tox,\ +./.venv,\ +./.egg,\ +./.git,\ +./__pycache__,\ +./build,\ +./dist\ +""" + +[tool.check-manifest] +ignore = [ + 'install.sh', + 'uninstall.sh', + 'Makefile', + 'tox.ini', + 'tests/*', + 'examples/*', + 'daemon/*', + 'documentation/*', + 'fx/*', + '.editorconfig', + '.pre-commit-config.yaml' +] + +[tool.pimoroni] +apt_packages = [] +configtxt = [] +commands = [] diff --git a/sphinx/_static/custom.css b/sphinx/_static/custom.css deleted file mode 100644 index 300e7a8..0000000 --- a/sphinx/_static/custom.css +++ /dev/null @@ -1,39 +0,0 @@ -.rst-content a, .rst-content a:focus { - color:#13c0d7; -} -.rst-content a:visited, .rst-content a:active { - color:#87319a; -} -.rst-content .highlighted { - background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAJElEQVQIW2P8//9/PSMjYyMDEmAEsdElwILoEnBBZAkUQZgEABMWE4Kzp1KUAAAAAElFTkSuQmCC),rgba(246,167,4,0.2); -} -.wy-side-nav-search { - background:#333333; -} -.wy-nav-side { - background:#444444; -} -.rst-content dl:not(.docutils) dt { - background:#e7fafd; - border-top:solid 3px #13c0d7; - color:rgba(0,0,0,0.5); -} -.rst-content .viewcode-link, .rst-content .viewcode-back { - color:#00b09b; -} -code.literal { - color:#e63c2e; -} -.rst-content #module-plasma { - margin-bottom:24px; -} -.rst-content #module-plasma dl:not(.docutils) dt { - border:none; - background:#f0f0f0; -} -.rst-content #module-plasma dl:not(.docutils) dd { - display:none; -} -.rst-content #module-plasma dl:not(.docutils) { - margin-bottom:0; -} diff --git a/sphinx/_templates/breadcrumbs.html b/sphinx/_templates/breadcrumbs.html deleted file mode 100644 index e69de29..0000000 diff --git a/sphinx/_templates/layout.html b/sphinx/_templates/layout.html deleted file mode 100644 index d89399b..0000000 --- a/sphinx/_templates/layout.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "!layout.html" %} -{% block extrahead %} - -{% endblock %} \ No newline at end of file diff --git a/sphinx/conf.py b/sphinx/conf.py deleted file mode 100644 index 24bb5d8..0000000 --- a/sphinx/conf.py +++ /dev/null @@ -1,389 +0,0 @@ -#-*- coding: utf-8 -*- - -import sys -import site - -import mock - - -PACKAGE_NAME = u"Plasma" -PACKAGE_HANDLE = "Plasma" -PACKAGE_MODULE = "plasma" - -# Prompte /usr/local/lib to the front of sys.path -#sys.path.insert(0,site.getsitepackages()[0]) - -import sphinx_rtd_theme - -sys.modules['RPi'] = mock.Mock() -sys.modules['RPi.GPIO'] = mock.Mock() - -sys.path.insert(0, '../library/') - -import plasma - - -from sphinx.ext import autodoc - - -class OutlineMethodDocumenter(autodoc.MethodDocumenter): - objtype = 'method' - - def add_content(self, more_content, no_docstring=False): - return - -class OutlineFunctionDocumenter(autodoc.FunctionDocumenter): - objtype = 'function' - - def add_content(self, more_content, no_docstring=False): - return - -class ModuleOutlineDocumenter(autodoc.ModuleDocumenter): - objtype = 'moduleoutline' - - def __init__(self, directive, name, indent=u''): - # Monkey path the Method and Function documenters - sphinx_app.add_autodocumenter(OutlineMethodDocumenter) - sphinx_app.add_autodocumenter(OutlineFunctionDocumenter) - autodoc.ModuleDocumenter.__init__(self, directive, name, indent) - - def __del__(self): - # Return the Method and Function documenters to normal - sphinx_app.add_autodocumenter(autodoc.MethodDocumenter) - sphinx_app.add_autodocumenter(autodoc.FunctionDocumenter) - - -def setup(app): - global sphinx_app - sphinx_app = app - app.add_autodocumenter(ModuleOutlineDocumenter) - - ModuleOutlineDocumenter.objtype = 'module' - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.viewcode', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -# -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = PACKAGE_NAME -copyright = u'2017, Pimoroni Ltd' -author = u'Phil Howard' - -# 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. -# -# The short X.Y version. -version = u'{}'.format(plasma.__version__) -# The full version, including alpha/beta/rc tags. -release = u'{}'.format(plasma.__version__) - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# -# today = '' -# -# Else, today_fmt is used as the format for a strftime call. -# -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' -#html_theme = 'alabaster' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -html_theme_options = { - 'collapse_navigation': False, - 'display_version': True -} - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = [ - '_themes', - sphinx_rtd_theme.get_html_theme_path() -] - -# The name for this set of Sphinx documents. -# " v documentation" by default. -# -# html_title = PACKAGE_NAME + u' v0.1.2' - -# A shorter title for the navigation bar. Default is the same as html_title. -# -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# -html_logo = 'shop-logo.png' - -# The name of an image file (relative to this directory) to use as a favicon of -# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# -html_favicon = 'favicon.png' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# -# html_extra_path = [] - -# If not None, a 'Last updated on:' timestamp is inserted at every page -# bottom, using the given strftime format. -# The empty string is equivalent to '%b %d, %Y'. -# -# html_last_updated_fmt = None - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# -# html_additional_pages = {} - -# If false, no module index is generated. -# -# html_domain_indices = True - -# If false, no index is generated. -# -html_use_index = False - -# If true, the index is split into individual pages for each letter. -# -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# -html_show_sourcelink = False - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# -html_show_sphinx = False - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' -# -# html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# 'ja' uses this config value. -# 'zh' user can custom change `jieba` dictionary path. -# -# html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -# -# html_search_scorer = 'scorer.js' - -# Output file base name for HTML help builder. -htmlhelp_basename = PACKAGE_HANDLE + 'doc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, PACKAGE_HANDLE + '.tex', PACKAGE_NAME + u' Documentation', - u'Phil Howard', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# -# latex_use_parts = False - -# If true, show page references after internal links. -# -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# -# latex_appendices = [] - -# It false, will not define \strong, \code, itleref, \crossref ... but only -# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added -# packages. -# -# latex_keep_old_macro_names = True - -# If false, no module index is generated. -# -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, PACKAGE_MODULE, PACKAGE_NAME + u' Documentation', - [author], 1) -] - -# If true, show URL addresses after external links. -# -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, PACKAGE_HANDLE, PACKAGE_NAME + u' Documentation', - author, PACKAGE_HANDLE, 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# -# texinfo_appendices = [] - -# If false, no module index is generated. -# -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# -# texinfo_no_detailmenu = False diff --git a/sphinx/favicon.png b/sphinx/favicon.png deleted file mode 100644 index 5ed0316c76a3c90cc6c8844cdd3c1385ad5cf09d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26049 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 diff --git a/sphinx/index.rst b/sphinx/index.rst deleted file mode 100644 index 3646ba2..0000000 --- a/sphinx/index.rst +++ /dev/null @@ -1,90 +0,0 @@ -.. role:: python(code) - :language: python - -.. toctree:: - :titlesonly: - :maxdepth: 0 - -Welcome -------- - -This documentation will guide you through the methods available in the Plasma Python library. - -* More information - https://shop.pimoroni.com/products/plasma -* Get the code - https://github.com/pimoroni/plasma -* Get started - https://learn.pimoroni.com/tutorial/sandyj/getting-started-with-plasma -* Get help - http://forums.pimoroni.com/c/support - -At A Glance ------------ - -.. automoduleoutline:: plasma - :members: - -Set A Single Pixel ------------------- - -The bread and butter of Plasma is setting pixels. Your Plasma LED string will always have -4x more pixels than buttons. You can set these to any one of around 16 million colours! - -The :python:`brightness` argument is completely optional. Omit it to keep the last -brightness value set for that particular pixel. - -.. automodule:: plasma - :noindex: - :members: set_pixel - -Set All Pixels --------------- - -Sometimes you need to set all the pixels to the same colour. This convinience method -does just that! - -.. automodule:: plasma - :noindex: - :members: set_all - -Show ----- - -None of your pixels will appear on Plasma until you :python:`show()` them. This method writes -all the pixel data out to your device. - -.. automodule:: plasma - :noindex: - :members: show - -Clear ------ - -Exactly the same as calling :python:`set_all(0,0,0)`, clear sets all the pixels to black. - -You must also call :python:`show()` if you want to turn Plasma off. - -.. automodule:: plasma - :noindex: - :members: clear - -Enable/Disable Clear On Exit ----------------------------- - -Sometimes you want a script that runs and quits, leaving a pattern up on Plasma - -.. automodule:: plasma - :noindex: - :members: set_clear_on_exit - -Get A Single Pixel ------------------- - -.. automodule:: plasma - :noindex: - :members: get_pixel - -Constants ---------- - -Plasma has 8 pixels. Simple. Use the constant :python:`plasma.NUM_PIXELS` when you're iterating over pixels, -so you can avoid a *magic number* in your code. - -:python:`plasma.NUM_PIXELS = 8` diff --git a/sphinx/shop-logo.png b/sphinx/shop-logo.png deleted file mode 100644 index 8fd0cda225cb6add7db0f7137919d92322c89f06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19652 zcmeI4c|6qJ+sD76O(@y2Z%ML@nX%1e-x~WG#y-Yivdl1pAt6b!Wi7flqJ$LwBB=f&vQSo-}Ahle`a2o?{%*8xvuklpL3n>`RD7Tx#B5#7%!FXX2p#d1H zP*ZDks5ctsE~cT*tV)7Y0{CKxC=rscj~@X}QWIN`3#VMKHG{-N*1HhB)x@;c5{lTF zn2YG(0x%+qa!RsjFc>VN43h&Zsz4!%G9nOpup&qv1_H~=%7fuxc{oH~ljcD%m6 zzfDab>IG2{d_($MCBZrbj{#X?2)MuiG)6Coax3D$yL%$mlRZ0~^E&4g* zKjUF^QACV7Wko8RLAMlFeiP1Z39zBuhjs?T^00~)}O=rq5LMp z{$>LI73Ht-f06%lj$kj0-#=8)*C^{9{#j4Jc@Tq90T?Y$%5wO3mHjpDUxm~OR6%RI zy$|;PU=|3dp#N|V{#6nEXLIm<8~Xp8gP-dc?S=C5#JH=2zAxSH4gYT1zE0*}YwG(} z@b?;2U3>PxDfLTPP^k5~0jq-k*7h^=*Zy{Eb?6hg-d`CaPx(+zD3s4P>91~TDU!DI zS2wlvr}Cg5mZ(nI;MNC9)%=TU?E(39E4QMYii%K0RSN)nRU&^XmA_|O7yjb-Cj2^AzZ|>PEAVS88?IxgCLsJwJsR6uB(bX>IgP9}a~q4LskQ30WC(s9w|L*=F8q5?wOq~oH^hssOGMFoVmNykN-50#gWiwX#Bla7lv zA1W^$7ZniNCLI@TK2%;hE-E0jO*$^xe5ky1TvR}4n{-^X`A~W3xTt{8HtD!%^P%$6 zaZv%GZPIbk=0oMB7&4A0wX&^Bq}V*dGxCdQBQUS=@m z1oYDggkI#{gjI3+4MY00hbdz@#exz%v1W2bb#7tPcP?0}OPv ztVsiJooX%lO?Kk;j!VASmMv2o>G3@#yWA}Ir+f{@Gmd!d$A<7A-TU5=KmgyG*$2qiEzez$Qm&g-N zLP_UaNKaBg2G|d;c;ty)71m{gXaR54Y98 c29iqXq3)xp_>0_3<6H>b_N?3w_6T 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 diff --git a/library/tests/conftest.py b/tests/conftest.py similarity index 99% rename from library/tests/conftest.py rename to tests/conftest.py index b648699..98c6adf 100644 --- a/library/tests/conftest.py +++ b/tests/conftest.py @@ -3,12 +3,12 @@ These allow the mocking of various Python modules that might otherwise have runtime side-effects. """ -import os +import pathlib import sys +import tempfile + import mock import pytest -import pathlib -import tempfile @pytest.fixture(scope='function', autouse=True) diff --git a/library/tests/test_apa102.py b/tests/test_apa102.py similarity index 99% rename from library/tests/test_apa102.py rename to tests/test_apa102.py index 2b8e18b..1f42421 100644 --- a/library/tests/test_apa102.py +++ b/tests/test_apa102.py @@ -20,7 +20,7 @@ def test_apa102_set_pixel(GPIO): from plasma.apa102 import PlasmaAPA102 plasma = PlasmaAPA102(10, gpio_data=10, gpio_clock=11) plasma.set_pixel(0, 255, 0, 255) - + assert plasma.get_pixel(0) == (255, 0, 255, 1.0) @@ -29,7 +29,7 @@ def test_apa102_set_all(GPIO): from plasma.apa102 import PlasmaAPA102 plasma = PlasmaAPA102(10, gpio_data=10, gpio_clock=11) plasma.set_all(255, 0, 255) - + assert plasma.get_pixel(0) == (255, 0, 255, 1.0) @@ -39,7 +39,7 @@ def test_apa102_clear(GPIO): plasma = PlasmaAPA102(10, gpio_data=10, gpio_clock=11) plasma.set_all(255, 0, 255) plasma.clear() - + assert plasma.get_pixel(0) == (0, 0, 0, 1.0) diff --git a/library/tests/test_auto.py b/tests/test_auto.py similarity index 99% rename from library/tests/test_auto.py rename to tests/test_auto.py index e4a118f..62c0d4e 100644 --- a/library/tests/test_auto.py +++ b/tests/test_auto.py @@ -27,11 +27,10 @@ def test_get_device_from_argv(argv_valid, GPIO): def test_get_device_from_config(config_file, GPIO, rpi_ws281x, serial): from plasma import auto - from plasma.matrix import PlasmaMatrix from plasma.apa102 import PlasmaAPA102 + from plasma.matrix import PlasmaMatrix plasma = auto("XXXXX", config_file) assert isinstance(plasma, PlasmaMatrix) assert isinstance(plasma.get_device("WALL"), PlasmaAPA102) - diff --git a/library/tests/test_fx.py b/tests/test_fx.py similarity index 88% rename from library/tests/test_fx.py rename to tests/test_fx.py index 8858dfe..46d0650 100644 --- a/library/tests/test_fx.py +++ b/tests/test_fx.py @@ -1,13 +1,13 @@ -import pytest def test_fx_cycle(argv, GPIO): """Test that set_sequence supports the output of a PlasmaFX Sequence""" - from plasma import auto - from plasma.apa102 import PlasmaAPA102 from plasmafx import Sequence from plasmafx.plugins import FXCycle + from plasma import auto + from plasma.apa102 import PlasmaAPA102 + sequence = Sequence(10) sequence.set_plugin(0, FXCycle()) @@ -15,4 +15,4 @@ def test_fx_cycle(argv, GPIO): plasma.set_sequence(sequence.get_pixels()) - assert isinstance(plasma, PlasmaAPA102) \ No newline at end of file + assert isinstance(plasma, PlasmaAPA102) diff --git a/library/tests/test_get_device.py b/tests/test_get_device.py similarity index 100% rename from library/tests/test_get_device.py rename to tests/test_get_device.py diff --git a/library/tests/test_legacy_gpio.py b/tests/test_legacy_gpio.py similarity index 100% rename from library/tests/test_legacy_gpio.py rename to tests/test_legacy_gpio.py diff --git a/library/tests/test_matrix.py b/tests/test_matrix.py similarity index 100% rename from library/tests/test_matrix.py rename to tests/test_matrix.py diff --git a/library/tests/test_serial.py b/tests/test_serial.py similarity index 98% rename from library/tests/test_serial.py rename to tests/test_serial.py index 9072646..e852391 100644 --- a/library/tests/test_serial.py +++ b/tests/test_serial.py @@ -1,5 +1,4 @@ """Test Plasma Serial initialisation.""" -import mock def test_serial_setup(serial): diff --git a/library/tests/test_setup.py b/tests/test_setup.py similarity index 100% rename from library/tests/test_setup.py rename to tests/test_setup.py diff --git a/library/tests/test_ws281x.py b/tests/test_ws281x.py similarity index 98% rename from library/tests/test_ws281x.py rename to tests/test_ws281x.py index 2ea16e7..2c4f6d2 100644 --- a/library/tests/test_ws281x.py +++ b/tests/test_ws281x.py @@ -1,5 +1,4 @@ """Test Plasma WS281X initialisation.""" -import mock def test_apa102_setup(rpi_ws281x): diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..f60646e --- /dev/null +++ b/tox.ini @@ -0,0 +1,27 @@ +[tox] +envlist = py,qa +skip_missing_interpreters = True +isolated_build = true +minversion = 4.0.0 + +[testenv] +commands = + coverage run -m pytest -v -r wsx + coverage report +deps = + mock + pytest>=3.1 + pytest-cov + build + {toxinidir}/fx/plasmafx + {toxinidir}/fx/plasmafx_plugin_cycle + +[testenv:qa] +commands = + check-manifest + python -m build --no-isolation + python -m build --no-isolation --outdir dist fx/plasmafx + python -m build --no-isolation --outdir dist fx/plasmafx_plugin_cycle + python -m twine check dist/* +dependency_groups = + dev diff --git a/uninstall.sh b/uninstall.sh new file mode 100755 index 0000000..3314b7f --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +FORCE=false +LIBRARY_NAME=$(grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}') +RESOURCES_DIR=$HOME/Pimoroni/$LIBRARY_NAME +PYTHON="python" + + +venv_check() { + PYTHON_BIN=$(which $PYTHON) + if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then + printf "This script should be run in a virtual Python environment.\n" + exit 1 + fi +} + +user_check() { + if [ "$(id -u)" -eq 0 ]; then + printf "Script should not be run as root. Try './uninstall.sh'\n" + exit 1 + fi +} + +confirm() { + if $FORCE; then + true + else + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi + fi +} + +prompt() { + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi +} + +success() { + echo -e "$(tput setaf 2)$1$(tput sgr0)" +} + +inform() { + echo -e "$(tput setaf 6)$1$(tput sgr0)" +} + +warning() { + echo -e "$(tput setaf 1)$1$(tput sgr0)" +} + +printf "%s Python Library: Uninstaller\n\n" "$LIBRARY_NAME" + +user_check +venv_check + +printf "Uninstalling for Python 3...\n" +$PYTHON -m pip uninstall "$LIBRARY_NAME" + +if [ -d "$RESOURCES_DIR" ]; then + if confirm "Would you like to delete $RESOURCES_DIR?"; then + rm -r "$RESOURCES_DIR" + fi +fi + +printf "Done!\n" From 127f90a36b0611da61cd1cb3113e1dac38129f3c Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 24 Aug 2026 12:45:49 +0100 Subject: [PATCH 2/3] Fold in PR #19 fixes missed by the repackage Point GitHub URLs at plasma-python; pimoroni/plasma is a different repo. daemon: install plasmalights to /etc/plasma/plasma with pip --target and add it to sys.path, so the daemon works under PEP 668. png stays on apt since it's imported before the path insert. Bump to 3.0.0 with a CHANGELOG entry for the repackage. --- CHANGELOG.md | 8 ++++++++ README.md | 6 +++--- daemon/install.sh | 6 ++++-- daemon/usr/bin/plasma | 1 + fx/plasmafx/pyproject.toml | 2 +- fx/plasmafx_plugin_cycle/pyproject.toml | 2 +- plasma/__init__.py | 2 +- pyproject.toml | 2 +- 8 files changed, 20 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a54e0e..91b86a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +3.0.0 +----- + +* Repackage to pyproject/hatchling +* Drop Python 3.7 and 3.8, add 3.12 and 3.13 +* Fix GitHub URL to plasma-python +* plasmafx: replace pkg_resources with importlib.metadata + 2.0.2 ----- diff --git a/README.md b/README.md index 23f42d6..433f9e6 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ Plasma is an LED/Light sequencing suite written to harmonise a variety of LED st Plasma also includes plasmad, a system daemon for sequencing light strips using PNG images to provide animation frames. -[![Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/plasma/test.yml?branch=master)](https://github.com/pimoroni/plasma/actions/workflows/test.yml) -[![Coverage Status](https://coveralls.io/repos/github/pimoroni/plasma/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/plasma?branch=master) +[![Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/plasma-python/test.yml?branch=master)](https://github.com/pimoroni/plasma-python/actions/workflows/test.yml) +[![Coverage Status](https://coveralls.io/repos/github/pimoroni/plasma-python/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/plasma-python?branch=master) [![PyPi Package](https://img.shields.io/pypi/v/plasmalights.svg)](https://pypi.python.org/pypi/plasmalights) [![Python Versions](https://img.shields.io/pypi/pyversions/plasmalights.svg)](https://pypi.python.org/pypi/plasmalights) @@ -48,7 +48,7 @@ python3 -m pip install plasmalights To install the Plasma daemon you should clone this repository, navigate to the "daemon" directory and run the installer: ``` -git clone https://github.com/pimoroni/plasma +git clone https://github.com/pimoroni/plasma-python cd plasma/daemon sudo ./install ``` diff --git a/daemon/install.sh b/daemon/install.sh index ce9ddda..a30b28f 100755 --- a/daemon/install.sh +++ b/daemon/install.sh @@ -11,14 +11,16 @@ if [ -d "/etc/plasma" ]; then fi printf "Installing requirements\n" -sudo apt install python3 python3-pip -sudo pip3 install pypng plasmalights +apt install python3 python3-pip python3-png printf "Installing plasma\n" mkdir /etc/plasma cp etc/plasma/* /etc/plasma cp usr/bin/* /usr/bin/ +printf "Installing python libraries into /etc/plasma/plasma\n" +python3 -m pip install --target /etc/plasma/plasma plasmalights + printf "Installing /etc/systemd/system/plasma.service\n" cp etc/systemd/system/plasma.service /etc/systemd/system/plasma.service systemctl reenable plasma.service diff --git a/daemon/usr/bin/plasma b/daemon/usr/bin/plasma index 4f10090..4363347 100755 --- a/daemon/usr/bin/plasma +++ b/daemon/usr/bin/plasma @@ -74,6 +74,7 @@ def main(): if args.daemonize: fork() + sys.path.insert(0, "/etc/plasma/plasma") from plasma import auto plasma = auto(f"GPIO:14:15:pixel_count={LIGHTS * 4}", CONFIG_FILE) diff --git a/fx/plasmafx/pyproject.toml b/fx/plasmafx/pyproject.toml index ab411c0..a3af328 100644 --- a/fx/plasmafx/pyproject.toml +++ b/fx/plasmafx/pyproject.toml @@ -39,7 +39,7 @@ classifiers = [ dependencies = [] [project.urls] -GitHub = "https://www.github.com/pimoroni/plasma" +GitHub = "https://www.github.com/pimoroni/plasma-python" Homepage = "https://www.pimoroni.com" [tool.hatch.build] diff --git a/fx/plasmafx_plugin_cycle/pyproject.toml b/fx/plasmafx_plugin_cycle/pyproject.toml index 34a96b0..b034798 100644 --- a/fx/plasmafx_plugin_cycle/pyproject.toml +++ b/fx/plasmafx_plugin_cycle/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ Cycle = "plasmafx_plugin_cycle:Cycle" [project.urls] -GitHub = "https://www.github.com/pimoroni/plasma" +GitHub = "https://www.github.com/pimoroni/plasma-python" Homepage = "https://www.pimoroni.com" [tool.hatch.build] diff --git a/plasma/__init__.py b/plasma/__init__.py index 76441c9..8b26a7c 100644 --- a/plasma/__init__.py +++ b/plasma/__init__.py @@ -2,7 +2,7 @@ import pathlib import sys -__version__ = '2.0.2' +__version__ = '3.0.0' def auto(default=None, descriptor=None): diff --git a/pyproject.toml b/pyproject.toml index e3e0279..8b67ef7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ dependencies = [ ] [project.urls] -GitHub = "https://www.github.com/pimoroni/plasma" +GitHub = "https://www.github.com/pimoroni/plasma-python" Homepage = "https://www.pimoroni.com" [dependency-groups] From 6c537ae193d53acab57861004f25ddb730cc3540 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 24 Aug 2026 12:58:27 +0100 Subject: [PATCH 3/3] Trusted publishing, version from the git tag plasmalights builds from the tag via uv-dynamic-versioning and publishes to PyPI over OIDC when a GitHub release is published. No API token in the repo; configure the publisher at pypi.org/manage/project/plasmalights/settings/publishing/ against publish.yml and the pypi environment. plasma.__version__ now reads back from package metadata. The fx packages keep static versions and the manual make deploy. Makefile inverts: CHANGELOG.md names the next version, make tag cuts it, the build reads it back from the tag. make check now catches a CHANGELOG whose top entry is tagged. fallback-version covers check-manifest and sdist rebuilds. publish.yml compares the built version against the tag. Actions bumped to checkout v7, setup-python v7, upload-artifact v7, setup-uv v10.0.1. Full history where the version matters. --- .github/workflows/build.yml | 8 ++++-- .github/workflows/install.yml | 6 ++-- .github/workflows/publish.yml | 54 +++++++++++++++++++++++++++++++++++ .github/workflows/qa.yml | 6 ++-- .github/workflows/test.yml | 4 +-- CHANGELOG.md | 3 +- Makefile | 35 ++++++++++++++--------- plasma/__init__.py | 6 +++- pyproject.toml | 10 +++++-- 9 files changed, 105 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e138a95..7f3817a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,10 +23,12 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v5 + uses: actions/checkout@v7 + with: + fetch-depth: 0 # the version comes from the git tag - name: Set up uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v10.0.1 with: enable-cache: true python-version: ${{ matrix.python }} @@ -36,7 +38,7 @@ jobs: uv run make build - name: Upload Packages - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{ env.RELEASE_FILE }} path: dist/ diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index d4da262..c34d0d6 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -21,10 +21,12 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v5 + uses: actions/checkout@v7 + with: + fetch-depth: 0 # the version comes from the git tag - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v6 + uses: actions/setup-python@v7 with: python-version: ${{ matrix.python }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..df20179 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,54 @@ +name: Publish + +# Trusted publishing, so PyPI trusts this workflow over OIDC and no API token is stored +# here. Configure it once at pypi.org/manage/project/plasmalights/settings/publishing/ with +# this repository, this filename and the `pypi` environment. +# +# Only plasmalights is published from here. The two fx packages are still built and +# uploaded by hand via `make deploy`. +on: + release: + types: [published] + workflow_dispatch: + +permissions: + contents: read + +jobs: + publish: + name: Build and publish to PyPI + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write # for trusted publishing + + steps: + - uses: actions/checkout@v7 + with: + # The tag is the version, so the build has to be able to see it. A checkout of a + # tag has it at HEAD anyway; this covers a dispatch from a branch as well. + fetch-depth: 0 + + # setup-uv publishes no floating major tag past v6, only exact versions + - uses: astral-sh/setup-uv@v10.0.1 + with: + version: "0.9.2" + + - run: uv build + + # A build that cannot see the tag produces 0.0.0.postN.devN+hash rather than failing, + # so what came out is checked against what was asked for. Skipped for a dispatch from + # a branch, where the ref is not a version at all. + - name: Check the built version is the tag + run: | + TAG="${GITHUB_REF_NAME#v}" + BUILT="$(ls dist/*.whl | head -1 | cut -d- -f2)" + echo "tag=$TAG built=$BUILT" + if [ "$GITHUB_REF_NAME" != "$TAG" ] && [ "$TAG" != "$BUILT" ]; then + echo "::error::built $BUILT from tag $TAG - check whether the checkout could" + echo "::error::see the tag at all" + exit 1 + fi + + - name: Publish + run: uv publish --trusted-publishing always diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index fb0e72c..c592ebf 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -18,10 +18,12 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v5 + uses: actions/checkout@v7 + with: + fetch-depth: 0 # the version comes from the git tag - name: Set up uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v10.0.1 with: enable-cache: true python-version: '3.11' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c775cbc..6907e6d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,10 +21,10 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v5 + uses: actions/checkout@v7 - name: Set up uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v10.0.1 with: enable-cache: true python-version: ${{ matrix.python }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 91b86a0..e9e163a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,13 @@ # Changelog -3.0.0 +3.0.1 ----- * Repackage to pyproject/hatchling * Drop Python 3.7 and 3.8, add 3.12 and 3.13 * Fix GitHub URL to plasma-python * plasmafx: replace pkg_resources with importlib.metadata +* Version now comes from the git tag; `plasma.__version__` reads package metadata 2.0.2 ----- diff --git a/Makefile b/Makefile index 15f193c..c79e7a8 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,13 @@ LIBRARY_NAME := $(shell hatch project metadata name 2> /dev/null) -LIBRARY_VERSION := $(shell hatch version 2> /dev/null) +LIBRARY_VERSION := $(shell grep -m 1 '^[0-9]' CHANGELOG.md) +BUILD_VERSION := $(shell hatch version 2> /dev/null) .PHONY: usage version install uninstall dev-deps check pre-commit qa pytest nopost tag build clean testdeploy deploy usage: ifdef LIBRARY_NAME @echo "Library: ${LIBRARY_NAME}" - @echo "Version: ${LIBRARY_VERSION}\n" + @echo "Next version: ${LIBRARY_VERSION} (from CHANGELOG.md)" + @echo "Build version: ${BUILD_VERSION} (from the git tag)\n" else @echo "WARNING: You should 'make dev-deps'\n" endif @@ -13,7 +15,7 @@ endif @echo "install: install the library locally from source" @echo "uninstall: uninstall the local library" @echo "dev-deps: install Python dev dependencies" - @echo "check: verify CHANGELOG.md has an entry for the current version" + @echo "check: verify CHANGELOG.md has an entry for an untagged version" @echo "qa: run package QA (check-manifest, build, twine)" @echo "pre-commit: run pre-commit hooks (lint, whitespace) on all files" @echo "pytest: run Python test fixtures" @@ -21,10 +23,13 @@ endif @echo "build: build Python distribution files" @echo "testdeploy: build and upload to test PyPi" @echo "deploy: build and upload to PyPi" - @echo "tag: tag the repository with the current version\n" + @echo "tag: tag the repository with the version from CHANGELOG.md" + @echo "\nplasmalights is published by .github/workflows/publish.yml when a\n" + @echo "GitHub release is published. deploy is for the fx packages.\n" version: - @hatch version + @echo "${LIBRARY_VERSION} (next, from CHANGELOG.md)" + @echo "${BUILD_VERSION} (build, from the git tag)" install: ./install.sh --unstable @@ -37,13 +42,15 @@ dev-deps: pre-commit install check: - @LIBRARY_VERSION=`hatch version | awk -F '.' '{print $$1"."$$2"."$$3}'`; \ - if grep -q "^$$LIBRARY_VERSION" CHANGELOG.md; then \ - echo "Changes found for version $$LIBRARY_VERSION."; \ - else \ - echo "Changes missing for version $$LIBRARY_VERSION! Please update CHANGELOG.md."; \ + @if [ -z "${LIBRARY_VERSION}" ]; then \ + echo "No version heading at the top of CHANGELOG.md."; \ exit 1; \ fi + @if git rev-parse -q --verify "refs/tags/v${LIBRARY_VERSION}" > /dev/null; then \ + echo "v${LIBRARY_VERSION} is already tagged. Add a CHANGELOG.md entry for the new version."; \ + exit 1; \ + fi + @echo "CHANGELOG.md is ready for v${LIBRARY_VERSION}." pre-commit: pre-commit run --all-files @@ -55,16 +62,16 @@ pytest: tox -e py nopost: - @POST_VERSION=`hatch version | awk -F '.' '{print $$4}'`; \ + @POST_VERSION=`echo "${BUILD_VERSION}" | awk -F '.' '{print $$4}'`; \ if [ -n "$$POST_VERSION" ]; then \ - echo "Found .$$POST_VERSION on library version; only use these for testpypi releases."; \ + echo "Found .$$POST_VERSION on the build version; tag a release first."; \ exit 1; \ fi -tag: version +tag: check git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}" -build: check +build: uv build uv build --out-dir dist fx/plasmafx uv build --out-dir dist fx/plasmafx_plugin_cycle diff --git a/plasma/__init__.py b/plasma/__init__.py index 8b26a7c..a61de9a 100644 --- a/plasma/__init__.py +++ b/plasma/__init__.py @@ -1,8 +1,12 @@ """Plasma multi device LED driver.""" import pathlib import sys +from importlib.metadata import PackageNotFoundError, version -__version__ = '3.0.0' +try: + __version__ = version("plasmalights") +except PackageNotFoundError: + __version__ = "0.0.0" def auto(default=None, descriptor=None): diff --git a/pyproject.toml b/pyproject.toml index 8b67ef7..73f9660 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["hatchling", "hatch-fancy-pypi-readme"] +requires = ["hatchling", "hatch-fancy-pypi-readme", "uv-dynamic-versioning"] build-backend = "hatchling.build" [project] @@ -53,12 +53,18 @@ dev = [ "twine", "hatch", "hatch-fancy-pypi-readme", + "uv-dynamic-versioning", "tox", "pdoc", ] [tool.hatch.version] -path = "plasma/__init__.py" +source = "uv-dynamic-versioning" + +# check-manifest and any build from an unpacked sdist have no git to read, and the +# publish workflow checks the built version against the tag to catch the fallback. +[tool.uv-dynamic-versioning] +fallback-version = "0.0.0" [tool.hatch.build] include = [