-
Notifications
You must be signed in to change notification settings - Fork 16
switch hatch to dependency groups, split deps to data file #144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
44e09ea
b1dd00d
c0c4cca
2149d05
6c7bafb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Deps are specified as dicts of dicts | ||
| # | ||
| # The top-level key is the key that will be used within the pyproject.toml file, | ||
| # unless "key" is specified in the render_deps macro | ||
| # | ||
| # Within each group, | ||
| # the key is the package name, and the value, if present, is the version constraint. | ||
| # Use empty values to indicate no version constraint. | ||
|
|
||
|
|
||
| build: | ||
| pip-audit: | ||
| twine: | ||
|
|
||
| dev: | ||
| hatch: ">=1.16.0" | ||
| pre-commit: | ||
|
|
||
| mkdocs: | ||
| mkdocs-material: '~=9.5' | ||
| mkdocstrings[python]: '~=0.24' | ||
| mkdocs-awesome-pages-plugin: '~=2.9' | ||
|
|
||
| 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' | ||
|
|
||
| style: | ||
| pydoclint: | ||
| ruff: | ||
|
|
||
| tests: | ||
| pytest: | ||
| pytest-cov: | ||
| pytest-raises: | ||
| pytest-randomly: | ||
| pytest-xdist: | ||
|
|
||
| types: | ||
| mypy: | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| {# Import this macro like `from .. import render_deps with context` to give it access to the deps data #} | ||
| {% macro render_deps(group, inner=False, key=None) %} | ||
| {#- for some reason copier insists on loading things as nested lists inconsistently across versions #} | ||
| {%- if not deps[0] is mapping %} | ||
| {%- set d=deps[0][0] %} | ||
| {%- else -%} | ||
| {%- set d=deps[0] %} | ||
| {%- endif -%} | ||
| {%- if group and group in d -%} | ||
| {%- if not inner -%} | ||
| {{ group if not key else key }} = [ | ||
| {%- endif %} | ||
| {%- for package in d[group]|sort %} | ||
| "{{ package }}{% if d[group][package] %}{{ d[group][package] }}{% endif %}", | ||
| {%- endfor %} | ||
| {%- if not inner %} | ||
| ] | ||
| {%- endif -%} | ||
| {%- endif -%} | ||
| {% endmacro %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| {%- from pathjoin("includes", "dependencies.toml.jinja") import render_deps with context -%} | ||
| ################################################################################ | ||
| # Build Configuration | ||
| ################################################################################ | ||
|
|
@@ -69,67 +70,27 @@ Documentation = "{{ dev_platform_url }}/{{ username }}/{{ project_slug }}/blob/m | |
| {%- endif %} | ||
| Download = "https://pypi.org/project/{{ project_slug }}/#files" | ||
|
|
||
| [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. | ||
| [dependency-groups] | ||
| 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]", | ||
| {{- render_deps("dev", inner=True) }} | ||
| {%- if documentation in ("sphinx", "mkdocs") %} | ||
| {include-group = "docs"}, | ||
| {%- endif %} | ||
| {%- if use_test %} | ||
| {include-group = "tests"}, | ||
| {%- endif -%} | ||
| {%- if use_lint %} | ||
| {include-group = "style"}, | ||
| {%- endif -%} | ||
| {%- if use_types %} | ||
| {include-group = "types"}, | ||
| {%- endif %} | ||
| ] | ||
|
|
||
| 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 %} | ||
| ] | ||
|
|
||
| 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 %} | ||
|
|
||
| {{ render_deps(documentation, key="docs") }} | ||
| {{ render_deps("build") }} | ||
| {%- if use_test %}{{ "\n" }}{{ render_deps("tests") }}{% endif %} | ||
| {%- if use_lint %}{{ "\n" }}{{ render_deps("style") }}{% endif %} | ||
| {%- if use_types %}{{ "\n" }}{{ render_deps("types") }}{% endif %} | ||
|
Comment on lines
+91
to
+93
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I always have to look up the whitespace behavior of jinja2, but isn't it possible to have each group start on a newline without inserting a literal
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could also consider {%- if use_test %}
tests = [
{{ render_deps("tests") }}
]
{%- endif %}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did it this way mostly because i wanted the top level pyproject.toml to be a logical template (what to display) and the macro to be the structural template (how to display it), and to link the keys in the dict with the output to avoid key discrepancies (tests/test). Its a little awkward but this way of writing them makes them self contained lines that dont have different start/end whitespace behavior that would need to be handled by, e.g., remembering to suppress whitespace at the end of the last line and start of first, and future groups could copy/paste the one line and just modify the key |
||
|
|
||
| ################################################################################ | ||
| # Tool Configuration | ||
|
|
@@ -276,10 +237,12 @@ 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", | ||
| ] | ||
| {{ render_deps("build", key="dependencies") }} | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this, btw, is why it's nice to have the dependencies separated out as data - if we need to do weird things with them like render them twice, we can do it without needing to remember to keep multiple places updated, all the deps stay specified in the yaml |
||
| detached = true | ||
sneakers-the-rat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| builder = true | ||
|
|
||
| # This table installs created the command hatch run install:check which will build and check your package. | ||
| [tool.hatch.envs.build.scripts] | ||
|
|
@@ -293,7 +256,7 @@ check = [ | |
| {%- if use_test %} | ||
| [tool.hatch.envs.test] | ||
| description = """Run the test suite.""" | ||
| features = [ | ||
| dependency-groups = [ | ||
| "tests", | ||
| ] | ||
sneakers-the-rat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
@@ -310,10 +273,10 @@ run = "pytest {args:--cov={{ package_name }} --cov-report=term-missing --cov-rep | |
| [tool.hatch.envs.docs] | ||
| description = """Build or serve the documentation.""" | ||
| # Install optional dependency test for docs | ||
| features = [ | ||
| dependency-groups = [ | ||
| "docs", | ||
| ] | ||
|
|
||
| builder = true | ||
| # This table contains the scripts that you can use to build and serve your docs | ||
| # hatch run docs:build will build your documentation | ||
| # hatch run docs:serve will serve them 'live' on your computer locally | ||
|
|
@@ -333,7 +296,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 | ||
sneakers-the-rat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
@@ -349,9 +312,10 @@ check = ["docstrings", "code"] | |
|
|
||
| [tool.hatch.envs.audit] | ||
| description = """Check dependencies for security vulnerabilities.""" | ||
| features = [ | ||
| dependency-groups = [ | ||
| "build", | ||
| ] | ||
sneakers-the-rat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| builder = true | ||
|
|
||
| [tool.hatch.envs.audit.scripts] | ||
| check = ["pip-audit"] | ||
|
|
@@ -361,7 +325,8 @@ check = ["pip-audit"] | |
| #--------------- Typing ---------------# | ||
| [tool.hatch.envs.types] | ||
| description = """Check the static types of the codebase.""" | ||
| features = ["types"] | ||
| dependency-groups = ["types"] | ||
sneakers-the-rat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| builder = true | ||
|
|
||
| [tool.hatch.envs.types.scripts] | ||
| check = "mypy src/{{ package_name }}" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,16 @@ | ||
| """Provide fixtures to the entire test suite.""" | ||
|
|
||
| import shutil | ||
| from pathlib import Path | ||
| from typing import TYPE_CHECKING, Generator | ||
| from pathlib import Path, PurePosixPath | ||
| from typing import TYPE_CHECKING, Any, Generator | ||
|
|
||
| import pytest | ||
| from funcy import lflatten | ||
| from _pytest.monkeypatch import MonkeyPatch | ||
| from jinja2 import Environment, FileSystemLoader | ||
| from ruamel.yaml import YAML | ||
| from ruamel.yaml import YAML, Loader | ||
| from ruamel.yaml.constructor import Constructor | ||
| from ruamel.yaml.nodes import ScalarNode | ||
|
|
||
| if TYPE_CHECKING: | ||
| from _pytest.config.argparsing import Parser | ||
|
|
@@ -16,9 +19,25 @@ | |
| COPIER_CONFIG_PATH = Path(__file__).parents[1] / "copier.yml" | ||
| INCLUDES_PATH = Path(__file__).parents[1] / "includes" | ||
|
|
||
| # handle copier's !include tags - | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, OK. So we are modifying this function rather than using what the copier uses, to allow us to import the dependency files effectively in a way that we can trust the "API" part won't change on us? Is that the gist?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's basically so that we do effectively what they do in the tests where we are loading the copier template directly (rather than calling copier, which we do in most of the tests) without reaching inside their private methods. also they use pyyaml and we are using ruamel.yaml, so had to adapt it for that too |
||
| # they went and did us the favor of making their entire package private, | ||
| # so to respect their wishes to touch nothing we copy it here | ||
| # with mild modifications for our use case and for ruamel.yaml | ||
| # https://github.com/copier-org/copier/blob/24e842d838cf41b90a024ae4f80834add0ea95c2/copier/_template.py#L86 | ||
| def _include(loader: Constructor, node: ScalarNode) -> Any: | ||
| if not isinstance(node, ScalarNode): | ||
| raise ValueError(f"Unsupported YAML node: {node!r}") | ||
| include_file = str(loader.construct_scalar(node)) | ||
| if PurePosixPath(include_file).is_absolute(): | ||
| raise ValueError("YAML include file path must be a relative path") | ||
| path = next(COPIER_CONFIG_PATH.parent.glob(include_file)) | ||
| return [YAML(typ="safe").load(path.read_bytes())] | ||
|
|
||
|
|
||
| def _load_copier_config() -> dict: | ||
| yaml = YAML(typ="safe") | ||
| yaml.constructor.add_constructor("!include", _include) | ||
|
|
||
| with COPIER_CONFIG_PATH.open("r") as yfile: | ||
| return yaml.load(yfile) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had the discussion on Slack regarding a lack of being able to require a minimum hatch version, despite that, we need hatch as a global dependency to even create the hatch environments, so this dependency does not make sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was hoping that it would be able to detect a mismatch of version between the installed hatch and the requested hatch when it created the build env it used to install the package, but that might have been an empty hope. Im not sure where this dep should get declared but we need to declare it somewhere, because now the template wouldnt work with hatch versions older than this. So i guess this is a problem we would need to fix for whatever implementation of
dependency-groupswe do hereUh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we think about the hatch dependency in the same way we think about python. This template supports hatch >= version x.x.x in our documentation? I know it's not a perfect solution.
The better solution would be something akin to
python-requiresThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah i think in any case it's sort of at a different level than the template itself, it seems like a feature that hatch needs to me. the thing i worry about with putting dependencies in the README is that it's much easier to not see them than programmatic specifications that force them to be true, but I added a note in the readme here: b1dd00d
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
definitely not a concern for your pr here! agree 💯 !!