From 9d5b47885c6f1ac89818dd3329c23b58a800852d Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 28 Apr 2026 22:21:30 -0400 Subject: [PATCH 1/9] Migrate to prek --- .pre-commit-config.yaml | 46 ------------------------- prek.toml | 74 +++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 75 insertions(+), 47 deletions(-) delete mode 100644 .pre-commit-config.yaml create mode 100644 prek.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index ce00c90..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,46 +0,0 @@ -repos: - - repo: https://github.com/codespell-project/codespell - rev: v2.4.1 - hooks: - - id: codespell - additional_dependencies: [tomli] - args: ["--toml", "pyproject.toml"] - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.19.0 - hooks: - - id: mypy - additional_dependencies: [types-pywin32] - - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.7 - hooks: - - id: ruff-check - args: [--fix] - - id: ruff-format - - - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.8.3 - hooks: - - id: prettier - files: ^tests/data/pyodide/.*\.(ts|json)$ - - - repo: local - hooks: - - id: cargo-fmt - name: Cargo fmt - entry: cargo fmt --all - language: system - pass_filenames: false - - - id: cargo-check - name: Cargo check - entry: cargo check --workspace - language: system - pass_filenames: false - - - id: rust-clippy - name: Cargo clippy - entry: cargo clippy --all-targets --all-features -- -Dclippy::all -Dclippy::nursery - language: system - pass_filenames: false diff --git a/prek.toml b/prek.toml new file mode 100644 index 0000000..e2094f7 --- /dev/null +++ b/prek.toml @@ -0,0 +1,74 @@ +# Configuration file for `prek`, a git hook framework written in Rust. +# See https://prek.j178.dev for more information. +#:schema https://www.schemastore.org/prek.json + +[[repos]] +repo = "https://github.com/codespell-project/codespell" +rev = "v2.4.1" +hooks = [ + { + id = "codespell", + additional_dependencies = ["tomli"], + args = [ + "--toml", + "pyproject.toml" + ] + } +] + +[[repos]] +repo = "https://github.com/pre-commit/mirrors-mypy" +rev = "v1.19.0" +hooks = [ + { + id = "mypy", + additional_dependencies = ["types-pywin32"] + } +] + +[[repos]] +repo = "https://github.com/astral-sh/ruff-pre-commit" +rev = "v0.14.7" +hooks = [ + { + id = "ruff-check", + args = ["--fix"] + }, + { id = "ruff-format" } +] + +[[repos]] +repo = "https://github.com/rbubley/mirrors-prettier" +rev = "v3.8.3" +hooks = [ + { + id = "prettier", + files = '^tests/data/pyodide/.*\.(ts|json)$' + } +] + +[[repos]] +repo = "local" +hooks = [ + { + id = "cargo-fmt", + name = "Cargo fmt", + entry = "cargo fmt --all", + language = "system", + pass_filenames = false + }, + { + id = "cargo-check", + name = "Cargo check", + entry = "cargo check --workspace", + language = "system", + pass_filenames = false + }, + { + id = "rust-clippy", + name = "Cargo clippy", + entry = "cargo clippy --all-targets --all-features -- -Dclippy::all -Dclippy::nursery", + language = "system", + pass_filenames = false + } +] diff --git a/pyproject.toml b/pyproject.toml index 9b3d791..56c1e8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ esphome = [ dev = [ "ruff", "pytest", - "pre-commit", + "prek", "pytest-asyncio", "pytest-timeout", "pytest-xdist", From 7a8f3296aa90b28ea0251f4eac4f730f390e7766 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 28 Apr 2026 22:33:10 -0400 Subject: [PATCH 2/9] Set up zizimor --- .github/workflows/ci.yml | 30 +++++++++++++++--------------- prek.toml | 11 +++++++++++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31dadea..4328f1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: curl -LsSf https://astral.sh/uv/install.sh | sh uv venv venv . venv/bin/activate - uv pip install -U pip setuptools pre-commit setuptools-rust + uv pip install -U pip setuptools setuptools-rust prek PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 uv pip install -e .[dev] - name: Cache base Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' @@ -51,8 +51,8 @@ jobs: path: venv key: ${{ steps.cache-venv.outputs.cache-primary-key }} - pre-commit: - name: Run pre-commit + prek: + name: Run prek runs-on: ubuntu-latest needs: prepare-base steps: @@ -76,28 +76,28 @@ jobs: hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libudev-dev - - name: Restore pre-commit environment from cache - id: cache-precommit + - name: Restore prek environment from cache + id: cache-prek uses: actions/cache/restore@v4 with: - path: ~/.cache/pre-commit + path: ~/.cache/prek key: | - 1-${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} - - name: Install pre-commit dependencies - if: steps.cache-precommit.outputs.cache-hit != 'true' + 1-${{ runner.os }}-prek-${{ hashFiles('prek.toml') }} + - name: Install prek hooks + if: steps.cache-prek.outputs.cache-hit != 'true' run: | . venv/bin/activate - pre-commit install-hooks - - name: Cache pre-commit environment - if: steps.cache-precommit.outputs.cache-hit != 'true' + prek install-hooks + - name: Cache prek environment + if: steps.cache-prek.outputs.cache-hit != 'true' uses: actions/cache/save@v4 with: - path: ~/.cache/pre-commit - key: ${{ steps.cache-precommit.outputs.cache-primary-key }} + path: ~/.cache/prek + key: ${{ steps.cache-prek.outputs.cache-primary-key }} - name: Lint and static analysis run: | . venv/bin/activate - pre-commit run --show-diff-on-failure --color=always --all-files + prek run --show-diff-on-failure --color=always --all-files build-ser2net: name: Build ser2net diff --git a/prek.toml b/prek.toml index e2094f7..74c9bfb 100644 --- a/prek.toml +++ b/prek.toml @@ -72,3 +72,14 @@ hooks = [ pass_filenames = false } ] + + +[[repos]] +repo = "https://github.com/zizmorcore/zizmor-pre-commit" +rev = "v1.24.1" +hooks = [ + { + id = "zizmor", + args = ["--pedantic"] + } +] \ No newline at end of file From 9f66773b178da1247be0664c3576f21297f12a79 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 28 Apr 2026 22:35:04 -0400 Subject: [PATCH 3/9] Apply zizmor fixes --- .github/workflows/ci.yml | 129 +++++++++++++++----------- .github/workflows/docs.yml | 28 ++++-- .github/workflows/publish-to-pypi.yml | 47 ++++++---- 3 files changed, 126 insertions(+), 78 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4328f1e..77620cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,13 @@ on: push: pull_request: ~ +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + jobs: prepare-base: name: Prepare base dependencies @@ -13,17 +20,19 @@ jobs: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} id: python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: ${{ matrix.python-version }} allow-prereleases: true check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: venv key: >- @@ -35,7 +44,7 @@ jobs: run: sudo apt-get update && sudo apt-get install -y libudev-dev - name: Set up Rust if: steps.cache-venv.outputs.cache-hit != 'true' - uses: dtolnay/rust-toolchain@stable + run: rustup default stable && rustup update stable - name: Create Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' run: | @@ -46,7 +55,7 @@ jobs: PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 uv pip install -e .[dev] - name: Cache base Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: venv key: ${{ steps.cache-venv.outputs.cache-primary-key }} @@ -57,16 +66,18 @@ jobs: needs: prepare-base steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Python 3.10 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 id: python with: python-version: "3.10" check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: fail-on-cache-miss: true path: venv @@ -78,7 +89,7 @@ jobs: run: sudo apt-get update && sudo apt-get install -y libudev-dev - name: Restore prek environment from cache id: cache-prek - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.cache/prek key: | @@ -87,10 +98,10 @@ jobs: if: steps.cache-prek.outputs.cache-hit != 'true' run: | . venv/bin/activate - prek install-hooks + prek prepare-hooks - name: Cache prek environment if: steps.cache-prek.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.cache/prek key: ${{ steps.cache-prek.outputs.cache-primary-key }} @@ -108,7 +119,7 @@ jobs: steps: - name: Restore cached ser2net binary id: cache-ser2net - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ser2net key: ser2net-${{ env.SER2NET_VERSION }}-gensio-${{ env.GENSIO_VERSION }} @@ -160,7 +171,7 @@ jobs: $GITHUB_WORKSPACE/ser2net -v - name: Cache ser2net binary if: steps.cache-ser2net.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ser2net key: ${{ steps.cache-ser2net.outputs.cache-primary-key }} @@ -170,10 +181,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Restore cached ESPHome binary id: cache-esphome - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: tests/esphome/.esphome/build/serialx-host-daemon/.pioenvs/serialx-host-daemon/program key: >- @@ -181,7 +194,7 @@ jobs: hashFiles('tests/esphome/host_daemon.yaml', 'tests/esphome/external_components/**') }} - name: Set up Python 3.13 if: steps.cache-esphome.outputs.cache-hit != 'true' - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: "3.13" - name: Compile ESPHome host daemon @@ -196,7 +209,7 @@ jobs: esphome compile tests/esphome/host_daemon.yaml - name: Cache ESPHome binary if: steps.cache-esphome.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: tests/esphome/.esphome/build/serialx-host-daemon/.pioenvs/serialx-host-daemon/program key: ${{ steps.cache-esphome.outputs.cache-primary-key }} @@ -212,9 +225,11 @@ jobs: name: Run tests Python ${{ matrix.python-version }} (Linux) steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 id: python with: python-version: ${{ matrix.python-version }} @@ -222,7 +237,7 @@ jobs: check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: fail-on-cache-miss: true path: venv @@ -231,14 +246,14 @@ jobs: steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} - name: Restore cached ESPHome binary - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: tests/esphome/.esphome/build/serialx-host-daemon/.pioenvs/serialx-host-daemon/program key: >- 2-esphome-daemon-${{ hashFiles('tests/esphome/host_daemon.yaml', 'tests/esphome/external_components/**') }} - name: Restore cached ser2net binary - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ser2net key: ser2net-4.6.7-gensio-3.0.2 @@ -251,7 +266,7 @@ jobs: run: echo "version=$(uname -r)" >> "$GITHUB_OUTPUT" - name: Restore cached tty0tty module id: cache-tty0tty - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: tty0tty.ko key: tty0tty-${{ env.TTY0TTY_COMMIT }}-${{ steps.kernel.outputs.version }} @@ -297,7 +312,7 @@ jobs: --adapter-pair /dev/tnt0,/dev/tnt1,no-rts-cts,no-dtr-dsr,no-num-unwritten-bytes,no-reset-write-buffer,no-write-timeout,no-buffer-control \ tests - name: Upload coverage artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: coverage-${{ matrix.python-version }} include-hidden-files: true @@ -308,16 +323,18 @@ jobs: name: Run tests Python 3.13 (macOS) steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Python id: python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: "3.13" check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: venv key: >- @@ -326,7 +343,7 @@ jobs: hashFiles('pyproject.toml') }} - name: Restore cached Rust extension id: cache-ext - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: serialx/_serialx_rust* key: >- @@ -335,7 +352,7 @@ jobs: hashFiles('Cargo.toml', 'Cargo.lock', 'src/**') }} - name: Set up Rust if: steps.cache-ext.outputs.cache-hit != 'true' - uses: dtolnay/rust-toolchain@stable + run: rustup default stable && rustup update stable - name: Create Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' run: | @@ -349,13 +366,13 @@ jobs: python setup.py build_ext --inplace - name: Cache base Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: venv key: ${{ steps.cache-venv.outputs.cache-primary-key }} - name: Cache Rust extension if: steps.cache-ext.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: serialx/_serialx_rust* key: ${{ steps.cache-ext.outputs.cache-primary-key }} @@ -365,7 +382,7 @@ jobs: python -m serialx.tools.list_ports pytest --timeout=20 --cov=serialx tests - name: Upload coverage artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: coverage-macos-latest include-hidden-files: true @@ -376,7 +393,9 @@ jobs: name: Run tests Python 3.13 (Windows) steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Install com0com and hub4com shell: pwsh working-directory: tests/data/windows @@ -393,13 +412,13 @@ jobs: .\setupc.exe --wait 1 install PortName=-,EmuBR=yes,cts=rrts,dsr=rdtr,dcd=rdtr PortName=-,EmuBR=yes,cts=rrts,dsr=rdtr,dcd=rdtr - name: Set up Python id: python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: "3.13" check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: venv key: >- @@ -408,7 +427,7 @@ jobs: hashFiles('pyproject.toml') }} - name: Restore cached Rust extension id: cache-ext - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: serialx/_serialx_rust* key: >- @@ -417,7 +436,7 @@ jobs: hashFiles('Cargo.toml', 'Cargo.lock', 'src/**') }} - name: Set up Rust if: steps.cache-ext.outputs.cache-hit != 'true' - uses: dtolnay/rust-toolchain@stable + run: rustup default stable && rustup update stable - name: Create Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' run: | @@ -431,13 +450,13 @@ jobs: python setup.py build_ext --inplace - name: Cache base Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: venv key: ${{ steps.cache-venv.outputs.cache-primary-key }} - name: Cache Rust extension if: steps.cache-ext.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: serialx/_serialx_rust* key: ${{ steps.cache-ext.outputs.cache-primary-key }} @@ -448,7 +467,7 @@ jobs: python -m serialx.tools.list_ports pytest --timeout=20 --cov=serialx tests --adapter-pair CNCA0,CNCB0,no-buffer-control,no-rts-dtr-readback - name: Upload coverage artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: coverage-windows-latest include-hidden-files: true @@ -460,16 +479,18 @@ jobs: name: Run serialx-compat tests (Python 3.10) steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Python 3.10 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 id: python with: python-version: "3.10" check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: fail-on-cache-miss: true path: venv @@ -488,13 +509,15 @@ jobs: name: Run tests Pyodide (Bun) steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Bun - uses: oven-sh/setup-bun@v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: bun-version: latest - name: Cache Pyodide package wheels - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: tests/data/pyodide/node_modules/pyodide key: >- @@ -506,7 +529,7 @@ jobs: - name: Run tests under Pyodide run: bun tests/data/pyodide/run_tests.ts --cov=serialx --cov-report= - name: Upload coverage artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: coverage-pyodide include-hidden-files: true @@ -518,16 +541,18 @@ jobs: needs: [pytest, pytest-macos, pytest-windows, pytest-pyodide] steps: - name: Check out code from GitHub - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Python 3.10 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 id: python with: python-version: "3.10" check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache/restore@v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: fail-on-cache-miss: true path: venv @@ -536,7 +561,7 @@ jobs: steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} - name: Download all coverage artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - name: Combine coverage results run: | . venv/bin/activate @@ -546,7 +571,7 @@ jobs: coverage xml - name: Upload coverage to Codecov if: ${{ github.event.repository.fork == false }} - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: false diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cf34e4c..27be9a2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,33 +4,45 @@ on: push: branches: - dev + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + permissions: contents: read - pages: write - id-token: write + jobs: build: + name: Build documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-python@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.14" - - uses: astral-sh/setup-uv@v8.0.0 + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 with: enable-cache: true - run: uv run --extra docs sphinx-build -b html docs site - - uses: actions/upload-pages-artifact@v4 + - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 with: path: site + deploy: + name: Deploy to GitHub Pages if: github.event_name != 'pull_request' needs: build + permissions: + pages: write # required by actions/deploy-pages to publish the artifact + id-token: write # required by actions/deploy-pages for OIDC verification environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - uses: actions/configure-pages@v5 - - uses: actions/deploy-pages@v4 + - uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 + - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 id: deployment diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 7d0c8e8..d2c2aaa 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -1,8 +1,5 @@ name: Build and Publish to PyPI -permissions: - id-token: write - on: release: types: @@ -15,6 +12,13 @@ on: paths: - ".github/workflows/publish-to-pypi.yml" +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + jobs: build-wheels: name: Build wheels (${{ matrix.name }}) @@ -37,15 +41,16 @@ jobs: cibw_before_all: "rustup target add x86_64-apple-darwin aarch64-apple-darwin" steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false - name: Set up Rust - uses: dtolnay/rust-toolchain@stable + run: rustup default stable && rustup update stable - name: Build wheels - uses: pypa/cibuildwheel@v3.4 + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4 env: CIBW_BUILD: "cp310-*" CIBW_ARCHS: "${{ matrix.cibw_archs }}" @@ -59,7 +64,7 @@ jobs: CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "abi3audit {wheel} && cp {wheel} {dest_dir}" - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: wheels-${{ matrix.name }} path: ./wheelhouse/*.whl @@ -68,12 +73,13 @@ jobs: name: Build sdist and pure Python wheel runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: "3.10" @@ -93,13 +99,13 @@ jobs: rm dist/*-cp*-*.whl - name: Upload sdist - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: sdist path: dist/*.tar.gz - name: Upload wheel - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: wheels-ubuntu path: dist/*.whl @@ -109,15 +115,17 @@ jobs: needs: [build-wheels, build-sdist] runs-on: ubuntu-latest if: github.event_name == 'release' + permissions: + id-token: write # required for PyPI trusted publishing (OIDC) steps: - name: Download all artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: path: dist merge-multiple: true - name: Publish serialx to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 build-serialx-compat: name: Build serialx-compat @@ -125,12 +133,13 @@ jobs: runs-on: ubuntu-latest if: ${{ always() && (github.event_name != 'release' || needs['publish-pypi'].result == 'success') }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: "3.10" @@ -223,7 +232,7 @@ jobs: print(f"{wheel}: {metadata.name} version {version}") - name: Upload serialx-compat artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: serialx-compat-dist path: dist-serialx-compat/* @@ -233,14 +242,16 @@ jobs: needs: [build-serialx-compat] runs-on: ubuntu-latest if: github.event_name == 'release' + permissions: + id-token: write # required for PyPI trusted publishing (OIDC) steps: - name: Download serialx-compat artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: serialx-compat-dist path: dist-serialx-compat - name: Publish serialx-compat to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 with: packages-dir: dist-serialx-compat/ From c2a66ef230885d5159459f53719d5e5072e6fbf6 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 28 Apr 2026 22:53:09 -0400 Subject: [PATCH 4/9] Autoupdate --- prek.toml | 8 ++++---- serialx/platforms/serial_pyodide/__init__.py | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/prek.toml b/prek.toml index 74c9bfb..219e24c 100644 --- a/prek.toml +++ b/prek.toml @@ -4,7 +4,7 @@ [[repos]] repo = "https://github.com/codespell-project/codespell" -rev = "v2.4.1" +rev = "v2.4.2" hooks = [ { id = "codespell", @@ -18,7 +18,7 @@ hooks = [ [[repos]] repo = "https://github.com/pre-commit/mirrors-mypy" -rev = "v1.19.0" +rev = "v1.20.2" hooks = [ { id = "mypy", @@ -28,7 +28,7 @@ hooks = [ [[repos]] repo = "https://github.com/astral-sh/ruff-pre-commit" -rev = "v0.14.7" +rev = "v0.15.12" hooks = [ { id = "ruff-check", @@ -82,4 +82,4 @@ hooks = [ id = "zizmor", args = ["--pedantic"] } -] \ No newline at end of file +] diff --git a/serialx/platforms/serial_pyodide/__init__.py b/serialx/platforms/serial_pyodide/__init__.py index 7044768..9d380d5 100644 --- a/serialx/platforms/serial_pyodide/__init__.py +++ b/serialx/platforms/serial_pyodide/__init__.py @@ -269,7 +269,6 @@ async def _writer_loop(self) -> None: self._cleanup(e) break finally: - assert not isinstance(chunk, type) self._write_buffer_size -= len(chunk) self._write_queue.task_done() From bbb14e0aeda18cc617029a590de719439aa85441 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 28 Apr 2026 22:52:57 -0400 Subject: [PATCH 5/9] Validate and format YAML and TOML --- .github/workflows/ci.yml | 45 +-- .github/workflows/publish-to-pypi.yml | 2 +- prek.toml | 117 +++---- pyproject.toml | 419 +++++++++++++------------- serialx_compat/pyproject.toml | 14 +- 5 files changed, 281 insertions(+), 316 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77620cb..9801c2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,9 +36,7 @@ jobs: with: path: venv key: >- - 1-${{ runner.os }}-base-venv-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} + 1-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} - name: Install system dependencies if: steps.cache-venv.outputs.cache-hit != 'true' run: sudo apt-get update && sudo apt-get install -y libudev-dev @@ -82,9 +80,7 @@ jobs: fail-on-cache-miss: true path: venv key: >- - 1-${{ runner.os }}-base-venv-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} + 1-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libudev-dev - name: Restore prek environment from cache @@ -190,8 +186,7 @@ jobs: with: path: tests/esphome/.esphome/build/serialx-host-daemon/.pioenvs/serialx-host-daemon/program key: >- - 2-esphome-daemon-${{ - hashFiles('tests/esphome/host_daemon.yaml', 'tests/esphome/external_components/**') }} + 2-esphome-daemon-${{ hashFiles('tests/esphome/host_daemon.yaml', 'tests/esphome/external_components/**') }} - name: Set up Python 3.13 if: steps.cache-esphome.outputs.cache-hit != 'true' uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 @@ -242,16 +237,13 @@ jobs: fail-on-cache-miss: true path: venv key: >- - 1-${{ runner.os }}-base-venv-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} + 1-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} - name: Restore cached ESPHome binary uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: tests/esphome/.esphome/build/serialx-host-daemon/.pioenvs/serialx-host-daemon/program key: >- - 2-esphome-daemon-${{ - hashFiles('tests/esphome/host_daemon.yaml', 'tests/esphome/external_components/**') }} + 2-esphome-daemon-${{ hashFiles('tests/esphome/host_daemon.yaml', 'tests/esphome/external_components/**') }} - name: Restore cached ser2net binary uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: @@ -338,18 +330,14 @@ jobs: with: path: venv key: >- - 1-${{ runner.os }}-base-venv-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('pyproject.toml') }} + 1-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }} - name: Restore cached Rust extension id: cache-ext uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: serialx/_serialx_rust* key: >- - 1-${{ runner.os }}-rust-ext-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('Cargo.toml', 'Cargo.lock', 'src/**') }} + 1-${{ runner.os }}-rust-ext-${{ steps.python.outputs.python-version }}-${{ hashFiles('Cargo.toml', 'Cargo.lock', 'src/**') }} - name: Set up Rust if: steps.cache-ext.outputs.cache-hit != 'true' run: rustup default stable && rustup update stable @@ -422,18 +410,14 @@ jobs: with: path: venv key: >- - 1-${{ runner.os }}-base-venv-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('pyproject.toml') }} + 1-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }} - name: Restore cached Rust extension id: cache-ext uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: serialx/_serialx_rust* key: >- - 1-${{ runner.os }}-rust-ext-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('Cargo.toml', 'Cargo.lock', 'src/**') }} + 1-${{ runner.os }}-rust-ext-${{ steps.python.outputs.python-version }}-${{ hashFiles('Cargo.toml', 'Cargo.lock', 'src/**') }} - name: Set up Rust if: steps.cache-ext.outputs.cache-hit != 'true' run: rustup default stable && rustup update stable @@ -495,9 +479,7 @@ jobs: fail-on-cache-miss: true path: venv key: >- - 1-${{ runner.os }}-base-venv-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} + 1-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} - name: Install serialx-compat and run tests run: | . venv/bin/activate @@ -521,8 +503,7 @@ jobs: with: path: tests/data/pyodide/node_modules/pyodide key: >- - 1-${{ runner.os }}-pyodide-${{ - hashFiles('tests/data/pyodide/package.json', + 1-${{ runner.os }}-pyodide-${{ hashFiles('tests/data/pyodide/package.json', 'tests/data/pyodide/bun.lock') }} - name: Install Bun dependencies run: bun install --cwd tests/data/pyodide --frozen-lockfile @@ -557,9 +538,7 @@ jobs: fail-on-cache-miss: true path: venv key: >- - 1-${{ runner.os }}-base-venv-${{ - steps.python.outputs.python-version }}-${{ - hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} + 1-${{ runner.os }}-base-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'Cargo.toml', 'src/lib.rs') }} - name: Download all coverage artifacts uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - name: Combine coverage results diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index d2c2aaa..be71696 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -59,7 +59,7 @@ jobs: # Validate abi3 compliance and repair wheels CIBW_BEFORE_BUILD: "pip install abi3audit" # Ensure wheels include a loadable Rust extension on supported test platforms - CIBW_TEST_COMMAND: 'python -m serialx.tools.list_ports' + CIBW_TEST_COMMAND: "python -m serialx.tools.list_ports" CIBW_REPAIR_WHEEL_COMMAND_MACOS: "abi3audit {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "abi3audit {wheel} && cp {wheel} {dest_dir}" diff --git a/prek.toml b/prek.toml index 219e24c..df3b8e0 100644 --- a/prek.toml +++ b/prek.toml @@ -5,81 +5,82 @@ [[repos]] repo = "https://github.com/codespell-project/codespell" rev = "v2.4.2" -hooks = [ - { - id = "codespell", - additional_dependencies = ["tomli"], - args = [ - "--toml", - "pyproject.toml" - ] - } -] + +[[repos.hooks]] +id = "codespell" +additional_dependencies = ["tomli"] +args = ["--toml", "pyproject.toml"] [[repos]] repo = "https://github.com/pre-commit/mirrors-mypy" rev = "v1.20.2" -hooks = [ - { - id = "mypy", - additional_dependencies = ["types-pywin32"] - } -] + +[[repos.hooks]] +id = "mypy" +additional_dependencies = ["types-pywin32"] [[repos]] repo = "https://github.com/astral-sh/ruff-pre-commit" rev = "v0.15.12" -hooks = [ - { - id = "ruff-check", - args = ["--fix"] - }, - { id = "ruff-format" } -] + +[[repos.hooks]] +id = "ruff-check" +args = ["--fix"] + +[[repos.hooks]] +id = "ruff-format" [[repos]] repo = "https://github.com/rbubley/mirrors-prettier" rev = "v3.8.3" -hooks = [ - { - id = "prettier", - files = '^tests/data/pyodide/.*\.(ts|json)$' - } -] + +[[repos.hooks]] +id = "prettier" +files = '^tests/data/pyodide/.*\.(ts|json)$' + +[[repos]] +repo = "https://github.com/google/yamlfmt" +rev = "v0.21.0" + +[[repos.hooks]] +id = "yamlfmt" +args = ["-formatter", "retain_line_breaks=true"] + +[[repos]] +repo = "https://github.com/ComPWA/taplo-pre-commit" +rev = "v0.9.3" + +[[repos.hooks]] +id = "taplo-format" [[repos]] repo = "local" -hooks = [ - { - id = "cargo-fmt", - name = "Cargo fmt", - entry = "cargo fmt --all", - language = "system", - pass_filenames = false - }, - { - id = "cargo-check", - name = "Cargo check", - entry = "cargo check --workspace", - language = "system", - pass_filenames = false - }, - { - id = "rust-clippy", - name = "Cargo clippy", - entry = "cargo clippy --all-targets --all-features -- -Dclippy::all -Dclippy::nursery", - language = "system", - pass_filenames = false - } -] +[[repos.hooks]] +id = "cargo-fmt" +name = "Cargo fmt" +entry = "cargo fmt --all" +language = "system" +pass_filenames = false + +[[repos.hooks]] +id = "cargo-check" +name = "Cargo check" +entry = "cargo check --workspace" +language = "system" +pass_filenames = false + +[[repos.hooks]] +id = "rust-clippy" +name = "Cargo clippy" +entry = "cargo clippy --all-targets --all-features -- -Dclippy::all -Dclippy::nursery" +language = "system" +pass_filenames = false [[repos]] repo = "https://github.com/zizmorcore/zizmor-pre-commit" rev = "v1.24.1" -hooks = [ - { - id = "zizmor", - args = ["--pedantic"] - } -] + +[[repos.hooks]] +id = "zizmor" +args = ["--pedantic"] diff --git a/pyproject.toml b/pyproject.toml index 56c1e8a..1d0d91c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,16 +6,14 @@ build-backend = "setuptools.build_meta" name = "serialx" dynamic = ["version"] description = "Serial library with native sync and async support for Windows, Linux, macOS, and other platforms" -authors = [ - {name = "puddly", email = "puddly3@gmail.com"} -] +authors = [{ name = "puddly", email = "puddly3@gmail.com" }] readme = "README.md" license = "Apache-2.0" requires-python = ">=3.10" dependencies = [ - "typing-extensions", - "async-timeout; python_version < '3.12'", - "pywin32; platform_system == 'Windows'", + "typing-extensions", + "async-timeout; python_version < '3.12'", + "pywin32; platform_system == 'Windows'", ] [project.urls] @@ -23,28 +21,26 @@ repository = "https://github.com/puddly/serialx" documentation = "https://puddly.github.io/serialx/" [project.optional-dependencies] -esphome = [ - "aioesphomeapi>=44.17.0; python_version >= '3.11'", -] +esphome = ["aioesphomeapi>=44.17.0; python_version >= '3.11'"] dev = [ - "ruff", - "pytest", - "prek", - "pytest-asyncio", - "pytest-timeout", - "pytest-xdist", - "psutil", - "aioesphomeapi>=44.17.0 ; python_version >= '3.11'", + "ruff", + "pytest", + "prek", + "pytest-asyncio", + "pytest-timeout", + "pytest-xdist", + "psutil", + "aioesphomeapi>=44.17.0 ; python_version >= '3.11'", ] docs = [ - "sphinx>=7,<8.2.3; python_version < '3.11'", - "sphinx>=8.2.3; python_version >= '3.11'", - "furo>=2025.7.19", - "myst-parser>=4.0.1,<5; python_version < '3.11'", - "myst-parser>=5; python_version >= '3.11'", - "sphinx-design>=0.6", - "sphinx-autobuild>=2024.10.3; python_version < '3.11'", - "sphinx-autobuild>=2025.08.25; python_version >= '3.11'", + "sphinx>=7,<8.2.3; python_version < '3.11'", + "sphinx>=8.2.3; python_version >= '3.11'", + "furo>=2025.7.19", + "myst-parser>=4.0.1,<5; python_version < '3.11'", + "myst-parser>=5; python_version >= '3.11'", + "sphinx-design>=0.6", + "sphinx-autobuild>=2024.10.3; python_version < '3.11'", + "sphinx-autobuild>=2025.08.25; python_version >= '3.11'", ] [tool.setuptools.packages.find] @@ -80,93 +76,93 @@ warn_unreachable = true disallow_any_expr = false disallow_any_decorated = false enable_error_code = [ - "abstract", - "annotation-unchecked", - "arg-type", - "assert-type", - "assignment", - "attr-defined", - "await-not-async", - "call-arg", - "call-overload", - "comparison-overlap", - "deprecated", - "dict-item", - "empty-body", - "exhaustive-match", - "exit-return", - # "explicit-any", - # "explicit-override", - "func-returns-value", - "has-type", - "ignore-without-code", - "import", - "import-not-found", - "import-untyped", - "index", - "list-item", - "literal-required", - "maybe-unrecognized-str-typeform", - "metaclass", - "method-assign", - # "misc", - # "mutable-override", - "name-defined", - "name-match", - "narrowed-type-not-subtype", - # "no-any-return", - # "no-any-unimported", - "no-overload-impl", - "no-redef", - "no-untyped-call", - "no-untyped-def", - "operator", - "overload-cannot-match", - "overload-overlap", - "override", - "possibly-undefined", - "prop-decorator", - "redundant-cast", - "redundant-expr", - "redundant-self", - "return", - "return-value", - "safe-super", - "str-bytes-safe", - "str-format", - "syntax", - "top-level-await", - "truthy-bool", - "truthy-function", - "truthy-iterable", - "type-abstract", - "type-arg", - "type-var", - "typeddict-item", - "typeddict-readonly-mutated", - "typeddict-unknown-key", - "unimported-reveal", - "union-attr", - "unreachable", - "untyped-decorator", - "unused-awaitable", - "unused-coroutine", - "unused-ignore", - "used-before-def", - "valid-newtype", - "valid-type", - "var-annotated", + "abstract", + "annotation-unchecked", + "arg-type", + "assert-type", + "assignment", + "attr-defined", + "await-not-async", + "call-arg", + "call-overload", + "comparison-overlap", + "deprecated", + "dict-item", + "empty-body", + "exhaustive-match", + "exit-return", + # "explicit-any", + # "explicit-override", + "func-returns-value", + "has-type", + "ignore-without-code", + "import", + "import-not-found", + "import-untyped", + "index", + "list-item", + "literal-required", + "maybe-unrecognized-str-typeform", + "metaclass", + "method-assign", + # "misc", + # "mutable-override", + "name-defined", + "name-match", + "narrowed-type-not-subtype", + # "no-any-return", + # "no-any-unimported", + "no-overload-impl", + "no-redef", + "no-untyped-call", + "no-untyped-def", + "operator", + "overload-cannot-match", + "overload-overlap", + "override", + "possibly-undefined", + "prop-decorator", + "redundant-cast", + "redundant-expr", + "redundant-self", + "return", + "return-value", + "safe-super", + "str-bytes-safe", + "str-format", + "syntax", + "top-level-await", + "truthy-bool", + "truthy-function", + "truthy-iterable", + "type-abstract", + "type-arg", + "type-var", + "typeddict-item", + "typeddict-readonly-mutated", + "typeddict-unknown-key", + "unimported-reveal", + "union-attr", + "unreachable", + "untyped-decorator", + "unused-awaitable", + "unused-coroutine", + "unused-ignore", + "used-before-def", + "valid-newtype", + "valid-type", + "var-annotated", ] [[tool.mypy.overrides]] module = ["tests.*"] disable_error_code = [ - "abstract", - "no-untyped-call", - "no-untyped-def", - "redundant-expr", - "unreachable", - "untyped-decorator", + "abstract", + "no-untyped-call", + "no-untyped-def", + "redundant-expr", + "unreachable", + "untyped-decorator", ] [tool.coverage.run] @@ -174,121 +170,118 @@ source = ["serialx"] relative_files = true [tool.coverage.paths] -source = [ - "serialx/", - "serialx\\", -] +source = ["serialx/", "serialx\\"] [tool.ruff] target-version = "py310" [tool.ruff.lint] select = [ - "B002", # Python does not support the unary prefix increment - "B007", # Loop control variable {name} not used within loop body - "B014", # Exception handler with duplicate exception - "B023", # Function definition does not bind loop variable {name} - "B026", # Star-arg unpacking after a keyword argument is strongly discouraged - "B904", # Use raise from to specify exception cause - "C", # complexity - "COM818", # Trailing comma on bare tuple prohibited - "D", # docstrings - "DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow() - "DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts) - "E", # pycodestyle - "F", # pyflakes/autoflake - "G", # flake8-logging-format - "I", # isort - "ICN001", # import concentions; {name} should be imported as {asname} - "N804", # First argument of a class method should be named cls - "N805", # First argument of a method should be named self - "N815", # Variable {name} in class scope should not be mixedCase - "PERF101", # Do not cast an iterable to list before iterating over it - "PERF102", # When using only the {subset} of a dict use the {subset}() method - "PERF203", # try-except within a loop incurs performance overhead - "PGH004", # Use specific rule codes when using noqa - "PLC0414", # Useless import alias. Import alias does not rename original package. - "PLC", # pylint - "PLE", # pylint - "PLR", # pylint - "PLW", # pylint - "Q000", # Double quotes found but single quotes preferred - "RUF006", # Store a reference to the return value of asyncio.create_task - "S102", # Use of exec detected - "S103", # bad-file-permissions - "S108", # hardcoded-temp-file - "S306", # suspicious-mktemp-usage - "S307", # suspicious-eval-usage - "S313", # suspicious-xmlc-element-tree-usage - "S314", # suspicious-xml-element-tree-usage - "S315", # suspicious-xml-expat-reader-usage - "S316", # suspicious-xml-expat-builder-usage - "S317", # suspicious-xml-sax-usage - "S318", # suspicious-xml-mini-dom-usage - "S319", # suspicious-xml-pull-dom-usage - "S601", # paramiko-call - "S602", # subprocess-popen-with-shell-equals-true - "S604", # call-with-shell-equals-true - "S608", # hardcoded-sql-expression - "S609", # unix-command-wildcard-injection - "SIM", # flake8-simplify - "T100", # Trace found: {name} used - "T20", # flake8-print - "TID251", # Banned imports - "TRY", # tryceratops - "UP", # pyupgrade - "W", # pycodestyle + "B002", # Python does not support the unary prefix increment + "B007", # Loop control variable {name} not used within loop body + "B014", # Exception handler with duplicate exception + "B023", # Function definition does not bind loop variable {name} + "B026", # Star-arg unpacking after a keyword argument is strongly discouraged + "B904", # Use raise from to specify exception cause + "C", # complexity + "COM818", # Trailing comma on bare tuple prohibited + "D", # docstrings + "DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow() + "DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts) + "E", # pycodestyle + "F", # pyflakes/autoflake + "G", # flake8-logging-format + "I", # isort + "ICN001", # import concentions; {name} should be imported as {asname} + "N804", # First argument of a class method should be named cls + "N805", # First argument of a method should be named self + "N815", # Variable {name} in class scope should not be mixedCase + "PERF101", # Do not cast an iterable to list before iterating over it + "PERF102", # When using only the {subset} of a dict use the {subset}() method + "PERF203", # try-except within a loop incurs performance overhead + "PGH004", # Use specific rule codes when using noqa + "PLC0414", # Useless import alias. Import alias does not rename original package. + "PLC", # pylint + "PLE", # pylint + "PLR", # pylint + "PLW", # pylint + "Q000", # Double quotes found but single quotes preferred + "RUF006", # Store a reference to the return value of asyncio.create_task + "S102", # Use of exec detected + "S103", # bad-file-permissions + "S108", # hardcoded-temp-file + "S306", # suspicious-mktemp-usage + "S307", # suspicious-eval-usage + "S313", # suspicious-xmlc-element-tree-usage + "S314", # suspicious-xml-element-tree-usage + "S315", # suspicious-xml-expat-reader-usage + "S316", # suspicious-xml-expat-builder-usage + "S317", # suspicious-xml-sax-usage + "S318", # suspicious-xml-mini-dom-usage + "S319", # suspicious-xml-pull-dom-usage + "S601", # paramiko-call + "S602", # subprocess-popen-with-shell-equals-true + "S604", # call-with-shell-equals-true + "S608", # hardcoded-sql-expression + "S609", # unix-command-wildcard-injection + "SIM", # flake8-simplify + "T100", # Trace found: {name} used + "T20", # flake8-print + "TID251", # Banned imports + "TRY", # tryceratops + "UP", # pyupgrade + "W", # pycodestyle ] ignore = [ - "D202", # No blank lines allowed after function docstring - "D203", # 1 blank line required before class docstring - "D213", # Multi-line docstring summary should start at the second line - "D406", # Section name should end with a newline - "D407", # Section name underlining - "E501", # line too long - "E731", # do not assign a lambda expression, use a def + "D202", # No blank lines allowed after function docstring + "D203", # 1 blank line required before class docstring + "D213", # Multi-line docstring summary should start at the second line + "D406", # Section name should end with a newline + "D407", # Section name underlining + "E501", # line too long + "E731", # do not assign a lambda expression, use a def - # Ignore ignored, as the rule is now back in preview/nursery, which cannot - # be ignored anymore without warnings. - # https://github.com/astral-sh/ruff/issues/7491 - # "PLC1901", # Lots of false positives + # Ignore ignored, as the rule is now back in preview/nursery, which cannot + # be ignored anymore without warnings. + # https://github.com/astral-sh/ruff/issues/7491 + # "PLC1901", # Lots of false positives - # False positives https://github.com/astral-sh/ruff/issues/5386 - "PLC0208", # Use a sequence type instead of a `set` when iterating over values - "PLR0911", # Too many return statements ({returns} > {max_returns}) - "PLR0912", # Too many branches ({branches} > {max_branches}) - "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) - "PLR0915", # Too many statements ({statements} > {max_statements}) - "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable - "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target - "UP006", # keep type annotation style as is - "UP007", # keep type annotation style as is + # False positives https://github.com/astral-sh/ruff/issues/5386 + "PLC0208", # Use a sequence type instead of a `set` when iterating over values + "PLR0911", # Too many return statements ({returns} > {max_returns}) + "PLR0912", # Too many branches ({branches} > {max_branches}) + "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) + "PLR0915", # Too many statements ({statements} > {max_statements}) + "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable + "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target + "UP006", # keep type annotation style as is + "UP007", # keep type annotation style as is - # Ignored due to incompatible with mypy: https://github.com/python/mypy/issues/15238 - "UP040", # Checks for use of TypeAlias annotation for declaring type aliases. + # Ignored due to incompatible with mypy: https://github.com/python/mypy/issues/15238 + "UP040", # Checks for use of TypeAlias annotation for declaring type aliases. - # May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules - "W191", - "E111", - "E114", - "E117", - "D206", - "D300", - "Q000", - "Q", - "COM812", - "COM819", - "ISC001", - "ISC002", + # May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules + "W191", + "E111", + "E114", + "E117", + "D206", + "D300", + "Q000", + "Q", + "COM812", + "COM819", + "ISC001", + "ISC002", - # Disabled because ruff does not understand type of __all__ generated by a function - "PLE0605", - "TRY003", - "TRY201", - "TRY300", + # Disabled because ruff does not understand type of __all__ generated by a function + "PLE0605", + "TRY003", + "TRY201", + "TRY300", - "SIM103", # Return the condition {condition} directly + "SIM103", # Return the condition {condition} directly ] extend-safe-fixes = ["F401"] @@ -302,16 +295,13 @@ ban-relative-imports = "all" [tool.ruff.lint.isort] force-sort-within-sections = true -known-first-party = [ - "serialx", - "tests", -] +known-first-party = ["serialx", "tests"] combine-as-imports = true split-on-trailing-comma = false [tool.ruff.lint.per-file-ignores] "tests/*" = [ - "SIM117", # Nested `with` statements are often more readable + "SIM117", # Nested `with` statements are often more readable ] @@ -323,7 +313,4 @@ skip = "tests/data/*" [tool.coverage.report] show_missing = true -exclude_also = [ - "if TYPE_CHECKING:", - "raise NotImplementedError", -] +exclude_also = ["if TYPE_CHECKING:", "raise NotImplementedError"] diff --git a/serialx_compat/pyproject.toml b/serialx_compat/pyproject.toml index cac313c..8d222b8 100644 --- a/serialx_compat/pyproject.toml +++ b/serialx_compat/pyproject.toml @@ -4,22 +4,20 @@ build-backend = "setuptools.build_meta" [project] name = "serialx-compat" -version = "0.0.0" # Rewritten by CI to match the serialx version +version = "0.0.0" # Rewritten by CI to match the serialx version description = "Compatibility package exposing pyserial-style imports backed by serialx" readme = "README.md" requires-python = ">=3.10" license = "Apache-2.0" authors = [{ name = "puddly", email = "puddly3@gmail.com" }] -dependencies = ["serialx"] # Rewritten to serialx== by release CI +dependencies = [ + "serialx", +] # Rewritten to serialx== by release CI [project.optional-dependencies] -esphome = [ - "serialx[esphome]", -] +esphome = ["serialx[esphome]"] -dev = [ - "serialx[dev]", -] +dev = ["serialx[dev]"] [project.urls] Repository = "https://github.com/puddly/serialx" From c38e9c6502f8e18c9ce822884ac4e9d9feacdee5 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 28 Apr 2026 22:59:07 -0400 Subject: [PATCH 6/9] Add `no-commit-to-branch` --- prek.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/prek.toml b/prek.toml index df3b8e0..cee3c37 100644 --- a/prek.toml +++ b/prek.toml @@ -2,6 +2,13 @@ # See https://prek.j178.dev for more information. #:schema https://www.schemastore.org/prek.json +[[repos]] +repo = "builtin" + +[[repos.hooks]] +id = "no-commit-to-branch" +args = ["--branch", "dev"] + [[repos]] repo = "https://github.com/codespell-project/codespell" rev = "v2.4.2" From 307048070a326d5bbf31406389d25907c3eefadc Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 28 Apr 2026 23:13:47 -0400 Subject: [PATCH 7/9] Use tomlkit for toml operations --- .github/workflows/publish-to-pypi.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index be71696..51d4653 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -141,10 +141,10 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: - python-version: "3.10" + python-version: "3.11" - name: Install build tools - run: pip install build pkginfo + run: pip install build pkginfo tomlkit - name: Resolve version context for release if: github.event_name == 'release' @@ -187,19 +187,20 @@ jobs: shell: python run: | import os + import tomlkit from pathlib import Path version = os.environ["SERIALX_PIN_VERSION"] path = Path("serialx_compat/pyproject.toml") - text = path.read_text() + doc = tomlkit.parse(path.read_text()) + + assert doc["project"]["version"] == "0.0.0" + assert list(doc["project"]["dependencies"]) == ["serialx"] - assert 'version = "0.0.0"' in text - assert 'dependencies = ["serialx"]' in text + doc["project"]["version"] = version + doc["project"]["dependencies"] = [f"serialx=={version}"] - path.write_text( - text.replace('version = "0.0.0"', f'version = "{version}"') - .replace('dependencies = ["serialx"]', f'dependencies = ["serialx=={version}"]') - ) + path.write_text(tomlkit.dumps(doc)) - name: Build serialx-compat run: python -m build --sdist --wheel --outdir dist-serialx-compat serialx_compat From 48c7dbc7cc51e2b7da9cf32d4155d784ab4eede5 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:05:39 -0400 Subject: [PATCH 8/9] Update pre-commit reference in development docs --- docs/development.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/development.md b/docs/development.md index 357d10d..d600ba7 100644 --- a/docs/development.md +++ b/docs/development.md @@ -12,9 +12,9 @@ uv pip install '.[dev,docs]' On macOS and Windows, a Rust toolchain is required to build the native serial port enumeration extension. Install Rust via [rustup](https://rustup.rs/). -Set up pre-commit hooks with `pre-commit install`. Your code will then be type checked +Set up pre-commit hooks with `prek install`. Your code will then be type checked and auto-formatted when you run `git commit`. You can do this on-demand with -`pre-commit run`. +`prek run`. Serialx relies on automated testing. CI runs tests using both `socat` virtual PTYs (Linux/macOS) and socket-based serial pairs. To also test with physical adapter pairs, From 3bb715b477c5d209e6dde7081607cf722f2e9c34 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:09:02 -0400 Subject: [PATCH 9/9] Typo --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1d0d91c..259c13e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -192,7 +192,7 @@ select = [ "F", # pyflakes/autoflake "G", # flake8-logging-format "I", # isort - "ICN001", # import concentions; {name} should be imported as {asname} + "ICN001", # import conventions; {name} should be imported as {asname} "N804", # First argument of a class method should be named cls "N805", # First argument of a method should be named self "N815", # Variable {name} in class scope should not be mixedCase