From e30889cdaf30b135dc8f6fa685104c3d4c172db3 Mon Sep 17 00:00:00 2001 From: Corentin Garcia Date: Sat, 13 Jul 2024 23:26:16 +0200 Subject: [PATCH 01/22] fix #57 - include tests in sdist --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 0e846fb..99f986c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ version-file = "src/iniconfig/_version.py" [tool.hatch.build.targets.sdist] include = [ "/src", + "/testing", ] [tool.hatch.envs.test] From b402c9332d15d484246f22f937bc087a29d7e2dd Mon Sep 17 00:00:00 2001 From: tn3w Date: Mon, 14 Jul 2025 16:44:41 +0200 Subject: [PATCH 02/22] Refactor type hinting in SectionWrapper and IniConfig classes for improved readability --- src/iniconfig/__init__.py | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/iniconfig/__init__.py b/src/iniconfig/__init__.py index 3c40bc9..17e56f0 100644 --- a/src/iniconfig/__init__.py +++ b/src/iniconfig/__init__.py @@ -1,20 +1,15 @@ -""" brain-dead simple parser for ini-style files. +"""brain-dead simple parser for ini-style files. (C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed """ + from __future__ import annotations from typing import ( Callable, Iterator, Mapping, - Optional, - Tuple, TypeVar, - Union, TYPE_CHECKING, - NoReturn, - NamedTuple, overload, - cast, ) import os @@ -44,16 +39,14 @@ def lineof(self, name: str) -> int | None: return self.config.lineof(self.name, name) @overload - def get(self, key: str) -> str | None: - ... + def get(self, key: str) -> str | None: ... @overload def get( self, key: str, convert: Callable[[str], _T], - ) -> _T | None: - ... + ) -> _T | None: ... @overload def get( @@ -61,12 +54,10 @@ def get( key: str, default: None, convert: Callable[[str], _T], - ) -> _T | None: - ... + ) -> _T | None: ... @overload - def get(self, key: str, default: _D, convert: None = None) -> str | _D: - ... + def get(self, key: str, default: _D, convert: None = None) -> str | _D: ... @overload def get( @@ -74,8 +65,7 @@ def get( key: str, default: _D, convert: Callable[[str], _T], - ) -> _T | _D: - ... + ) -> _T | _D: ... # TODO: investigate possible mypy bug wrt matching the passed over data def get( # type: ignore [misc] @@ -148,8 +138,7 @@ def get( self, section: str, name: str, - ) -> str | None: - ... + ) -> str | None: ... @overload def get( @@ -157,8 +146,7 @@ def get( section: str, name: str, convert: Callable[[str], _T], - ) -> _T | None: - ... + ) -> _T | None: ... @overload def get( @@ -167,14 +155,12 @@ def get( name: str, default: None, convert: Callable[[str], _T], - ) -> _T | None: - ... + ) -> _T | None: ... @overload def get( self, section: str, name: str, default: _D, convert: None = None - ) -> str | _D: - ... + ) -> str | _D: ... @overload def get( @@ -183,8 +169,7 @@ def get( name: str, default: _D, convert: Callable[[str], _T], - ) -> _T | _D: - ... + ) -> _T | _D: ... def get( # type: ignore self, From dcbfb256adec9200695f2d0fbffe23a9c68c7c4e Mon Sep 17 00:00:00 2001 From: tn3w Date: Mon, 14 Jul 2025 21:34:19 +0200 Subject: [PATCH 03/22] Fix pre-commit black formatting --- src/iniconfig/__init__.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/iniconfig/__init__.py b/src/iniconfig/__init__.py index 17e56f0..1c68974 100644 --- a/src/iniconfig/__init__.py +++ b/src/iniconfig/__init__.py @@ -39,14 +39,16 @@ def lineof(self, name: str) -> int | None: return self.config.lineof(self.name, name) @overload - def get(self, key: str) -> str | None: ... + def get(self, key: str) -> str | None: + ... @overload def get( self, key: str, convert: Callable[[str], _T], - ) -> _T | None: ... + ) -> _T | None: + ... @overload def get( @@ -54,10 +56,12 @@ def get( key: str, default: None, convert: Callable[[str], _T], - ) -> _T | None: ... + ) -> _T | None: + ... @overload - def get(self, key: str, default: _D, convert: None = None) -> str | _D: ... + def get(self, key: str, default: _D, convert: None = None) -> str | _D: + ... @overload def get( @@ -65,7 +69,8 @@ def get( key: str, default: _D, convert: Callable[[str], _T], - ) -> _T | _D: ... + ) -> _T | _D: + ... # TODO: investigate possible mypy bug wrt matching the passed over data def get( # type: ignore [misc] @@ -138,7 +143,8 @@ def get( self, section: str, name: str, - ) -> str | None: ... + ) -> str | None: + ... @overload def get( @@ -146,7 +152,8 @@ def get( section: str, name: str, convert: Callable[[str], _T], - ) -> _T | None: ... + ) -> _T | None: + ... @overload def get( @@ -155,12 +162,14 @@ def get( name: str, default: None, convert: Callable[[str], _T], - ) -> _T | None: ... + ) -> _T | None: + ... @overload def get( self, section: str, name: str, default: _D, convert: None = None - ) -> str | _D: ... + ) -> str | _D: + ... @overload def get( @@ -169,7 +178,8 @@ def get( name: str, default: _D, convert: Callable[[str], _T], - ) -> _T | _D: ... + ) -> _T | _D: + ... def get( # type: ignore self, From f63efa8f6c4e76b3f494c5547f431e6c973f1071 Mon Sep 17 00:00:00 2001 From: tn3w Date: Tue, 15 Jul 2025 14:48:55 +0200 Subject: [PATCH 04/22] Remove unnecessary blank lines in pyproject.toml --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 05cd96e..1083259 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,6 @@ classifiers = [ [project.urls] Homepage = "https://github.com/pytest-dev/iniconfig" - [tool.hatch.version] source = "vcs" @@ -67,6 +66,5 @@ python = ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] [tool.mypy] strict = true - [tool.pytest.ini_options] testpaths = "testing" From 211b4c0038265354f8c87283a7f41c15e5dd3a52 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 15 Jul 2025 23:52:56 +0200 Subject: [PATCH 05/22] pre-commit autoupgrade --- .pre-commit-config.yaml | 8 +++--- pyproject.toml | 58 +++++++++++++++++++-------------------- src/iniconfig/__init__.py | 30 +++++++------------- 3 files changed, 42 insertions(+), 54 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 20e04ba..bd471e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,21 +1,21 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.20.0 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.4.1" + rev: "v2.6.0" hooks: - id: pyproject-fmt - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 25.1.0 hooks: - id: black language_version: python3 - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.991' + rev: 'v1.17.0' hooks: - id: mypy args: [] diff --git a/pyproject.toml b/pyproject.toml index 1083259..42158bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,33 +11,33 @@ description = "brain-dead simple config-ini parsing" readme = "README.rst" license = "MIT" authors = [ - { name = "Ronny Pfannschmidt", email = "opensource@ronnypfannschmidt.de" }, - { name = "Holger Krekel", email = "holger.krekel@gmail.com" }, + { name = "Ronny Pfannschmidt", email = "opensource@ronnypfannschmidt.de" }, + { name = "Holger Krekel", email = "holger.krekel@gmail.com" }, ] requires-python = ">=3.8" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Programming Language :: Python :: 3 :: Only", + "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", + "Topic :: Software Development :: Libraries", + "Topic :: Utilities", +] dynamic = [ "version", ] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: MacOS :: MacOS X", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "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", - "Topic :: Software Development :: Libraries", - "Topic :: Utilities", -] -[project.urls] -Homepage = "https://github.com/pytest-dev/iniconfig" +urls.Homepage = "https://github.com/pytest-dev/iniconfig" + +[tool.setuptools_scm] [tool.hatch.version] source = "vcs" @@ -47,24 +47,22 @@ version-file = "src/iniconfig/_version.py" [tool.hatch.build.targets.sdist] include = [ - "/src", - "/testing", + "/src", + "/testing", ] [tool.hatch.envs.test] dependencies = [ - "pytest" + "pytest", ] [tool.hatch.envs.test.scripts] default = "pytest {args}" [[tool.hatch.envs.test.matrix]] -python = ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] +python = [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13" ] -[tool.setuptools_scm] +[tool.pytest.ini_options] +testpaths = "testing" [tool.mypy] strict = true - -[tool.pytest.ini_options] -testpaths = "testing" diff --git a/src/iniconfig/__init__.py b/src/iniconfig/__init__.py index 1c68974..17e56f0 100644 --- a/src/iniconfig/__init__.py +++ b/src/iniconfig/__init__.py @@ -39,16 +39,14 @@ def lineof(self, name: str) -> int | None: return self.config.lineof(self.name, name) @overload - def get(self, key: str) -> str | None: - ... + def get(self, key: str) -> str | None: ... @overload def get( self, key: str, convert: Callable[[str], _T], - ) -> _T | None: - ... + ) -> _T | None: ... @overload def get( @@ -56,12 +54,10 @@ def get( key: str, default: None, convert: Callable[[str], _T], - ) -> _T | None: - ... + ) -> _T | None: ... @overload - def get(self, key: str, default: _D, convert: None = None) -> str | _D: - ... + def get(self, key: str, default: _D, convert: None = None) -> str | _D: ... @overload def get( @@ -69,8 +65,7 @@ def get( key: str, default: _D, convert: Callable[[str], _T], - ) -> _T | _D: - ... + ) -> _T | _D: ... # TODO: investigate possible mypy bug wrt matching the passed over data def get( # type: ignore [misc] @@ -143,8 +138,7 @@ def get( self, section: str, name: str, - ) -> str | None: - ... + ) -> str | None: ... @overload def get( @@ -152,8 +146,7 @@ def get( section: str, name: str, convert: Callable[[str], _T], - ) -> _T | None: - ... + ) -> _T | None: ... @overload def get( @@ -162,14 +155,12 @@ def get( name: str, default: None, convert: Callable[[str], _T], - ) -> _T | None: - ... + ) -> _T | None: ... @overload def get( self, section: str, name: str, default: _D, convert: None = None - ) -> str | _D: - ... + ) -> str | _D: ... @overload def get( @@ -178,8 +169,7 @@ def get( name: str, default: _D, convert: Callable[[str], _T], - ) -> _T | _D: - ... + ) -> _T | _D: ... def get( # type: ignore self, From 4fa1d1918c1728d97cad3af35d1dc5fe2097e61f Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 14:24:16 +0200 Subject: [PATCH 06/22] Remove pre-commit job from workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pre-commit checks are now handled by pre-commit.ci, so the dedicated workflow job is no longer needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/main.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f491e17..e7a322a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,12 +24,3 @@ jobs: run: python -m pip install --upgrade pip hatch hatch-vcs - name: Run tests run: hatch run +py=${{ matrix.python }} test:default --color=yes - - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.x - - uses: pre-commit/action@v3.0.1 From a0cd289631bd5b6b4b4c9dac5f524e798a0fc8c5 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 14:41:09 +0200 Subject: [PATCH 07/22] Migrate to unified test workflow with build-and-inspect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace separate main.yml and deploy.yml with unified test.yml - Use hynek/build-and-inspect-python-package@v2 for building - Remove SETUPTOOLS_SCM_PRETEND_VERSION hack (no longer needed) - Test actual built wheel artifacts instead of source - Migrate from hatch to uv for faster dependency management - Add uv caching with astral-sh/setup-uv@v7 - Use trusted publishing with correct environment names - Enable build provenance attestations for supply chain security 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/deploy.yml | 63 ---------------------- .github/workflows/main.yml | 26 --------- .github/workflows/test.yml | 102 +++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 89 deletions(-) delete mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index b6942e9..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Deploy - -on: - push: - branches: - - master - - "*deploy*" - release: - types: - - published - -jobs: - build: - if: github.repository == 'pytest-dev/iniconfig' - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Cache - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: deploy-${{ hashFiles('**/pyproject.toml') }} - restore-keys: | - deploy- - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - - name: Install build + twine - run: python -m pip install build twine setuptools_scm - - - name: git describe output - run: git describe --tags - - - id: scm_version - run: | - VERSION=$(python -m setuptools_scm --strip-dev) - echo SETUPTOOLS_SCM_PRETEND_VERSION=$VERSION >> $GITHUB_ENV - - - name: Build package - run: python -m build - - - name: twine check - run: twine check dist/* - - - name: Publish package to PyPI - if: github.event.action == 'published' - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.pypi_password }} - - - name: Publish package to TestPyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.test_pypi_password }} - repository_url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index e7a322a..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: build - -on: [push, pull_request, workflow_dispatch] - -jobs: - build: - - runs-on: ${{ matrix.os }} - - strategy: - fail-fast: false - matrix: - python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] - os: [ubuntu-latest, windows-latest] - - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - allow-prereleases: true - - name: Install hatch - run: python -m pip install --upgrade pip hatch hatch-vcs - - name: Run tests - run: hatch run +py=${{ matrix.python }} test:default --color=yes diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..6be27ee --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,102 @@ +name: test + +on: [push, pull_request, workflow_dispatch] + +jobs: + build-and-inspect: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build and inspect package + uses: hynek/build-and-inspect-python-package@v2 + with: + attest-build-provenance-github: true + + test: + needs: build-and-inspect + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + os: [ubuntu-latest, windows-latest] + + steps: + - name: Download built packages + uses: actions/download-artifact@v4 + with: + name: Packages + path: dist + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Set up Python ${{ matrix.python }} + run: uv python install ${{ matrix.python }} + + - name: Install package from wheel + run: uv pip install --system dist/*.whl + + - name: Download test files + uses: actions/checkout@v4 + with: + sparse-checkout: | + testing + sparse-checkout-cone-mode: false + + - name: Run tests + run: uv run pytest testing --color=yes + + publish-to-pypi: + name: Publish to PyPI + if: github.event_name == 'push' && github.event.action == 'published' + needs: [test, build-and-inspect] + runs-on: ubuntu-latest + environment: pypi-upload + permissions: + id-token: write + attestations: write + + steps: + - name: Download built packages + uses: actions/download-artifact@v4 + with: + name: Packages + path: dist + + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + publish-to-test-pypi: + name: Publish to TestPyPI + if: | + github.repository == 'pytest-dev/iniconfig' && + (github.event_name == 'push' && ( + github.ref == 'refs/heads/main' || + contains(github.ref, 'deploy') + )) + needs: [test, build-and-inspect] + runs-on: ubuntu-latest + environment: test-pypi-upload + permissions: + id-token: write + attestations: write + + steps: + - name: Download built packages + uses: actions/download-artifact@v4 + with: + name: Packages + path: dist + + - name: Publish package to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ From 11bb727653a9f8e125acdcf019de7c819d5328c7 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 14:43:49 +0200 Subject: [PATCH 08/22] Add required permissions for build attestations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add id-token, attestations, and contents permissions to the build-and-inspect job to enable OIDC token generation for build provenance attestations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6be27ee..ccaa1b2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,10 @@ on: [push, pull_request, workflow_dispatch] jobs: build-and-inspect: runs-on: ubuntu-latest + permissions: + id-token: write + attestations: write + contents: read steps: - uses: actions/checkout@v4 From ce099712b011421f45980ae780a8478782292e0e Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 15:08:33 +0200 Subject: [PATCH 09/22] Remove --system flag and use uv run --with properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use uv's isolated environment approach instead of system-wide installation. This is the recommended pattern for both GitHub Actions and GitLab CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/test.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ccaa1b2..1910f65 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,9 +45,6 @@ jobs: - name: Set up Python ${{ matrix.python }} run: uv python install ${{ matrix.python }} - - name: Install package from wheel - run: uv pip install --system dist/*.whl - - name: Download test files uses: actions/checkout@v4 with: @@ -55,8 +52,8 @@ jobs: testing sparse-checkout-cone-mode: false - - name: Run tests - run: uv run pytest testing --color=yes + - name: Run tests with built wheel + run: uv run --with dist/*.whl --with pytest pytest testing --color=yes publish-to-pypi: name: Publish to PyPI From 22e00189c00e758d082e1abd066cdf893bb3fda2 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 15:18:44 +0200 Subject: [PATCH 10/22] Move checkout before uv setup and verify artifacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move sparse checkout to the beginning (before uv setup) - Include uv.lock in sparse checkout for dependency resolution - Add verification step to assert wheel and sdist artifacts exist - List dist/ contents for debugging if tests fail This ensures the workflow fails fast if build artifacts are missing or malformed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/test.yml | 22 +++++++++++++++------- uv.lock | 7 +++++++ 2 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 uv.lock diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1910f65..3506708 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,12 +31,27 @@ jobs: os: [ubuntu-latest, windows-latest] steps: + - name: Checkout test files + uses: actions/checkout@v4 + with: + sparse-checkout: | + testing + uv.lock + sparse-checkout-cone-mode: false + - name: Download built packages uses: actions/download-artifact@v4 with: name: Packages path: dist + - name: Verify artifacts exist + run: | + ls -la dist/ + test -f dist/*.whl || (echo "No wheel found!" && exit 1) + test -f dist/*.tar.gz || (echo "No sdist found!" && exit 1) + shell: bash + - name: Install uv uses: astral-sh/setup-uv@v7 with: @@ -45,13 +60,6 @@ jobs: - name: Set up Python ${{ matrix.python }} run: uv python install ${{ matrix.python }} - - name: Download test files - uses: actions/checkout@v4 - with: - sparse-checkout: | - testing - sparse-checkout-cone-mode: false - - name: Run tests with built wheel run: uv run --with dist/*.whl --with pytest pytest testing --color=yes diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..98e385b --- /dev/null +++ b/uv.lock @@ -0,0 +1,7 @@ +version = 1 +revision = 3 +requires-python = ">=3.8" + +[[package]] +name = "iniconfig" +source = { editable = "." } From 08658769dfa2c9c507ff3efe398acfb4cb562e84 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 15:26:32 +0200 Subject: [PATCH 11/22] Use bash as default shell for cross-platform glob support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set bash as the default shell for the test job to ensure glob patterns work consistently on both Ubuntu and Windows runners. This simplifies the workflow by avoiding platform-specific paths. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3506708..4dbd893 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,6 +23,9 @@ jobs: test: needs: build-and-inspect runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash strategy: fail-fast: false @@ -50,7 +53,6 @@ jobs: ls -la dist/ test -f dist/*.whl || (echo "No wheel found!" && exit 1) test -f dist/*.tar.gz || (echo "No sdist found!" && exit 1) - shell: bash - name: Install uv uses: astral-sh/setup-uv@v7 From 340c77e7afe32a278f6bb641f5b9d6ef0d15e02d Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 15:32:53 +0200 Subject: [PATCH 12/22] Use build-and-inspect Python versions output for test matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the supported_python_classifiers_json_array output from build-and-inspect-python-package instead of hardcoding Python versions. This ensures the CI automatically tests all Python versions declared in the package classifiers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4dbd893..f9cd22d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,8 @@ jobs: id-token: write attestations: write contents: read + outputs: + python-versions: ${{ steps.baipp.outputs.supported_python_classifiers_json_array }} steps: - uses: actions/checkout@v4 @@ -16,6 +18,7 @@ jobs: fetch-depth: 0 - name: Build and inspect package + id: baipp uses: hynek/build-and-inspect-python-package@v2 with: attest-build-provenance-github: true @@ -30,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python: ${{ fromJSON(needs.build-and-inspect.outputs.python-versions) }} os: [ubuntu-latest, windows-latest] steps: From 3af74adab75bf42fbfca55848da60b7a5a116859 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 18:17:44 +0200 Subject: [PATCH 13/22] Migrate to setuptools 77 with PEP 639 and drop Python 3.8/3.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Switch from hatchling to setuptools>=77 build backend - Use modern PEP 639 license specifiers (license-files) - Configure setuptools_scm to write version to _version.py - Drop Python 3.8 and 3.9 support (requires-python >= 3.10) - Add SETUPTOOLS_SCM_OVERRIDES_FOR_INICONFIG to use no-local-version scheme on main branch instead of SETUPTOOLS_SCM_PRETEND_VERSION hack - Remove hatch configuration and test environments This modernizes the build system and uses setuptools_scm's official override mechanism for controlling version schemes per branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/test.yml | 2 ++ pyproject.toml | 40 ++++++++++++-------------------------- uv.lock | 2 +- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9cd22d..637af7d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,8 @@ jobs: uses: hynek/build-and-inspect-python-package@v2 with: attest-build-provenance-github: true + env: + SETUPTOOLS_SCM_OVERRIDES_FOR_INICONFIG: ${{ github.ref == 'refs/heads/main' && 'local_scheme="no-local-version"' || '' }} test: needs: build-and-inspect diff --git a/pyproject.toml b/pyproject.toml index 42158bb..bd520bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [build-system] -build-backend = "hatchling.build" +build-backend = "setuptools.build_meta" requires = [ - "hatch-vcs", - "hatchling>=1.26", + "setuptools>=77", + "setuptools-scm>=8", ] [project] @@ -10,21 +10,19 @@ name = "iniconfig" description = "brain-dead simple config-ini parsing" readme = "README.rst" license = "MIT" +license-files = [ "LICENSE" ] authors = [ { name = "Ronny Pfannschmidt", email = "opensource@ronnypfannschmidt.de" }, { name = "Holger Krekel", email = "holger.krekel@gmail.com" }, ] -requires-python = ">=3.8" +requires-python = ">=3.10" classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Programming Language :: Python :: 3 :: Only", - "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", @@ -37,29 +35,15 @@ dynamic = [ ] urls.Homepage = "https://github.com/pytest-dev/iniconfig" -[tool.setuptools_scm] - -[tool.hatch.version] -source = "vcs" +[tool.setuptools] +packages = [ "iniconfig" ] +package-dir = { "" = "src" } -[tool.hatch.build.hooks.vcs] -version-file = "src/iniconfig/_version.py" - -[tool.hatch.build.targets.sdist] -include = [ - "/src", - "/testing", -] +[tool.setuptools.package-data] +iniconfig = [ "py.typed" ] -[tool.hatch.envs.test] -dependencies = [ - "pytest", -] -[tool.hatch.envs.test.scripts] -default = "pytest {args}" - -[[tool.hatch.envs.test.matrix]] -python = [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13" ] +[tool.setuptools_scm] +write_to = "src/iniconfig/_version.py" [tool.pytest.ini_options] testpaths = "testing" diff --git a/uv.lock b/uv.lock index 98e385b..470797b 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.8" +requires-python = ">=3.10" [[package]] name = "iniconfig" From 40f7760e0e8f9c1b7a733099e43e2c52bbe7d001 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 18:19:18 +0200 Subject: [PATCH 14/22] pre-commit upgrade --- .pre-commit-config.yaml | 8 ++++---- pyproject.toml | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bd471e1..4058b5f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,21 +1,21 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.20.0 + rev: v3.21.0 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/tox-dev/pyproject-fmt - rev: "v2.6.0" + rev: "v2.11.0" hooks: - id: pyproject-fmt - repo: https://github.com/psf/black - rev: 25.1.0 + rev: 25.9.0 hooks: - id: black language_version: python3 - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.17.0' + rev: 'v1.18.2' hooks: - id: mypy args: [] diff --git a/pyproject.toml b/pyproject.toml index bd520bc..65dfeed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Libraries", "Topic :: Utilities", ] From 1dab01a81f78dd6154833b27217a038d06b5c2eb Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 18:28:25 +0200 Subject: [PATCH 15/22] Migrate from black + pyupgrade to ruff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace black and pyupgrade with ruff-check and ruff-format hooks - Configure ruff with isort (I), bugbear (B), pyupgrade (UP), and pytest-style (PT) rules - Set force-single-line imports for isort - Auto-fix imports and code style with ruff Ruff automatically: - Sorted and organized imports (isort) - Upgraded to modern Python syntax (pyupgrade) - Applied bugbear fixes - Fixed pytest style issues (renamed shadowing variables) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .pre-commit-config.yaml | 31 ++++++++++++++----------------- pyproject.toml | 11 +++++++++++ src/iniconfig/__init__.py | 19 +++++++++---------- src/iniconfig/_parse.py | 4 ++-- src/iniconfig/exceptions.py | 1 + testing/test_iniconfig.py | 31 +++++++++++++++++-------------- 6 files changed, 54 insertions(+), 43 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4058b5f..1e54504 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,24 +1,21 @@ repos: -- repo: https://github.com/asottile/pyupgrade - rev: v3.21.0 - hooks: - - id: pyupgrade - args: [--py38-plus] - repo: https://github.com/tox-dev/pyproject-fmt rev: "v2.11.0" hooks: - id: pyproject-fmt -- repo: https://github.com/psf/black - rev: 25.9.0 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.1 hooks: - - id: black - language_version: python3 -- repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.18.2' - hooks: - - id: mypy - args: [] - additional_dependencies: - - "pytest==7.2.0" - - "tomli" \ No newline at end of file + - id: ruff-check + args: [--fix] + - id: ruff-format + +- repo: https://github.com/pre-commit/mirrors-mypy + rev: 'v1.18.2' + hooks: + - id: mypy + args: [] + additional_dependencies: + - "pytest==7.2.0" + - "tomli" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 65dfeed..c689521 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,17 @@ iniconfig = [ "py.typed" ] [tool.setuptools_scm] write_to = "src/iniconfig/_version.py" +[tool.ruff] + +lint.extend-select = [ + "B", # flake8-bugbear + "I", # isort + "PT", # flake8-pytest-style + "UP", # pyupgrade +] +lint.isort.force-single-line = true +lint.isort.known-first-party = [ "iniconfig" ] + [tool.pytest.ini_options] testpaths = "testing" diff --git a/src/iniconfig/__init__.py b/src/iniconfig/__init__.py index 17e56f0..934dfab 100644 --- a/src/iniconfig/__init__.py +++ b/src/iniconfig/__init__.py @@ -3,25 +3,24 @@ """ from __future__ import annotations -from typing import ( - Callable, - Iterator, - Mapping, - TypeVar, - TYPE_CHECKING, - overload, -) import os +from collections.abc import Callable +from collections.abc import Iterator +from collections.abc import Mapping +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import overload if TYPE_CHECKING: from typing import Final __all__ = ["IniConfig", "ParseError", "COMMENTCHARS", "iscommentline"] -from .exceptions import ParseError from . import _parse -from ._parse import COMMENTCHARS, iscommentline +from ._parse import COMMENTCHARS +from ._parse import iscommentline +from .exceptions import ParseError _D = TypeVar("_D") _T = TypeVar("_T") diff --git a/src/iniconfig/_parse.py b/src/iniconfig/_parse.py index 2d03437..22a327f 100644 --- a/src/iniconfig/_parse.py +++ b/src/iniconfig/_parse.py @@ -1,8 +1,8 @@ from __future__ import annotations -from .exceptions import ParseError from typing import NamedTuple +from .exceptions import ParseError COMMENTCHARS = "#;" @@ -70,7 +70,7 @@ def _parseline(path: str, line: str, lineno: int) -> tuple[str | None, str | Non try: name, value = line.split(":", 1) except ValueError: - raise ParseError(path, lineno, "unexpected line: %r" % line) + raise ParseError(path, lineno, f"unexpected line: {line!r}") from None return name.strip(), value.strip() # continuation else: diff --git a/src/iniconfig/exceptions.py b/src/iniconfig/exceptions.py index 8c4dc9a..9705216 100644 --- a/src/iniconfig/exceptions.py +++ b/src/iniconfig/exceptions.py @@ -1,4 +1,5 @@ from __future__ import annotations + from typing import TYPE_CHECKING if TYPE_CHECKING: diff --git a/testing/test_iniconfig.py b/testing/test_iniconfig.py index 9e94b28..f8547c8 100644 --- a/testing/test_iniconfig.py +++ b/testing/test_iniconfig.py @@ -1,11 +1,15 @@ from __future__ import annotations -import pytest -from iniconfig import IniConfig, ParseError, __all__ as ALL -from iniconfig._parse import _ParsedLine as PL -from iniconfig import iscommentline -from textwrap import dedent + from pathlib import Path +from textwrap import dedent +import pytest + +from iniconfig import IniConfig +from iniconfig import ParseError +from iniconfig import __all__ as ALL +from iniconfig import iscommentline +from iniconfig._parse import _ParsedLine as PL check_tokens: dict[str, tuple[str, list[PL]]] = { "section": ("[section]", [PL(0, "section", None, None)]), @@ -44,7 +48,6 @@ @pytest.fixture(params=sorted(check_tokens)) def input_expected(request: pytest.FixtureRequest) -> tuple[str, list[PL]]: - return check_tokens[request.param] @@ -214,12 +217,12 @@ def test_config_iter() -> None: """ ), ) - l = list(config) - assert len(l) == 2 - assert l[0].name == "section1" - assert l[0]["value"] == "1" - assert l[1].name == "section2" - assert l[1]["value"] == "2" + sections = list(config) + assert len(sections) == 2 + assert sections[0].name == "section1" + assert sections[0]["value"] == "1" + assert sections[1].name == "section2" + assert sections[1]["value"] == "2" def test_config_contains() -> None: @@ -251,8 +254,8 @@ def test_iter_file_order() -> None: b = 2 """, ) - l = list(config) - secnames = [x.name for x in l] + sections_list = list(config) + secnames = [x.name for x in sections_list] assert secnames == ["section2", "section"] assert list(config["section2"]) == ["value", "value2"] assert list(config["section"]) == ["a", "b"] From 7fc4089c33d8cccc219c82fc2d973c3eb3af7069 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 18:51:33 +0200 Subject: [PATCH 16/22] Remove Python 3.10+ redundant patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove `from __future__ import annotations` (default in Python 3.10+) - Remove TYPE_CHECKING guard for Final imports (not needed in 3.10+) - Import Final directly from typing module - Use forward references for IniConfig in SectionWrapper These patterns are no longer needed since we require Python >= 3.10 and PEP 563 postponed evaluation is the default. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/iniconfig/__init__.py | 11 +++-------- src/iniconfig/_parse.py | 2 -- src/iniconfig/exceptions.py | 7 +------ testing/test_iniconfig.py | 2 -- 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/iniconfig/__init__.py b/src/iniconfig/__init__.py index 934dfab..5188fc4 100644 --- a/src/iniconfig/__init__.py +++ b/src/iniconfig/__init__.py @@ -2,19 +2,14 @@ (C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed """ -from __future__ import annotations - import os from collections.abc import Callable from collections.abc import Iterator from collections.abc import Mapping -from typing import TYPE_CHECKING +from typing import Final from typing import TypeVar from typing import overload -if TYPE_CHECKING: - from typing import Final - __all__ = ["IniConfig", "ParseError", "COMMENTCHARS", "iscommentline"] from . import _parse @@ -27,10 +22,10 @@ class SectionWrapper: - config: Final[IniConfig] + config: Final["IniConfig"] name: Final[str] - def __init__(self, config: IniConfig, name: str) -> None: + def __init__(self, config: "IniConfig", name: str) -> None: self.config = config self.name = name diff --git a/src/iniconfig/_parse.py b/src/iniconfig/_parse.py index 22a327f..db75fde 100644 --- a/src/iniconfig/_parse.py +++ b/src/iniconfig/_parse.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import NamedTuple from .exceptions import ParseError diff --git a/src/iniconfig/exceptions.py b/src/iniconfig/exceptions.py index 9705216..d078bc6 100644 --- a/src/iniconfig/exceptions.py +++ b/src/iniconfig/exceptions.py @@ -1,9 +1,4 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from typing import Final +from typing import Final class ParseError(Exception): diff --git a/testing/test_iniconfig.py b/testing/test_iniconfig.py index f8547c8..acdfda7 100644 --- a/testing/test_iniconfig.py +++ b/testing/test_iniconfig.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from pathlib import Path from textwrap import dedent From 65c974837e1818db1f3d0917614efa5638981c9d Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 18:53:46 +0200 Subject: [PATCH 17/22] Rename _ParsedLine to ParsedLine for consistency and clarity --- src/iniconfig/_parse.py | 10 +++++----- testing/test_iniconfig.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/iniconfig/_parse.py b/src/iniconfig/_parse.py index db75fde..a162636 100644 --- a/src/iniconfig/_parse.py +++ b/src/iniconfig/_parse.py @@ -5,27 +5,27 @@ COMMENTCHARS = "#;" -class _ParsedLine(NamedTuple): +class ParsedLine(NamedTuple): lineno: int section: str | None name: str | None value: str | None -def parse_lines(path: str, line_iter: list[str]) -> list[_ParsedLine]: - result: list[_ParsedLine] = [] +def parse_lines(path: str, line_iter: list[str]) -> list[ParsedLine]: + result: list[ParsedLine] = [] section = None for lineno, line in enumerate(line_iter): name, data = _parseline(path, line, lineno) # new value if name is not None and data is not None: - result.append(_ParsedLine(lineno, section, name, data)) + result.append(ParsedLine(lineno, section, name, data)) # new section elif name is not None and data is None: if not name: raise ParseError(path, lineno, "empty section name") section = name - result.append(_ParsedLine(lineno, section, None, None)) + result.append(ParsedLine(lineno, section, None, None)) # continuation elif name is None and data is not None: if not result: diff --git a/testing/test_iniconfig.py b/testing/test_iniconfig.py index acdfda7..dd11c73 100644 --- a/testing/test_iniconfig.py +++ b/testing/test_iniconfig.py @@ -7,7 +7,7 @@ from iniconfig import ParseError from iniconfig import __all__ as ALL from iniconfig import iscommentline -from iniconfig._parse import _ParsedLine as PL +from iniconfig._parse import ParsedLine as PL check_tokens: dict[str, tuple[str, list[PL]]] = { "section": ("[section]", [PL(0, "section", None, None)]), From 1c81708be28537e649e2979b65a7f6a5db26f617 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 19:02:00 +0200 Subject: [PATCH 18/22] Add development dependencies for pytest and pytest-xdist --- pyproject.toml | 6 ++ uv.lock | 160 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index c689521..50db162 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,12 @@ dynamic = [ ] urls.Homepage = "https://github.com/pytest-dev/iniconfig" +[dependency-groups] +dev = [ + "pytest>=8.4.2", + "pytest-xdist>=3.8", +] + [tool.setuptools] packages = [ "iniconfig" ] package-dir = { "" = "src" } diff --git a/uv.lock b/uv.lock index 470797b..18ebf4f 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,166 @@ version = 1 revision = 3 requires-python = ">=3.10" +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "execnet" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524, upload-time = "2024-04-08T09:04:19.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612, upload-time = "2024-04-08T09:04:17.414Z" }, +] + [[package]] name = "iniconfig" source = { editable = "." } + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-xdist" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.4.2" }, + { name = "pytest-xdist", specifier = ">=3.8.0" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytest-xdist" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, +] + +[[package]] +name = "tomli" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, + { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, + { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] From 306fd862817de17a47fec0890fc0c89c5cb61dee Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 19:11:19 +0200 Subject: [PATCH 19/22] type annotate _sources of IniConfig for clarity --- src/iniconfig/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/iniconfig/__init__.py b/src/iniconfig/__init__.py index 5188fc4..3fadacf 100644 --- a/src/iniconfig/__init__.py +++ b/src/iniconfig/__init__.py @@ -89,6 +89,7 @@ def items(self) -> Iterator[tuple[str, str]]: class IniConfig: path: Final[str] sections: Final[Mapping[str, Mapping[str, str]]] + _sources: Final[Mapping[tuple[str, str | None], int]] def __init__( self, From c1926a98c160a284c4b7ded6a33a621b6c0da19b Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 19:59:14 +0200 Subject: [PATCH 20/22] Add create-release job to GitHub Actions workflow for automated releases --- .github/workflows/test.yml | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 637af7d..33c840a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,15 +70,41 @@ jobs: - name: Run tests with built wheel run: uv run --with dist/*.whl --with pytest pytest testing --color=yes + create-release: + name: Create GitHub Release + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + needs: [test, build-and-inspect] + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Download built packages + uses: actions/download-artifact@v4 + with: + name: Packages + path: dist + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: dist/* + fail_on_unmatched_files: true + generate_release_notes: true + name: Version ${{ steps.version.outputs.VERSION }} + publish-to-pypi: name: Publish to PyPI - if: github.event_name == 'push' && github.event.action == 'published' - needs: [test, build-and-inspect] + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + needs: [test, build-and-inspect, create-release] runs-on: ubuntu-latest environment: pypi-upload permissions: id-token: write - attestations: write steps: - name: Download built packages From 8b2bccb3b6be37c9bf70a4fbe0cda4c92c70355f Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 21:51:57 +0200 Subject: [PATCH 21/22] Update CHANGELOG and automate releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add CHANGELOG entry for version 2.2.0 - Add create-release job to auto-create GitHub releases on tag push - Change publish-to-pypi to trigger on tag push (not release event) - Auto-generate release notes and attach build artifacts The release process is now fully automated: push a tag and the workflow will build, test, create a GitHub release, and publish to PyPI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index cc3a51a..eeb0245 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,17 @@ +2.2.0 +===== + +* drop Python 3.8 and 3.9 support (now requires Python >= 3.10) +* add Python 3.14 classifier +* migrate from hatchling to setuptools 77 with setuptools_scm +* adopt PEP 639 license specifiers and PEP 740 build attestations +* migrate from black + pyupgrade to ruff +* migrate CI to uv and unified test workflow +* automate GitHub releases and PyPI publishing via Trusted Publishing +* include tests in sdist +* modernize code for Python 3.10+ (remove __future__ annotations, TYPE_CHECKING guards) +* rename _ParsedLine to ParsedLine + 2.1.0 ===== From 3402322097aa7b6eba9a68c96faddc780e04b085 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 18 Oct 2025 22:17:13 +0200 Subject: [PATCH 22/22] Disable build attestations for PRs from forks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Conditionally enable build provenance attestations only when not running in a fork PR context. Fork PRs lack the necessary permissions (id-token and attestations write) to create attestations, causing workflow failures. Attestations now only run for: - Pushes to branches - PRs from branches within the same repository 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 33c840a..8368741 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: id: baipp uses: hynek/build-and-inspect-python-package@v2 with: - attest-build-provenance-github: true + attest-build-provenance-github: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} env: SETUPTOOLS_SCM_OVERRIDES_FOR_INICONFIG: ${{ github.ref == 'refs/heads/main' && 'local_scheme="no-local-version"' || '' }}