diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ac8c1fb8..5300f1a8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -73,10 +73,11 @@ repos: - python - pyi require_serial: true - - id: toml-sort - name: toml-sort - entry: pdm run toml-sort - language: system + - id: pyproject-fmt + name: pyproject-fmt + entry: pyproject-fmt + language: python + files: '(^|/)pyproject\.toml$' types: - toml - id: forbidden-files diff --git a/Makefile b/Makefile index 0a91577e..3bfb8f4c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: clean deepclean install dev prerequisites mypy ruff ruff-format toml-sort lint pre-commit test-run test build publish doc-autobuild doc-gen doc-mypy doc-coverage doc consistency +.PHONY: clean deepclean install dev prerequisites mypy ruff ruff-format pyproject-fmt lint pre-commit test-run test build publish doc-autobuild doc-gen doc-mypy doc-coverage doc consistency ######################################################################################## # Variables @@ -53,6 +53,7 @@ dev: # Install standalone tools prerequisites: + pipx install --force pyproject-fmt==2.1.4 pipx install --force ruff==0.5.0 ######################################################################################## @@ -71,12 +72,12 @@ ruff: ruff-format: ruff format --check . -# Check lint with toml-sort. -toml-sort: - pdm run toml-sort --check pyproject.toml +# Check lint with pyproject-fmt. +pyproject-fmt: + pyproject-fmt pyproject.toml # Check lint with all linters. -lint: mypy ruff ruff-format toml-sort +lint: mypy ruff ruff-format pyproject-fmt # Run pre-commit with autofix against all files. pre-commit: diff --git a/README.md b/README.md index 9790dabb..fe70d8b8 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ If you find this helpful, please consider [sponsorship](https://github.com/spons - Project setup and template update with [copier](https://copier.readthedocs.io/). - Manage dependencies and virtual environments with [pdm](https://pdm-project.org/). - Build with [setuptools](https://github.com/pypa/setuptools) and versioned with [setuptools-scm](https://github.com/pypa/setuptools_scm/). -- Lint with [pre-commit](https://pre-commit.com), [mypy](http://www.mypy-lang.org/), [ruff](https://github.com/charliermarsh/ruff), [toml-sort](https://github.com/pappasam/toml-sort) and [commitlint](https://commitlint.js.org/). +- Lint with [pre-commit](https://pre-commit.com), [mypy](http://www.mypy-lang.org/), [ruff](https://github.com/charliermarsh/ruff), [pyproject-fmt](https://github.com/tox-dev/pyproject-fmt) and [commitlint](https://commitlint.js.org/). - Test with [pytest](https://docs.pytest.org/) and [coverage](https://coverage.readthedocs.io) for threshold and reports. - Documentation with [sphinx](https://www.sphinx-doc.org/), the [furo](https://pradyunsg.me/furo) theme and [MyST parser](https://myst-parser.readthedocs.io/) for markdown. - Develop Command Line Interfaces with [typer](https://typer.tiangolo.com/). diff --git a/includes/sample.jinja b/includes/sample.jinja index 0d5a1f54..7376b65f 100644 --- a/includes/sample.jinja +++ b/includes/sample.jinja @@ -15,7 +15,7 @@ If you find this helpful, please consider [sponsorship](https://github.com/spons - Project setup and template update with [copier](https://copier.readthedocs.io/). - Manage dependencies and virtual environments with [pdm](https://pdm-project.org/). - Build with [setuptools](https://github.com/pypa/setuptools) and versioned with [setuptools-scm](https://github.com/pypa/setuptools_scm/). -- Lint with [pre-commit](https://pre-commit.com), [mypy](http://www.mypy-lang.org/), [ruff](https://github.com/charliermarsh/ruff), [toml-sort](https://github.com/pappasam/toml-sort) and [commitlint](https://commitlint.js.org/). +- Lint with [pre-commit](https://pre-commit.com), [mypy](http://www.mypy-lang.org/), [ruff](https://github.com/charliermarsh/ruff), [pyproject-fmt](https://github.com/tox-dev/pyproject-fmt) and [commitlint](https://commitlint.js.org/). - Test with [pytest](https://docs.pytest.org/) and [coverage](https://coverage.readthedocs.io) for threshold and reports. - Documentation with [sphinx](https://www.sphinx-doc.org/), the [furo](https://pradyunsg.me/furo) theme and [MyST parser](https://myst-parser.readthedocs.io/) for markdown. - Develop Command Line Interfaces with [typer](https://typer.tiangolo.com/). diff --git a/pdm.lock b/pdm.lock index c577405e..d352e0fd 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "doc", "lint", "test"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.2" -content_hash = "sha256:1f6e6ebed5b67bffe4d942e03647dcff880de3de58626977c9bd4770a948409d" +content_hash = "sha256:eb5f49b942ac05bcd535904ede8adfa94191ddf9459593bf2c34d729bd725e9f" [[package]] name = "alabaster" @@ -1121,20 +1121,6 @@ files = [ {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] -[[package]] -name = "toml-sort" -version = "0.23.1" -requires_python = ">=3.7,<4.0" -summary = "Toml sorting library" -groups = ["lint"] -dependencies = [ - "tomlkit>=0.11.2", -] -files = [ - {file = "toml_sort-0.23.1-py3-none-any.whl", hash = "sha256:69ae60de9c4d67478533697eb4119092e2b30ddffe5ca09bbad3912905c935a0"}, - {file = "toml_sort-0.23.1.tar.gz", hash = "sha256:833728c48b0f8d509aecd9ae8347768ca3a9332977b32c9fd2002932f0eb9c90"}, -] - [[package]] name = "tomli" version = "2.0.1" @@ -1147,17 +1133,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "tomlkit" -version = "0.12.5" -requires_python = ">=3.7" -summary = "Style preserving TOML library" -groups = ["lint"] -files = [ - {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, - {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, -] - [[package]] name = "tornado" version = "6.4.1" diff --git a/pyproject.toml b/pyproject.toml index a089b28b..da02c085 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,100 +1,105 @@ [build-system] build-backend = "setuptools.build_meta" requires = [ - "setuptools-scm==8.1.0", - "setuptools==70.1.1", + "setuptools==70.1.1", + "setuptools-scm==8.1.0", ] [project] -authors = [ - {email = "i@huxuan.org", name = "huxuan"}, -] -classifiers = [ - "Development Status :: 3 - Alpha", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", -] -dependencies = [ - "pydantic-settings", - "typer[all]", -] +name = "ss-python" description = "An evolving Python project template that covers the full development lifecycle." -dynamic = [ - "version", -] +readme = "README.md" keywords = [ - "copier-template", - "full-development-lifecycle", - "project-template", - "serious-scaffold", + "copier-template", + "full-development-lifecycle", + "project-template", + "serious-scaffold", +] +license = { text = "MIT" } +authors = [ + { email = "i@huxuan.org", name = "huxuan" }, ] -license = {text = "MIT"} -name = "ss-python" -readme = "README.md" requires-python = ">=3.8" - -[project.scripts] -ss-python-cli = "ss_python.cli:app" - -[project.urls] -documentation = "https://serious-scaffold.github.io/ss-python" -issue = "https://github.com/serious-scaffold/ss-python/issues" -repository = "https://github.com/serious-scaffold/ss-python" - -[tool.coverage.report] -fail_under = 100 - -[tool.coverage.run] -source = [ - "ss_python", +classifiers = [ + "Development Status :: 3 - Alpha", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "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", ] - -[tool.mypy] -check_untyped_defs = true -disallow_any_unimported = true -disallow_untyped_defs = true -enable_error_code = [ - "ignore-without-code", +dynamic = [ + "version", ] -exclude = [ - "build", - "template", +dependencies = [ + "pydantic-settings", + "typer[all]", ] -no_implicit_optional = true -show_error_codes = true -warn_return_any = true -warn_unused_ignores = true +urls.documentation = "https://serious-scaffold.github.io/ss-python" +urls.issue = "https://github.com/serious-scaffold/ss-python/issues" +urls.repository = "https://github.com/serious-scaffold/ss-python" +scripts.ss-python-cli = "ss_python.cli:app" [tool.pdm] distribution = true [tool.pdm.dev-dependencies] doc = [ - "Sphinx", - "autodoc-pydantic", - "coverage", - "furo", - "mypy[reports]", - "myst-parser", - "pytest", - "sphinx-autobuild", - "sphinx-click", - "sphinx-design", + "Sphinx", + "autodoc-pydantic", + "coverage", + "furo", + "mypy[reports]", + "myst-parser", + "pytest", + "sphinx-autobuild", + "sphinx-click", + "sphinx-design", ] lint = [ - "mypy", - "toml-sort", + "mypy", ] test = [ - "coverage", - "pytest", + "coverage", + "pytest", ] +[tool.setuptools_scm] +fallback_version = "0.0.0" + +[tool.ruff] +src = [ + "src", +] +extend-exclude = [ + "template", +] +fix = true +lint.select = [ + "B", # flake8-bugbear + "D", # pydocstyle + "E", # pycodestyle error + "F", # Pyflakes + "I", # isort + "RUF100", # Unused noqa directive + "S", # flake8-bandit + "SIM", # flake8-simplify + "UP", # pyupgrade + "W", # pycodestyle warning +] +lint.per-file-ignores."tests/*" = [ + "S101", +] +lint.pydocstyle.convention = "google" + +[tool.pyproject-fmt] +indent = 4 +keep_full_version = true +max_supported_python = "3.12" + [tool.pytest.ini_options] addopts = "-l -s --durations=0" log_cli = true @@ -103,37 +108,26 @@ log_date_format = "%Y-%m-%d %H:%M:%S" log_format = "%(asctime)s %(levelname)s %(message)s" minversion = "6.0" -[tool.ruff] -extend-exclude = [ - "template", -] -fix = true -src = ["src"] +[tool.coverage.report] +fail_under = 100 -[tool.ruff.lint] -select = [ - "B", # flake8-bugbear - "D", # pydocstyle - "E", # pycodestyle error - "F", # Pyflakes - "I", # isort - "RUF100", # Unused noqa directive - "S", # flake8-bandit - "SIM", # flake8-simplify - "UP", # pyupgrade - "W", # pycodestyle warning +[tool.coverage.run] +source = [ + "ss_python", ] -[tool.ruff.lint.per-file-ignores] -"tests/*" = ["S101"] - -[tool.ruff.lint.pydocstyle] -convention = "google" - -[tool.setuptools_scm] -fallback_version = "0.0.0" - -[tool.tomlsort] -all = true -in_place = true -trailing_comma_inline_array = true +[tool.mypy] +check_untyped_defs = true +disallow_any_unimported = true +disallow_untyped_defs = true +enable_error_code = [ + "ignore-without-code", +] +exclude = [ + "build", + "template", +] +no_implicit_optional = true +show_error_codes = true +warn_return_any = true +warn_unused_ignores = true diff --git a/template/.pre-commit-config.yaml.jinja b/template/.pre-commit-config.yaml.jinja index 917e9127..14e1875f 100644 --- a/template/.pre-commit-config.yaml.jinja +++ b/template/.pre-commit-config.yaml.jinja @@ -76,10 +76,11 @@ repos: - python - pyi require_serial: true - - id: toml-sort - name: toml-sort - entry: pdm run toml-sort - language: system + - id: pyproject-fmt + name: pyproject-fmt + entry: pyproject-fmt + language: python + files: '(^|/)pyproject\.toml$' types: - toml - id: forbidden-files diff --git a/template/Makefile.jinja b/template/Makefile.jinja index 07720aac..2a418dc1 100644 --- a/template/Makefile.jinja +++ b/template/Makefile.jinja @@ -1,5 +1,5 @@ [% from pathjoin("includes", "variable.jinja") import page_url with context -%] -.PHONY: clean deepclean install dev prerequisites mypy ruff ruff-format toml-sort lint pre-commit test-run test build publish doc-autobuild doc-gen doc-mypy doc-coverage doc +.PHONY: clean deepclean install dev prerequisites mypy ruff ruff-format pyproject-fmt lint pre-commit test-run test build publish doc-autobuild doc-gen doc-mypy doc-coverage doc [%- if project_name == "Serious Scaffold Python" %] consistency[% endif %] ######################################################################################## @@ -55,6 +55,7 @@ dev: # Install standalone tools prerequisites: + pipx install --force pyproject-fmt==2.1.4 pipx install --force ruff==0.5.0 ######################################################################################## @@ -73,12 +74,12 @@ ruff: ruff-format: ruff format --check . -# Check lint with toml-sort. -toml-sort: - pdm run toml-sort --check pyproject.toml +# Check lint with pyproject-fmt. +pyproject-fmt: + pyproject-fmt pyproject.toml # Check lint with all linters. -lint: mypy ruff ruff-format toml-sort +lint: mypy ruff ruff-format pyproject-fmt # Run pre-commit with autofix against all files. pre-commit: diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 630ad5a3..60efbe24 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -4,179 +4,173 @@ [build-system] build-backend = "setuptools.build_meta" requires = [ - "setuptools-scm==8.1.0", - "setuptools==70.1.1", + "setuptools==70.1.1", + "setuptools-scm==8.1.0", ] [project] +name = "{{ package_name }}" +description = "{{ project_description }}" +readme = "README.md" +keywords = [ + "copier-template", + "full-development-lifecycle", + "project-template", + "serious-scaffold", +] +[%- if copyright_license == "Apache Software License" %] +license = { text = "Apache-2.0" } +[%- elif copyright_license == "Boost Software License 1.0 (BSL-1.0)" %] +license = { text = "BSL-1.0" } +[%- elif copyright_license == "GNU Affero General Public License v3" %] +license = { text = "AGPLv3" } +[%- elif copyright_license == "GNU General Public License v3 (GPLv3)" %] +license = { text = "GPLv3" } +[%- elif copyright_license == "GNU Lesser General Public License v3 (LGPLv3)" %] +license = { text = "LGPLv3" } +[%- elif copyright_license == "MIT License" %] +license = { text = "MIT" } +[%- elif copyright_license == "Mozilla Public License 2.0 (MPL 2.0)" %] +license = { text = "MPL-2.0" } +[%- elif copyright_license == "The Unlicense (Unlicense)" %] +license = { text = "Unlicense" } +[%- endif %] authors = [ - {email = "{{ author_email }}", name = "{{ author_name }}"}, + { email = "{{ author_email }}", name = "{{ author_name }}" }, ] +requires-python = ">={{ min_py }}" classifiers = [ [%- if development_status == "Alpha" %] - "Development Status :: 3 - Alpha", + "Development Status :: 3 - Alpha", [%- elif development_status == "Beta" %] - "Development Status :: 4 - Beta", + "Development Status :: 4 - Beta", [%- elif development_status == "Stable" %] - "Development Status :: 5 - Production/Stable", + "Development Status :: 5 - Production/Stable", +[%- endif %] + "License :: OSI Approved :: {{ copyright_license }}", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3 :: Only", +[%- if version_between("3.8", min_py, max_py) %] + "Programming Language :: Python :: 3.8", +[%- endif %] +[%- if version_between("3.9", min_py, max_py) %] + "Programming Language :: Python :: 3.9", [%- endif %] - "License :: OSI Approved :: {{ copyright_license }}", - "Operating System :: OS Independent", [%- if version_between("3.10", min_py, max_py) %] - "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.10", [%- endif %] [%- if version_between("3.11", min_py, max_py) %] - "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.11", [%- endif %] [%- if version_between("3.12", min_py, max_py) %] - "Programming Language :: Python :: 3.12", -[%- endif %] -[%- if version_between("3.8", min_py, max_py) %] - "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.12", [%- endif %] -[%- if version_between("3.9", min_py, max_py) %] - "Programming Language :: Python :: 3.9", -[%- endif %] -] -dependencies = [ - "pydantic-settings", - "typer[all]", ] -description = "{{ project_description }}" dynamic = [ - "version", + "version", ] -keywords = [ - "copier-template", - "full-development-lifecycle", - "project-template", - "serious-scaffold", +dependencies = [ + "pydantic-settings", + "typer[all]", ] -[%- if copyright_license == "Apache Software License" %] -license = {text = "Apache-2.0"} -[%- elif copyright_license == "Boost Software License 1.0 (BSL-1.0)" %] -license = {text = "BSL-1.0"} -[%- elif copyright_license == "GNU Affero General Public License v3" %] -license = {text = "AGPLv3"} -[%- elif copyright_license == "GNU General Public License v3 (GPLv3)" %] -license = {text = "GPLv3"} -[%- elif copyright_license == "GNU Lesser General Public License v3 (LGPLv3)" %] -license = {text = "LGPLv3"} -[%- elif copyright_license == "MIT License" %] -license = {text = "MIT"} -[%- elif copyright_license == "Mozilla Public License 2.0 (MPL 2.0)" %] -license = {text = "MPL-2.0"} -[%- elif copyright_license == "The Unlicense (Unlicense)" %] -license = {text = "Unlicense"} -[%- endif %] -name = "{{ package_name }}" -readme = "README.md" -requires-python = ">={{ min_py }}" - -[project.scripts] -{{ package_name }}-cli = "{{ module_name }}.cli:app" - -[project.urls] -documentation = "https://{{ page_url() }}" +urls.documentation = "https://{{ page_url() }}" [% if repo_platform == 'github' -%] -issue = "https://{{ repo_url() }}/issues" +urls.issue = "https://{{ repo_url() }}/issues" [%- elif repo_platform == 'gitlab' or repo_platform == 'gitlab-self-managed' -%] -issue = "https://{{ repo_url() }}/-/issues" -[%- endif %] -repository = "https://{{ repo_url() }}" - -[tool.coverage.report] -fail_under = {{ coverage_threshold }} - -[tool.coverage.run] -source = [ - "{{ module_name }}", -] - -[tool.mypy] -check_untyped_defs = true -disallow_any_unimported = true -disallow_untyped_defs = true -enable_error_code = [ - "ignore-without-code", -] -exclude = [ - "build", -[%- if project_name == "Serious Scaffold Python" %] - "template", +urls.issue = "https://{{ repo_url() }}/-/issues" [%- endif %] -] -no_implicit_optional = true -show_error_codes = true -warn_return_any = true -warn_unused_ignores = true +urls.repository = "https://{{ repo_url() }}" +scripts.{{ package_name }}-cli = "{{ module_name }}.cli:app" [tool.pdm] distribution = true [tool.pdm.dev-dependencies] doc = [ - "Sphinx", - "autodoc-pydantic", - "coverage", - "furo", - "mypy[reports]", - "myst-parser", - "pytest", - "sphinx-autobuild", - "sphinx-click", - "sphinx-design", + "Sphinx", + "autodoc-pydantic", + "coverage", + "furo", + "mypy[reports]", + "myst-parser", + "pytest", + "sphinx-autobuild", + "sphinx-click", + "sphinx-design", ] lint = [ - "mypy", - "toml-sort", + "mypy", ] test = [ - "coverage", - "pytest", + "coverage", + "pytest", ] -[tool.pytest.ini_options] -addopts = "-l -s --durations=0" -log_cli = true -log_cli_level = "info" -log_date_format = "%Y-%m-%d %H:%M:%S" -log_format = "%(asctime)s %(levelname)s %(message)s" -minversion = "6.0" +[tool.setuptools_scm] +fallback_version = "0.0.0" [tool.ruff] -[%- if project_name == "Serious Scaffold Python" %] +src = [ + "src", +] +[% if project_name == "Serious Scaffold Python" -%] extend-exclude = [ - "template", + "template", ] [%- endif %] fix = true -src = ["src"] - -[tool.ruff.lint] -select = [ - "B", # flake8-bugbear - "D", # pydocstyle - "E", # pycodestyle error - "F", # Pyflakes - "I", # isort - "RUF100", # Unused noqa directive - "S", # flake8-bandit - "SIM", # flake8-simplify - "UP", # pyupgrade - "W", # pycodestyle warning +lint.select = [ + "B", # flake8-bugbear + "D", # pydocstyle + "E", # pycodestyle error + "F", # Pyflakes + "I", # isort + "RUF100", # Unused noqa directive + "S", # flake8-bandit + "SIM", # flake8-simplify + "UP", # pyupgrade + "W", # pycodestyle warning +] +lint.per-file-ignores."tests/*" = [ + "S101", ] +lint.pydocstyle.convention = "google" -[tool.ruff.lint.per-file-ignores] -"tests/*" = ["S101"] +[tool.pyproject-fmt] +indent = 4 +keep_full_version = true +max_supported_python = "{{ max_py }}" -[tool.ruff.lint.pydocstyle] -convention = "google" +[tool.pytest.ini_options] +addopts = "-l -s --durations=0" +log_cli = true +log_cli_level = "info" +log_date_format = "%Y-%m-%d %H:%M:%S" +log_format = "%(asctime)s %(levelname)s %(message)s" +minversion = "6.0" -[tool.setuptools_scm] -fallback_version = "0.0.0" +[tool.coverage.report] +fail_under = {{ coverage_threshold }} + +[tool.coverage.run] +source = [ + "{{ module_name }}", +] -[tool.tomlsort] -all = true -in_place = true -trailing_comma_inline_array = true +[tool.mypy] +check_untyped_defs = true +disallow_any_unimported = true +disallow_untyped_defs = true +enable_error_code = [ + "ignore-without-code", +] +exclude = [ + "build", +[%- if project_name == "Serious Scaffold Python" %] + "template", +[%- endif %] +] +no_implicit_optional = true +show_error_codes = true +warn_return_any = true +warn_unused_ignores = true