diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d62f47de..5ff61343 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -95,5 +95,5 @@ repos: name: Cog the pages language: python entry: cog -P -r -I ./helpers - files: "^docs/pages/guides/(packaging_compiled|docs|tasks|gha_basic).md|^copier.yml" - additional_dependencies: [cogapp, cookiecutter] + files: "^docs/pages/guides/(packaging_compiled|docs|tasks|gha_basic).md|^copier.yml|^docs/_includes/pyproject.md" + additional_dependencies: [cogapp, cookiecutter, tomlkit] diff --git a/docs/_includes/pyproject.md b/docs/_includes/pyproject.md index fe6124e1..a747061b 100644 --- a/docs/_includes/pyproject.md +++ b/docs/_includes/pyproject.md @@ -1,51 +1,67 @@ ## pyproject.toml: project table + + + The metadata is specified in a [standards-based][metadata] format: + + ```toml [project] name = "package" version = "0.1.0" +authors = [ + { name = "My Name", email = "me@email.com" }, +] description = "A great package." readme = "README.md" license = "BSD-3-Clause" license-files = ["LICENSE"] -authors = [ - { name = "My Name", email = "me@email.com" }, -] -maintainers = [ - { name = "My Organization", email = "myemail@email.com" }, -] requires-python = ">=3.10" - -dependencies = [ - "typing_extensions", -] - classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 1 - Planning", + "Intended Audience :: Science/Research", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", - "Topic :: Scientific/Engineering :: Physics", + "Topic :: Scientific/Engineering", + "Typing :: Typed", ] +dependencies = [] [project.urls] -Homepage = "https://github.com/organization/package" -Documentation = "https://package.readthedocs.io/" -"Bug Tracker" = "https://github.com/organization/package/issues" -Discussions = "https://github.com/organization/package/discussions" -Changelog = "https://package.readthedocs.io/en/latest/changelog.html" +Homepage = "https://github.com/org/package" +"Bug Tracker" = "https://github.com/org/package/issues" +Discussions = "https://github.com/org/package/discussions" +Changelog = "https://github.com/org/package/releases" ``` + + You can read more about each field, and all allowed fields, in [packaging.python.org][metadata], [Flit](https://flit.readthedocs.io/en/latest/pyproject_toml.html#new-style-metadata) -or [Whey](https://whey.readthedocs.io/en/latest/configuration.html). Note that -"Homepage" is special, and replaces the old url setting. +or [Whey](https://whey.readthedocs.io/en/latest/configuration.html). Only the +`name` and `version` fields are strictly required. Note that "Homepage" is +special, and replaces the old url setting. + +If you use the above configuration, you need `README.md` and `LICENSE` files, +since they are explicitly specified. ### License @@ -55,7 +71,9 @@ The modern way is to use the `license` field and an [SPDX identifier expression][spdx]. You can specify a list of files globs in `license-files`. You need `hatchling>=1.26`, `flit-core>=1.11` (1.12 for complex license statements), `pdm-backend>=2.4`, `setuptools>=77`, `meson-python>=0.18`, `maturin>=1.9.2`, -`poetry-core>=2.2`, or `scikit-build-core>=0.12` to support this. +`poetry-core>=2.2`, or `scikit-build-core>=0.12` to support this. You can also +specify `license-files` as a list with globs for license files. If you don't, +most backends will discover common license file names by default. The classic convention uses one or more [Trove Classifiers][] to specify the license. There also was a `license.file` field, required by `meson-python`, but @@ -63,7 +81,7 @@ other tools often did the wrong thing (such as load the entire file into the metadata's free-form one line text field that was intended to describe deviations from the classifier license(s)). -``` +```toml classifiers = [ "License :: OSI Approved :: BSD License", ] @@ -122,15 +140,30 @@ your package); the `dev` group is even installed, by default, when using `uv`'s high level commands like `uv run` and `uv sync`. {% rr PP0086 %} Here is an example: + + ```toml [dependency-groups] test = [ - "pytest >=6.0", + "pytest >=6", + "pytest-cov >=3", ] dev = [ { include-group = "test" }, ] +docs = [ + "sphinx>=7.0", + "myst_parser>=0.13", + "sphinx_copybutton", + "sphinx_autodoc_typehints", + "furo>=2023.08.17", +] ``` + + You can include one dependency group in another. Most tools allow you to install groups using `--group`, like `pip` (25.1+), `uv pip`, and the high level `uv` diff --git a/docs/pages/guides/docs.md b/docs/pages/guides/docs.md index bb38ef82..02a6b3a3 100644 --- a/docs/pages/guides/docs.md +++ b/docs/pages/guides/docs.md @@ -75,15 +75,15 @@ Ideally, software documentation should include: > it if you are interested. diff --git a/docs/pages/guides/packaging_compiled.md b/docs/pages/guides/packaging_compiled.md index d84e97be..337c4053 100644 --- a/docs/pages/guides/packaging_compiled.md +++ b/docs/pages/guides/packaging_compiled.md @@ -163,10 +163,9 @@ install_subdir('src/package', install_dir: py.get_install_dir() / 'package', str -Meson also requires that `LICENSE` and `README.md` exist, and that your source -be tracked by version control. In a real project, you will likely be doing this, -but when trying out a build backend you might not think to add these even though -they are required. +Meson requires that your source be tracked by version control. In a real +project, you will likely be doing this, but when trying out a build backend you +might not think to set up a git repo to build it. {% endtab %} {% tab maturin Maturin %} diff --git a/docs/pages/guides/tasks.md b/docs/pages/guides/tasks.md index 0d104ede..f364860e 100644 --- a/docs/pages/guides/tasks.md +++ b/docs/pages/guides/tasks.md @@ -11,9 +11,9 @@ parent: Topical Guides # Task runners diff --git a/helpers/cog_helpers.py b/helpers/cog_helpers.py index bfc68202..3e935b32 100644 --- a/helpers/cog_helpers.py +++ b/helpers/cog_helpers.py @@ -2,16 +2,19 @@ import ast import contextlib +import functools import tempfile import typing from pathlib import Path from types import SimpleNamespace +import tomlkit +from cookiecutter.main import cookiecutter + if typing.TYPE_CHECKING: from collections.abc import Generator from typing import Self -from cookiecutter.main import cookiecutter DIR = Path(__file__).parent.resolve() @@ -29,17 +32,17 @@ def render_cookie(**context: str) -> Generator[Path, None, None]: yield Path(tmpdir).joinpath("package").resolve() -class Matcher: - def __init__(self, txt: str) -> None: +class PyMatcher: + def __init__(self, txt: str, /) -> None: self.ast = ast.parse(txt) self.lines = txt.splitlines() @classmethod - def from_file(cls, filename: Path) -> Self: + def from_file(cls, filename: Path, /) -> Self: with filename.open(encoding="utf-8") as f: return cls(f.read()) - def get_source(self, name: str) -> str: + def get_source(self, name: str, /) -> str: o = SimpleNamespace(name=name) for item in self.ast.body: match item: @@ -53,6 +56,24 @@ def get_source(self, name: str) -> str: raise RuntimeError(msg) +class TOMLMatcher: + def __init__(self, txt: str, /) -> None: + self.toml = tomlkit.loads(txt) + + @classmethod + def from_file(cls, filename: Path, /) -> Self: + with filename.open(encoding="utf-8") as f: + return cls(f.read()) + + def get_source(self, dotted_name: str, /) -> str: + names = dotted_name.split(".") + toml_inner = functools.reduce(lambda d, k: d[k], names, self.toml) + toml = functools.reduce( + lambda d, k: tomlkit.table().add(k, d), reversed(names), toml_inner + ) + return tomlkit.dumps(toml).strip() + + @contextlib.contextmanager def code_fence(lang: str, /, *, width: int = 3) -> Generator[None, None, None]: tics = "`" * width diff --git a/pyproject.toml b/pyproject.toml index 6783a099..245ca332 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,6 +96,7 @@ test = [ cog = [ "cogapp", "cookiecutter", + "tomlkit", ] [tool.hatch]