From 5a76cbdfd17eeb49651530e2b5b8c76de369a9c7 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Sun, 5 Jul 2026 21:27:34 +0100 Subject: [PATCH 1/6] CI: modernise packaging to uv-modernize method - hatchling dynamic optional-dependencies via hatch-requirements-txt - [dependency-groups] dev; ruff import sorting (drop isort/black) - pre-commit + editorconfig; uv-based CI and tox qa - requires-python >= 3.9, classifiers 3.9-3.13 - remove check.sh, requirements-dev.txt, .stickler.yml --- .editorconfig | 14 ++++++ .github/dependabot.yml | 14 ++++++ .github/workflows/build.yml | 19 ++++---- .github/workflows/install.yml | 43 ++++++++++++++++++ .github/workflows/qa.yml | 22 ++++----- .github/workflows/test.yml | 26 +++++------ .gitignore | 2 + .pre-commit-config.yaml | 22 +++++++++ .stickler.yml | 5 --- Makefile | 31 ++++++++----- check.sh | 84 ----------------------------------- pyproject.toml | 41 +++++++++++------ requirements-dev.txt | 9 ---- requirements-examples.txt | 0 tox.ini | 13 +----- 15 files changed, 179 insertions(+), 166 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/install.yml create mode 100644 .pre-commit-config.yaml delete mode 100644 .stickler.yml delete mode 100755 check.sh delete mode 100644 requirements-dev.txt create mode 100644 requirements-examples.txt 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/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4f8a87a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "pre-commit" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 07620e3..1b1d4ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,33 +6,34 @@ on: branches: - main +permissions: + contents: read + jobs: test: - name: Python ${{ matrix.python }} + name: Build (Python ${{ matrix.python }}) runs-on: ubuntu-latest strategy: matrix: - python: ['3.9', '3.10', '3.11'] + 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@v4 - - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v5 + - name: Set up uv + uses: astral-sh/setup-uv@v6 with: + enable-cache: true python-version: ${{ matrix.python }} - - name: Install Dependencies - run: | - make dev-deps - - name: Build Packages run: | - make build + uv run make build - name: Upload Packages uses: actions/upload-artifact@v4 diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml new file mode 100644 index 0000000..b8e5da8 --- /dev/null +++ b/.github/workflows/install.yml @@ -0,0 +1,43 @@ +name: Install Test + +on: + pull_request: + push: + branches: + - main + +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@v4 + + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v5 + 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 + sed -i "s|sudo raspi-config|raspi-config|g" pyproject.toml + touch raspi-config + chmod +x raspi-config + echo `pwd` >> $GITHUB_PATH + + - name: Run install.sh + run: | + ./install.sh --unstable --force diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index ac672a5..903a649 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -6,9 +6,12 @@ on: branches: - main +permissions: + contents: read + jobs: test: - name: linting & spelling + name: Linting & Spelling runs-on: ubuntu-latest env: TERM: xterm-256color @@ -17,23 +20,20 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 - - name: Set up Python '3,11' - uses: actions/setup-python@v5 + - name: Set up uv + uses: astral-sh/setup-uv@v6 with: + enable-cache: true python-version: '3.11' - - name: Install Dependencies - run: | - make dev-deps - - name: Run Quality Assurance run: | - make qa + uv run make qa - name: Run Code Checks run: | - make check + uv run make check - - name: Run Bash Code Checks + - name: Run Pre-Commit run: | - make shellcheck + uv run pre-commit run --all-files diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6f8cff7..8e4fb06 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,36 +6,36 @@ on: branches: - main +permissions: + contents: read + jobs: test: - name: Python ${{ matrix.python }} + name: Test (Python ${{ matrix.python }}) runs-on: ubuntu-latest + env: + TERM: xterm-256color strategy: matrix: - python: ['3.9', '3.10', '3.11'] + python: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v5 + - name: Set up uv + uses: astral-sh/setup-uv@v6 with: + enable-cache: true python-version: ${{ matrix.python }} - - name: Install Dependencies - run: | - make dev-deps - - name: Run Tests run: | - make pytest + uv run make pytest - name: Coverage if: ${{ matrix.python == '3.9' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - python -m pip install coveralls - coveralls --service=github - + uvx coveralls --service=github diff --git a/.gitignore b/.gitignore index fa45562..c673bb6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ library/debian/ .coverage .pytest_cache .tox +.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/.stickler.yml b/.stickler.yml deleted file mode 100644 index 2466815..0000000 --- a/.stickler.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -linters: - flake8: - python: 3 - max-line-length: 160 diff --git a/Makefile b/Makefile index 56cf0df..29700a2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ LIBRARY_NAME := $(shell hatch project metadata name 2> /dev/null) LIBRARY_VERSION := $(shell hatch version 2> /dev/null) -.PHONY: usage install uninstall check pytest qa build-deps check tag wheel sdist clean dist testdeploy deploy +.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}" @@ -13,8 +13,9 @@ endif @echo "install: install the library locally from source" @echo "uninstall: uninstall the local library" @echo "dev-deps: install Python dev dependencies" - @echo "check: perform basic integrity checks on the codebase" - @echo "qa: run linting and package QA" + @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" @@ -32,14 +33,20 @@ uninstall: ./uninstall.sh dev-deps: - python3 -m pip install -r requirements-dev.txt - sudo apt install dos2unix shellcheck + python3 -m pip install --group dev + pre-commit install check: - @bash check.sh + @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 -shellcheck: - shellcheck *.sh +pre-commit: + pre-commit run --all-files qa: tox -e qa @@ -48,13 +55,17 @@ pytest: tox -e py nopost: - @bash check.sh --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 - @hatch build + uv build clean: -rm -r dist diff --git a/check.sh b/check.sh deleted file mode 100755 index 38dfc3a..0000000 --- a/check.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash - -# This script handles some basic QA checks on the source - -NOPOST=$1 -LIBRARY_NAME=$(hatch project metadata name) -LIBRARY_VERSION=$(hatch version | awk -F "." '{print $1"."$2"."$3}') -POST_VERSION=$(hatch version | awk -F "." '{print substr($4,0,length($4))}') -TERM=${TERM:="xterm-256color"} - -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)" -} - -while [[ $# -gt 0 ]]; do - K="$1" - case $K in - -p|--nopost) - NOPOST=true - shift - ;; - *) - if [[ $1 == -* ]]; then - printf "Unrecognised option: %s\n" "$1"; - exit 1 - fi - POSITIONAL_ARGS+=("$1") - shift - esac -done - -inform "Checking $LIBRARY_NAME $LIBRARY_VERSION\n" - -inform "Checking for trailing whitespace..." -if grep -IUrn --color "[[:blank:]]$" --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO; then - warning "Trailing whitespace found!" - exit 1 -else - success "No trailing whitespace found." -fi -printf "\n" - -inform "Checking for DOS line-endings..." -if grep -lIUrn --color $'\r' --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile; then - warning "DOS line-endings found!" - exit 1 -else - success "No DOS line-endings found." -fi -printf "\n" - -inform "Checking CHANGELOG.md..." -if ! grep "^${LIBRARY_VERSION}" CHANGELOG.md > /dev/null 2>&1; then - warning "Changes missing for version ${LIBRARY_VERSION}! Please update CHANGELOG.md." - exit 1 -else - success "Changes found for version ${LIBRARY_VERSION}." -fi -printf "\n" - -inform "Checking for git tag ${LIBRARY_VERSION}..." -if ! git tag -l | grep -E "${LIBRARY_VERSION}$"; then - warning "Missing git tag for version ${LIBRARY_VERSION}" -fi -printf "\n" - -if [[ $NOPOST ]]; then - inform "Checking for .postN on library version..." - if [[ "$POST_VERSION" != "" ]]; then - warning "Found .$POST_VERSION on library version." - inform "Please only use these for testpypi releases." - exit 1 - else - success "OK" - fi -fi diff --git a/pyproject.toml b/pyproject.toml index 49bf9aa..c6db1ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,13 @@ [build-system] -requires = ["hatchling", "hatch-fancy-pypi-readme"] +requires = ["hatchling", "hatch-fancy-pypi-readme", "hatch-requirements-txt"] build-backend = "hatchling.build" [project] name = "as7262" -dynamic = ["version", "readme"] +dynamic = ["version", "readme", "optional-dependencies"] description = "Library for the AS7262 spectral sensor" license = {file = "LICENSE"} -requires-python = ">= 3.7" +requires-python = ">= 3.9" authors = [ { name = "Philip Howard", email = "phil@pimoroni.com" }, ] @@ -24,11 +24,11 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "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", @@ -38,19 +38,33 @@ dependencies = [ "i2cdevice>=1.0.0" ] +[tool.hatch.metadata.hooks.requirements_txt.optional-dependencies] +example-depends = ["requirements-examples.txt"] + [project.urls] GitHub = "https://www.github.com/pimoroni/as7262-python" Homepage = "https://www.pimoroni.com" +[dependency-groups] +dev = [ + "check-manifest", + "ruff", + "codespell", + "pre-commit", + "twine", + "hatch", + "hatch-fancy-pypi-readme", + "hatch-requirements-txt", + "tox", + "pdoc", +] + [tool.hatch.version] path = "as7262/__init__.py" [tool.hatch.build] include = [ "as7262", - "README.md", - "CHANGELOG.md", - "LICENSE" ] [tool.hatch.build.targets.sdist] @@ -81,6 +95,9 @@ exclude = [ ] line-length = 200 +[tool.ruff.lint] +extend-select = ["I"] + [tool.codespell] skip = """ ./.tox,\ @@ -91,14 +108,9 @@ skip = """ ./dist.\ """ -[tool.isort] -line_length = 200 - [tool.check-manifest] ignore = [ - '.stickler.yml', 'boilerplate.md', - 'check.sh', 'install.sh', 'uninstall.sh', 'Makefile', @@ -106,7 +118,8 @@ ignore = [ 'tests/*', 'examples/*', '.coveragerc', - 'requirements-dev.txt' + '.editorconfig', + '.pre-commit-config.yaml', ] [tool.pimoroni] diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 525b042..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,9 +0,0 @@ -check-manifest -ruff -codespell -isort -twine -hatch -hatch-fancy-pypi-readme -tox -pdoc diff --git a/requirements-examples.txt b/requirements-examples.txt new file mode 100644 index 0000000..e69de29 diff --git a/tox.ini b/tox.ini index 4726cef..6ad3c71 100644 --- a/tox.ini +++ b/tox.ini @@ -19,16 +19,7 @@ commands = check-manifest python -m build --no-isolation python -m twine check dist/* - isort --check . ruff check . codespell . -deps = - check-manifest - ruff - codespell - isort - twine - build - hatch - hatch-fancy-pypi-readme - +dependency_groups = + dev From e0f2bc6f453f5ba125dd2a8cedc064e657c84e25 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 6 Jul 2026 13:16:16 +0100 Subject: [PATCH 2/6] style: satisfy pre-commit hooks (end-of-file-fixer) --- README.md | 2 +- examples/bargraph.py | 1 - examples/spectrum.py | 1 - tests/conftest.py | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b4c3ef2..18739e2 100644 --- a/README.md +++ b/README.md @@ -48,4 +48,4 @@ On a Raspberry Pi you can do that like so: ``` sudo raspi-config nonint do_i2c 0 -``` \ No newline at end of file +``` diff --git a/examples/bargraph.py b/examples/bargraph.py index d02c9c0..cbbd58e 100644 --- a/examples/bargraph.py +++ b/examples/bargraph.py @@ -69,4 +69,3 @@ except KeyboardInterrupt: as7262.set_measurement_mode(3) as7262.set_illumination_led(0) - diff --git a/examples/spectrum.py b/examples/spectrum.py index ff6b5fe..f56acb9 100644 --- a/examples/spectrum.py +++ b/examples/spectrum.py @@ -21,4 +21,3 @@ except KeyboardInterrupt: as7262.set_measurement_mode(3) as7262.set_illumination_led(0) - diff --git a/tests/conftest.py b/tests/conftest.py index 6f4f838..66e134b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,4 +19,4 @@ def smbus(): def AS7262(): from as7262 import AS7262 yield AS7262 - del sys.modules['as7262'] \ No newline at end of file + del sys.modules['as7262'] From 559cf998eff1fbb37c3d823f28518dcf65f333e5 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 6 Jul 2026 13:45:44 +0100 Subject: [PATCH 3/6] CI: sync install.sh/uninstall.sh to boilerplate (uniform across repos) --- install.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/install.sh b/install.sh index 3db90bc..61f1a4a 100755 --- a/install.sh +++ b/install.sh @@ -166,6 +166,12 @@ function pip_pkg_install { 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 @@ -335,6 +341,15 @@ 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 From 48061c585a26bec1f7c3a6877cd05e5848bdd511 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 26 Aug 2026 19:50:18 +0100 Subject: [PATCH 4/6] Widen ruff ruleset and fix findings --- as7262/__init__.py | 14 +++++--------- as7262/__main__.py | 2 +- examples/bargraph.py | 30 +++++++++--------------------- pyproject.toml | 4 ++-- tests/conftest.py | 2 +- tests/test_features.py | 2 +- tests/test_setup.py | 2 +- tox.ini | 1 - 8 files changed, 20 insertions(+), 37 deletions(-) diff --git a/as7262/__init__.py b/as7262/__init__.py index c7a031f..748480b 100644 --- a/as7262/__init__.py +++ b/as7262/__init__.py @@ -8,7 +8,7 @@ __version__ = '1.0.0' -class as7262VirtualRegisterBus(): +class as7262VirtualRegisterBus: """AS7262 Virtual Register. This class implements the wacky virtual register setup @@ -68,7 +68,7 @@ def _decode(self, value): major_version = (value & 0x00F0) >> 4 minor_version = ((value & 0x000F) << 2) | ((value & 0b1100000000000000) >> 14) sub_version = (value & 0b0011111100000000) >> 8 - return '{}.{}.{}'.format(major_version, minor_version, sub_version) + return f'{major_version}.{minor_version}.{sub_version}' class FloatAdapter(Adapter): @@ -92,7 +92,7 @@ def _encode(self, value): class CalibratedValues: """Store the 6 band spectral values.""" - def __init__(self, red, orange, yellow, green, blue, violet): # noqa D107 + def __init__(self, red, orange, yellow, green, blue, violet): self.red = red self.orange = orange self.yellow = yellow @@ -100,7 +100,7 @@ def __init__(self, red, orange, yellow, green, blue, violet): # noqa D107 self.blue = blue self.violet = violet - def __iter__(self): # noqa D107 + def __iter__(self): for colour in ['red', 'orange', 'yellow', 'green', 'blue', 'violet']: yield getattr(self, colour) @@ -165,11 +165,7 @@ def __init__(self, i2c_dev=None): if isinstance(field.adapter, LookupAdapter): for key in field.adapter.lookup_table: value = field.adapter.lookup_table[key] - name = 'AS7262_{register}_{field}_{key}'.format( - register=register.name, - field=field.name, - key=key - ).upper() + name = f'AS7262_{register.name}_{field.name}_{key}'.upper() locals()[name] = key self.soft_reset() diff --git a/as7262/__main__.py b/as7262/__main__.py index 9a3c31e..b919059 100644 --- a/as7262/__main__.py +++ b/as7262/__main__.py @@ -6,7 +6,7 @@ hw_type, hw_version, fw_version = as7262.get_version() - print('{}'.format(fw_version)) + print(f'{fw_version}') as7262.set_gain(64) diff --git a/examples/bargraph.py b/examples/bargraph.py index cbbd58e..2f77f9c 100644 --- a/examples/bargraph.py +++ b/examples/bargraph.py @@ -6,7 +6,7 @@ as7262 = AS7262() -BAR_CHAR = u'\u2588' +BAR_CHAR = '\u2588' ANSI_COLOR_RED = '\x1b[31m' ANSI_COLOR_GREEN = '\x1b[32m' @@ -22,11 +22,6 @@ as7262.set_measurement_mode(2) as7262.set_illumination_led(1) -try: - input = raw_input -except NameError: - pass - input("Setting white point baseline.\n\nHold a white sheet of paper ~5cm in front of the sensor and press a key...\n") baseline = as7262.get_calibrated_values() time.sleep(1) @@ -42,24 +37,17 @@ red, orange, yellow, green, blue, violet = [(BAR_CHAR * value) + (' ' * (BAR_WIDTH - value)) for value in values] sys.stdout.write('\x1b[0;1H') - bargraph =u""" Spectrometer Bar Graph + bargraph =f""" Spectrometer Bar Graph --------------------------------- -|Red: {}{}\x1b[0m| -|Orange: {}{}\x1b[0m| -|Yellow: {}{}\x1b[0m| -|Green: {}{}\x1b[0m| -|Blue: {}{}\x1b[0m| -|Violet: {}{}\x1b[0m| +|Red: {ANSI_COLOR_RED}{red}\x1b[0m| +|Orange: {ANSI_COLOR_YELLOW}{orange}\x1b[0m| +|Yellow: {ANSI_COLOR_YELLOW}{yellow}\x1b[0m| +|Green: {ANSI_COLOR_GREEN}{green}\x1b[0m| +|Blue: {ANSI_COLOR_BLUE}{blue}\x1b[0m| +|Violet: {ANSI_COLOR_MAGENTA}{violet}\x1b[0m| --------------------------------- -""".format( - ANSI_COLOR_RED, red, - ANSI_COLOR_YELLOW, orange, - ANSI_COLOR_YELLOW, yellow, - ANSI_COLOR_GREEN, green, - ANSI_COLOR_BLUE, blue, - ANSI_COLOR_MAGENTA, violet -) +""" bargraph = "\n".join(line.ljust(tcols, " ") for line in bargraph.split("\n")) sys.stdout.write(bargraph) diff --git a/pyproject.toml b/pyproject.toml index c6db1ad..8bb4db9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ Homepage = "https://www.pimoroni.com" [dependency-groups] dev = [ "check-manifest", - "ruff", + "ruff==0.15.20", "codespell", "pre-commit", "twine", @@ -96,7 +96,7 @@ exclude = [ line-length = 200 [tool.ruff.lint] -extend-select = ["I"] +extend-select = ["I", "UP", "SIM", "PIE", "C4", "B", "RUF", "PERF", "FURB", "PLE"] [tool.codespell] skip = """ diff --git a/tests/conftest.py b/tests/conftest.py index 66e134b..ff14200 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import sys +from unittest import mock -import mock import pytest from .tools import SMBusFakeAS7262 diff --git a/tests/test_features.py b/tests/test_features.py index 6fbe8bb..4171d9f 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -1,4 +1,4 @@ -# noqa D100 + from .tools import CALIBRATED_VALUES diff --git a/tests/test_setup.py b/tests/test_setup.py index b233010..740b319 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -1,4 +1,4 @@ -# noqa D100 + def test_fw_info(smbus, AS7262): diff --git a/tox.ini b/tox.ini index 6ad3c71..c287b7b 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ commands = coverage run -m pytest -v -r wsx coverage report deps = - mock pytest>=3.1 pytest-cov build From cef8cbb7d5cb2c7b3ae6529e90e9d9a9af8071a4 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 18 Sep 2026 15:46:42 +0100 Subject: [PATCH 5/6] Adopt tag-derived versioning and PyPI trusted publishing --- .github/workflows/build.yml | 2 ++ .github/workflows/install.yml | 2 ++ .github/workflows/publish.yml | 42 +++++++++++++++++++++++++++++++++++ .github/workflows/qa.yml | 2 ++ Makefile | 33 +++++++++++++++------------ as7262/__init__.py | 6 ++++- pyproject.toml | 11 ++++++--- 7 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b1d4ea..b187c34 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up uv uses: astral-sh/setup-uv@v6 diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index b8e5da8..026c94b 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -22,6 +22,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v5 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..fb4bd8a --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,42 @@ +name: Publish + +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 + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + + - name: Build Packages + run: uv build + + - name: Check version matches 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" + exit 1 + fi + + - name: Publish + run: uv publish --trusted-publishing always diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 903a649..e2c04bf 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -19,6 +19,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up uv uses: astral-sh/setup-uv@v6 diff --git a/Makefile b/Makefile index 29700a2..d82edcf 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,11 @@ 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\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 +40,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 +60,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 clean: diff --git a/as7262/__init__.py b/as7262/__init__.py index 748480b..e05f6f1 100644 --- a/as7262/__init__.py +++ b/as7262/__init__.py @@ -1,11 +1,15 @@ """Library for the AS7262 Visible Light Spectral Sensor.""" import struct import time +from importlib.metadata import PackageNotFoundError, version from i2cdevice import BitField, Device, Register, _int_to_bytes from i2cdevice.adapter import Adapter, LookupAdapter -__version__ = '1.0.0' +try: + __version__ = version("as7262") +except PackageNotFoundError: + __version__ = "0.0.0" class as7262VirtualRegisterBus: diff --git a/pyproject.toml b/pyproject.toml index 8bb4db9..4876ebd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["hatchling", "hatch-fancy-pypi-readme", "hatch-requirements-txt"] +requires = ["hatchling", "hatch-fancy-pypi-readme", "hatch-requirements-txt", "uv-dynamic-versioning"] build-backend = "hatchling.build" [project] @@ -55,12 +55,16 @@ dev = [ "hatch", "hatch-fancy-pypi-readme", "hatch-requirements-txt", + "uv-dynamic-versioning", "tox", "pdoc", ] [tool.hatch.version] -path = "as7262/__init__.py" +source = "uv-dynamic-versioning" + +[tool.uv-dynamic-versioning] +fallback-version = "0.0.0" [tool.hatch.build] include = [ @@ -73,7 +77,8 @@ include = [ ] exclude = [ ".*", - "dist" + "dist", + "uv.lock" ] [tool.hatch.metadata.hooks.fancy-pypi-readme] From 0a642d146f15edacb3892eb7e236e347f98ec7bf Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 18 Sep 2026 15:52:07 +0100 Subject: [PATCH 6/6] CHANGELOG: add 2.0.0 entry --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3e4ff5..e4fbc52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +2.0.0 +----- + +* Enhancement: Repackage to the uv/hatchling method, with PyPI trusted publishing +* Enhancement: Version is derived from the git tag, __version__ from package metadata +* Python 3.9 or later, 3.7 and 3.8 support dropped + 1.0.0 -----