From ed990be7f92951b7d9f530588b31e6a8c80b9240 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 09:57:53 +0000 Subject: [PATCH 1/9] dependencies: (deps): bump the actions group across 1 directory with 2 updates Bumps the actions group with 2 updates in the / directory: [actions/checkout](https://github.com/actions/checkout) and [actions/setup-python](https://github.com/actions/setup-python). Updates `actions/checkout` from 5.0.0 to 6.0.0 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/08c6903cd8c0fde910a37f88322edcfb5dd907a8...1af3b93b6815bc44a9784bd300feb67ff0d1eeb3) Updates `actions/setup-python` from 6.0.0 to 6.1.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/e797f83bcb11b83ae66e0230d6156d7c80228e7c...83679a892e2d95755f2dac6acb0bfd1e9ac5d548) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/setup-python dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a3366ab..1756da7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,10 +18,10 @@ jobs: python-version: ["3.10", "3.13"] steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v5 with: python-version: ${{ matrix.python-version }} From 47794f9edbeb9c9891c9aa9ef7fe6e32c8768d6d Mon Sep 17 00:00:00 2001 From: Michael Pilosov Date: Mon, 12 Jan 2026 15:59:35 -0700 Subject: [PATCH 2/9] switch to dep groups (PEP 735) --- CHANGELOG.md | 7 +++ template/pyproject.toml.jinja | 82 ++++++++++++++++++++++++++++++----- tests/test_template_init.py | 47 ++++++++++++++++++++ 3 files changed, 124 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e66aec6..e5426b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## [Unreleased] +### Changed +* Migrate from `optional-dependencies` to PEP 735 `dependency-groups` for hatch environments (@mm, #145) + * All development dependencies now use the standard `[dependency-groups]` table instead of `[project.optional-dependencies]` + * Hatch environments now use `dependency-groups = [...]` instead of `features = [...]` + * This change requires Hatch 1.16+ (which supports dependency groups in builder and non-dev environments) + * When `use_hatch_envs=False`, optional-dependencies are still used for backward compatibility + ## [v0.6.8] ### Fixed diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 74a340a..ba661e3 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -69,21 +69,18 @@ Documentation = "{{ dev_platform_url }}/{{ username }}/{{ project_slug }}/blob/m {%- endif %} Download = "https://pypi.org/project/{{ project_slug }}/#files" +{%- if not use_hatch_envs %} [project.optional-dependencies] -# The groups below should be in the [development-groups] table -# They are here now because hatch hasn't released support for them but plans to -# in Mid November 2025. +# When not using hatch environments, we keep optional-dependencies for backward compatibility dev = [ "hatch", "pre-commit", - {%- if not use_hatch_envs %} "{{ package_name }}[ {%- if documentation!="" %}docs,{% endif -%} {%- if use_test %}tests,{% endif -%} {%- if use_lint %}style,{% endif -%} {%- if use_types %}types,{% endif -%} audit]", - {%- endif %} ] docs = [ @@ -129,6 +126,67 @@ types = [ "mypy", ] {%- endif %} +{%- endif %} + +{%- if use_hatch_envs %} +################################################################################ +# Dependency Groups (PEP 735) +################################################################################ + +[dependency-groups] +# Development tools that are used across multiple environments +dev = [ + "hatch", + "pre-commit", +] + +{%- if documentation != "no" %} +docs = [ +{%- if documentation == "sphinx" %} + "sphinx~=8.0", + "myst-parser>=4.0", + "pydata-sphinx-theme~=0.16", + "sphinx-autobuild>=2024.10.3", + "sphinx-autoapi>=3.6.0", + "sphinx_design>=0.6.1", + "sphinx-copybutton>=0.5.2", +{%- elif documentation == "mkdocs" %} + "mkdocs-material ~=9.5", + "mkdocstrings[python] ~=0.24", + "mkdocs-awesome-pages-plugin ~=2.9", +{% endif %} +] + +{%- endif %} +build = [ + "pip-audit", + "twine", +] + +{%- if use_test %} +tests = [ + "pytest", + "pytest-cov", + "pytest-raises", + "pytest-randomly", + "pytest-xdist", +] + +{%- endif %} +{%- if use_lint %} +style = [ + "pydoclint", + "ruff", +] + +{%- endif %} +{%- if use_types %} +types = [ + "mypy", +] + +{%- endif %} +{%- endif %} ################################################################################ @@ -276,7 +334,7 @@ installer = "uv" # This table installs the tools you need to test and build your package [tool.hatch.envs.build] description = """Test the installation the package.""" -features = [ +dependency-groups = [ "build", ] detached = true @@ -293,7 +351,7 @@ check = [ {%- if use_test %} [tool.hatch.envs.test] description = """Run the test suite.""" -features = [ +dependency-groups = [ "tests", ] @@ -309,8 +367,8 @@ run = "pytest {args:--cov={{ package_name }} --cov-report=term-missing --cov-rep # This sets up a hatch environment with associated dependencies that need to be installed [tool.hatch.envs.docs] description = """Build or serve the documentation.""" -# Install optional dependency test for docs -features = [ +# Install dependency group for docs +dependency-groups = [ "docs", ] @@ -333,7 +391,7 @@ serve = ["sphinx-autobuild docs --watch src/{{ package_name }} {args:-b html doc [tool.hatch.envs.style] description = """Check the code and documentation style.""" -features = [ +dependency-groups = [ "style", ] detached = true @@ -349,7 +407,7 @@ check = ["docstrings", "code"] [tool.hatch.envs.audit] description = """Check dependencies for security vulnerabilities.""" -features = [ +dependency-groups = [ "build", ] @@ -361,7 +419,7 @@ check = ["pip-audit"] #--------------- Typing ---------------# [tool.hatch.envs.types] description = """Check the static types of the codebase.""" -features = ["types"] +dependency-groups = ["types"] [tool.hatch.envs.types.scripts] check = "mypy src/{{ package_name }}" diff --git a/tests/test_template_init.py b/tests/test_template_init.py index 9e6a13f..d560410 100644 --- a/tests/test_template_init.py +++ b/tests/test_template_init.py @@ -254,3 +254,50 @@ def test_non_hatch_deps( if documentation != "no": assert "docs" in optional_deps assert any(dep.startswith(documentation) for dep in optional_deps["docs"]) + + +def test_hatch_deps_groups( + documentation: str, + generated: Callable[..., Path], +) -> None: + """When using hatch environments, we should use dependency-groups (PEP 735).""" + project = generated( + use_hatch_envs=True, + use_lint=True, + use_types=True, + use_test=True, + use_git=False, + documentation=documentation, + ) + + pyproject_file = project / "pyproject.toml" + with pyproject_file.open("rb") as pfile: + pyproject = tomllib.load(pfile) + + # validate pyproject.toml file if present + validator_api.Validator()(pyproject) + + # When using hatch_envs, dependencies should be in dependency-groups, not optional-dependencies + assert "dependency-groups" in pyproject + dep_groups = pyproject["dependency-groups"] + + # Check that expected groups exist + groups = ("dev", "tests", "style", "types", "build") + assert all(group in dep_groups for group in groups) + + # Check that docs group exists if documentation is enabled + if documentation != "no": + assert "docs" in dep_groups + assert any(dep.startswith(documentation) for dep in dep_groups["docs"]) + + # Verify that hatch environments use dependency-groups + if "tool" in pyproject and "hatch" in pyproject["tool"]: + hatch_envs = pyproject["tool"]["hatch"].get("envs", {}) + for env_name, env_config in hatch_envs.items(): + if env_name != "default" and isinstance(env_config, dict): + # Check that environments use dependency-groups instead of features + if "dependency-groups" in env_config: + # Verify the dependency-groups reference valid groups + env_dep_groups = env_config["dependency-groups"] + for group in env_dep_groups: + assert group in dep_groups, f"Environment {env_name} references unknown dependency group: {group}" From 8d13d80c9f79e39211c3af791cc73cc4af9a2992 Mon Sep 17 00:00:00 2001 From: Michael Pilosov Date: Mon, 12 Jan 2026 16:02:54 -0700 Subject: [PATCH 3/9] builder=true (for now) --- template/pyproject.toml.jinja | 1 + 1 file changed, 1 insertion(+) diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index ba661e3..0e4e8aa 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -334,6 +334,7 @@ installer = "uv" # This table installs the tools you need to test and build your package [tool.hatch.envs.build] description = """Test the installation the package.""" +builder = true dependency-groups = [ "build", ] From 66f4bf16b7a333a1d338bdc3512b0284ec426dad Mon Sep 17 00:00:00 2001 From: Michael Pilosov Date: Mon, 12 Jan 2026 16:06:26 -0700 Subject: [PATCH 4/9] hatch run build:check to scope tests properly --- tests/test_template_init.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_template_init.py b/tests/test_template_init.py index d560410..dc51a6a 100644 --- a/tests/test_template_init.py +++ b/tests/test_template_init.py @@ -166,10 +166,14 @@ def test_template_suite( generated: Callable[..., Path], ) -> None: """Expect that the test suite passes for the initialized template.""" - project_dir = generated() + # Explicitly enable hatch environments to ensure build environment exists + project_dir = generated(use_hatch_envs=True) # Run the local test suite. - run_command("hatch build --clean", project_dir) + # Use hatch run build:check to ensure we use the build environment + # (Hatch 1.16+ requires builder environments to have builder=true) + # This runs "hatch build --clean" and "twine check" in the build environment + run_command("hatch run build:check", project_dir) run_command(f"hatch run +py={sys.version_info.major}.{sys.version_info.minor} test:run", project_dir) run_command("hatch run style:check", project_dir) From 3bef2e1dcf1cb3189ebb5578e287251366f53698 Mon Sep 17 00:00:00 2001 From: Michael Pilosov Date: Mon, 12 Jan 2026 16:18:19 -0700 Subject: [PATCH 5/9] invoke twine as a module (dont assume it is in PATH) --- template/pyproject.toml.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 0e4e8aa..f0e753e 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -344,7 +344,7 @@ detached = true [tool.hatch.envs.build.scripts] check = [ "hatch build {args:--clean}", - "twine check dist/*", + "python -m twine check dist/*", ] #--------------- Run tests ---------------# From 4c624bb1f012abb149557c58e1836da82bbad7d6 Mon Sep 17 00:00:00 2001 From: Michael Pilosov Date: Mon, 12 Jan 2026 16:20:45 -0700 Subject: [PATCH 6/9] Revert "invoke twine as a module (dont assume it is in PATH)" This reverts commit 3bef2e1dcf1cb3189ebb5578e287251366f53698. --- template/pyproject.toml.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index f0e753e..0e4e8aa 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -344,7 +344,7 @@ detached = true [tool.hatch.envs.build.scripts] check = [ "hatch build {args:--clean}", - "python -m twine check dist/*", + "twine check dist/*", ] #--------------- Run tests ---------------# From 7e546ea894ac5c8fafe7535afd479c358e3f151e Mon Sep 17 00:00:00 2001 From: Michael Pilosov Date: Mon, 12 Jan 2026 16:22:04 -0700 Subject: [PATCH 7/9] guess we need twine? --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a3366ab..b304aa4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install hatch + python -m pip install hatch twine - name: Test suite run: hatch run +py=${{ matrix.python-version }} test:run -- -vv -s From 77e0622cf5e824246912b9194285c0299bcc41ea Mon Sep 17 00:00:00 2001 From: Michael Pilosov Date: Mon, 12 Jan 2026 16:51:17 -0700 Subject: [PATCH 8/9] declare twine as a dep to avoid manual installation --- .github/workflows/main.yml | 2 +- template/pyproject.toml.jinja | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b304aa4..a3366ab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install hatch twine + python -m pip install hatch - name: Test suite run: hatch run +py=${{ matrix.python-version }} test:run -- -vv -s diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 0e4e8aa..34e9081 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -101,7 +101,6 @@ docs = [ build = [ "pip-audit", - "twine", ] {%- if use_test %} @@ -338,6 +337,12 @@ builder = true dependency-groups = [ "build", ] +# Explicit dependencies are required as a workaround for dependency-groups +# not being installed in builder environments (see https://github.com/pypa/hatch/issues/2152) +# This ensures twine is available when scripts run +dependencies = [ + "twine", +] detached = true # This table installs created the command hatch run install:check which will build and check your package. From 93b4c3f4d1cd194b93e1d539cc09774959063d88 Mon Sep 17 00:00:00 2001 From: Michael Pilosov Date: Mon, 12 Jan 2026 16:58:05 -0700 Subject: [PATCH 9/9] fix reference to github username --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5426b7..fc45722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## [Unreleased] ### Changed -* Migrate from `optional-dependencies` to PEP 735 `dependency-groups` for hatch environments (@mm, #145) +* Migrate from `optional-dependencies` to PEP 735 `dependency-groups` for hatch environments (@mathematicalmichael, #145) * All development dependencies now use the standard `[dependency-groups]` table instead of `[project.optional-dependencies]` * Hatch environments now use `dependency-groups = [...]` instead of `features = [...]` * This change requires Hatch 1.16+ (which supports dependency groups in builder and non-dev environments)