diff --git a/copier.yaml b/copier.yaml index c7f7910..87c063f 100644 --- a/copier.yaml +++ b/copier.yaml @@ -99,6 +99,18 @@ python_version_minimum: - '3.13' - '3.14' +python_version_maximum: + type: str + qmark: 🐍 + help: Maximum Python version + default: No maximum + choices: | + - 'No maximum' + {%- set min_minor = python_version_minimum.split(".")[1] | int -%} + {%- for minor in range(min_minor, 15) %} + - '3.{{ minor }}' + {%- endfor %} + enable_coverage: type: bool qmark: 📊 diff --git a/includes/python.jinja b/includes/python.jinja index 061bed4..b594b98 100644 --- a/includes/python.jinja +++ b/includes/python.jinja @@ -1,11 +1,27 @@ {%- set all_versions = ["3.10", "3.11", "3.12", "3.13", "3.14"] -%} {%- set min_minor = python_version_minimum.split(".")[1] | int -%} +{%- set max_minor = ( + python_version_maximum.split(".")[1] | int + if python_version_maximum != 'No maximum' else + (all_versions|last).split(".")[1] | int +) -%} {%- macro for_each_version() -%} -{%- for v in all_versions if v.split(".")[1] | int >= min_minor -%} +{%- for v in all_versions if ( + v.split(".")[1] | int >= min_minor and v.split(".")[1] | int <= max_minor +) -%} {{ caller(loop, v) }} {%- endfor -%} {%- endmacro -%} {%- macro latest() -%} -{{- all_versions|last -}} +{%- for v in all_versions if v.split(".")[1] | int <= max_minor -%} +{{ v if loop.last }} +{%- endfor -%} +{%- endmacro -%} + +{%- macro requires_python() -%} +>={{ python_version_minimum }} +{%- if python_version_maximum != 'No maximum' -%} +,<3.{{ max_minor + 1 }} +{%- endif %} {%- endmacro -%} diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index f5885be..75830f5 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -10,7 +10,7 @@ description = "{{ project_description }}" readme = "README.md" license = "{{ copyright_license }}" license-files = ["LICENSE"] -requires-python = ">={{ python_version_minimum }}" +requires-python = "{{ py.requires_python() }}" authors = [ { name = "{{ user_name }}", email = "{{ user_email }}" }, ] diff --git a/tests/__snapshots__/test_pyproject.ambr b/tests/__snapshots__/test_pyproject.ambr index d3c48a2..803fc64 100644 --- a/tests/__snapshots__/test_pyproject.ambr +++ b/tests/__snapshots__/test_pyproject.ambr @@ -1515,3 +1515,1263 @@ ''' # --- +# name: test_pyproject_versions[3.12-3.10] + ''' + [build-system] + requires = ["hatchling", "uv-dynamic-versioning"] + build-backend = "hatchling.build" + + [project] + name = "PKFire" + dynamic = ["version"] + description = "Onett Little League" + readme = "README.md" + license = "MIT" + license-files = ["LICENSE"] + requires-python = ">=3.10,<3.13" + authors = [ + { name = "Ness", email = "ness@onett.example.com" }, + ] + classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Documentation", + "Topic :: Software Development", + "Topic :: Utilities", + "Typing :: Typed", + ] + keywords = [] + dependencies = [] + + [project.urls] + Homepage = "https://ness.github.io/PKFire" + Repository = "https://github.com/ness/PKFire" + Issues = "https://github.com/ness/PKFire/issues" + + [dependency-groups] + dev = [ + "poethepoet>=0.42", + "prek>=0.3", + "pysentry-rs>=0.4", + "pytest>=9.0", + "pytest-cov>=7.0", + "pytest-sugar>=1.0", + "ruff>=0.15", + "ty>=0.0.26", + ] + docs = [ + "mkdocstrings[python]>=1", + "zensical>=0.0.30", + ] + + [tool.hatch.build.targets.sdist] + include = ["/pkfire", "/tests", "*.md", "LICENSE"] + + [tool.hatch.version] + source = "uv-dynamic-versioning" + + [tool.poe.executor] + type = "uv" + + [tool.poe.tasks.audit] + cmd = "pysentry-rs" + help = "Run dependencies security audit" + + [tool.poe.tasks.build] + cmd = "uv build" + help = "Build distribution package" + + [tool.poe.tasks.docs-build] + cmd = "zensical build -c" + help = "Build documentation site" + + [tool.poe.tasks.docs] + sequence = [ + {ref = "docs-build"}, + {cmd = "zensical serve -a \"${host}:${port}\""}, + ] + help = "Run documentation site locally" + + [tool.poe.tasks.docs.args.host] + options = ['--host', '-h'] + help = "Bind IP" + default = "localhost" + + [tool.poe.tasks.docs.args.port] + positional = true + help = "Bind Port" + default = "8000" + + [tool.poe.tasks.init] + sequence = [ + {shell = "[ ! -d .git ] || { echo \"git repository already exists\"; exit 1; }"}, + {cmd = "git init"}, + {ref = "setup"}, + {cmd = "git remote add origin https://github.com/ness/PKFire"}, + {cmd = "git config --local remote.origin.pushurl git@github.com:ness/PKFire.git"}, + {cmd = "git add ."}, + {cmd = "git commit -m 'Create project from template'"}, + ] + help = "Initialize git repository once on project creation" + + [tool.poe.tasks.lint] + cmd = "prek run --all-files" + help = "Run all formatters and static checks" + + [tool.poe.tasks.lt] + sequence = [{ref = "lint"}, {ref = "test"}] + help = "Run all formatters, static checks and tests" + + [tool.poe.tasks.setup] + sequence = [ + {cmd = "uv sync"}, + {cmd = "prek install"}, + ] + help = "Install project dependencies and git hooks" + + [tool.poe.tasks.test] + cmd = "pytest" + help = "Run tests" + + [tool.poe.tasks.test-versions] + sequence = ["test-py310", "test-py311", "test-py312"] + help = "Run tests across all Python versions" + + [tool.poe.tasks.test-py310] + cmd = "pytest" + executor = {isolated = true, python = "3.10"} + help = "Run tests using Python 3.10" + + [tool.poe.tasks.test-py311] + cmd = "pytest" + executor = {isolated = true, python = "3.11"} + help = "Run tests using Python 3.11" + + [tool.poe.tasks.test-py312] + cmd = "pytest" + executor = {isolated = true, python = "3.12"} + help = "Run tests using Python 3.12" + + [tool.pytest] + addopts = [ + "-ra", + "--cov", + "--strict-config", + "--strict-markers", + ] + testpaths = ["tests"] + + [tool.coverage.run] + source = ["pkfire"] + branch = true + + [tool.coverage.report] + show_missing = true + skip_covered = true + exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "@abstractmethod", + ] + + [tool.ruff] + line-length = 79 + output-format = "concise" + target-version = "py310" + + [tool.ruff.lint] + select = ["ALL"] + ignore = [ + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed + "COM812", # Trailing comma missing + "D1", # Missing docstring presence rules + "EM", # flake8-errmsg + "G004", # Logging statement uses f-string + "PLR", # pylint Refactor + "S607", # Starting a process with a partial executable path + "TD", # flake8-todos + "TRY003", # Avoid specifying long messages outside the exception class + ] + + [tool.ruff.lint.isort] + known-first-party = ["pkfire"] + + [tool.ruff.lint.per-file-ignores] + "tests/**/*.py" = [ + "ARG", # flake8-unused-arguments + "S101", # Use of assert detected + ] + + [tool.ruff.lint.pydocstyle] + convention = "google" + + [tool.ty.terminal] + error-on-warning = true + output-format = "concise" + + [tool.uv] + default-groups = ["dev", "docs"] + + [tool.uv-dynamic-versioning] + pattern = "default-unprefixed" + fallback-version = "0.0.0" + + ''' +# --- +# name: test_pyproject_versions[3.12-3.12] + ''' + [build-system] + requires = ["hatchling", "uv-dynamic-versioning"] + build-backend = "hatchling.build" + + [project] + name = "PKFire" + dynamic = ["version"] + description = "Onett Little League" + readme = "README.md" + license = "MIT" + license-files = ["LICENSE"] + requires-python = ">=3.12,<3.13" + authors = [ + { name = "Ness", email = "ness@onett.example.com" }, + ] + classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Topic :: Documentation", + "Topic :: Software Development", + "Topic :: Utilities", + "Typing :: Typed", + ] + keywords = [] + dependencies = [] + + [project.urls] + Homepage = "https://ness.github.io/PKFire" + Repository = "https://github.com/ness/PKFire" + Issues = "https://github.com/ness/PKFire/issues" + + [dependency-groups] + dev = [ + "poethepoet>=0.42", + "prek>=0.3", + "pysentry-rs>=0.4", + "pytest>=9.0", + "pytest-cov>=7.0", + "pytest-sugar>=1.0", + "ruff>=0.15", + "ty>=0.0.26", + ] + docs = [ + "mkdocstrings[python]>=1", + "zensical>=0.0.30", + ] + + [tool.hatch.build.targets.sdist] + include = ["/pkfire", "/tests", "*.md", "LICENSE"] + + [tool.hatch.version] + source = "uv-dynamic-versioning" + + [tool.poe.executor] + type = "uv" + + [tool.poe.tasks.audit] + cmd = "pysentry-rs" + help = "Run dependencies security audit" + + [tool.poe.tasks.build] + cmd = "uv build" + help = "Build distribution package" + + [tool.poe.tasks.docs-build] + cmd = "zensical build -c" + help = "Build documentation site" + + [tool.poe.tasks.docs] + sequence = [ + {ref = "docs-build"}, + {cmd = "zensical serve -a \"${host}:${port}\""}, + ] + help = "Run documentation site locally" + + [tool.poe.tasks.docs.args.host] + options = ['--host', '-h'] + help = "Bind IP" + default = "localhost" + + [tool.poe.tasks.docs.args.port] + positional = true + help = "Bind Port" + default = "8000" + + [tool.poe.tasks.init] + sequence = [ + {shell = "[ ! -d .git ] || { echo \"git repository already exists\"; exit 1; }"}, + {cmd = "git init"}, + {ref = "setup"}, + {cmd = "git remote add origin https://github.com/ness/PKFire"}, + {cmd = "git config --local remote.origin.pushurl git@github.com:ness/PKFire.git"}, + {cmd = "git add ."}, + {cmd = "git commit -m 'Create project from template'"}, + ] + help = "Initialize git repository once on project creation" + + [tool.poe.tasks.lint] + cmd = "prek run --all-files" + help = "Run all formatters and static checks" + + [tool.poe.tasks.lt] + sequence = [{ref = "lint"}, {ref = "test"}] + help = "Run all formatters, static checks and tests" + + [tool.poe.tasks.setup] + sequence = [ + {cmd = "uv sync"}, + {cmd = "prek install"}, + ] + help = "Install project dependencies and git hooks" + + [tool.poe.tasks.test] + cmd = "pytest" + help = "Run tests" + + [tool.poe.tasks.test-versions] + sequence = ["test-py312"] + help = "Run tests across all Python versions" + + [tool.poe.tasks.test-py312] + cmd = "pytest" + executor = {isolated = true, python = "3.12"} + help = "Run tests using Python 3.12" + + [tool.pytest] + addopts = [ + "-ra", + "--cov", + "--strict-config", + "--strict-markers", + ] + testpaths = ["tests"] + + [tool.coverage.run] + source = ["pkfire"] + branch = true + + [tool.coverage.report] + show_missing = true + skip_covered = true + exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "@abstractmethod", + ] + + [tool.ruff] + line-length = 79 + output-format = "concise" + target-version = "py312" + + [tool.ruff.lint] + select = ["ALL"] + ignore = [ + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed + "COM812", # Trailing comma missing + "D1", # Missing docstring presence rules + "EM", # flake8-errmsg + "G004", # Logging statement uses f-string + "PLR", # pylint Refactor + "S607", # Starting a process with a partial executable path + "TD", # flake8-todos + "TRY003", # Avoid specifying long messages outside the exception class + ] + + [tool.ruff.lint.isort] + known-first-party = ["pkfire"] + + [tool.ruff.lint.per-file-ignores] + "tests/**/*.py" = [ + "ARG", # flake8-unused-arguments + "S101", # Use of assert detected + ] + + [tool.ruff.lint.pydocstyle] + convention = "google" + + [tool.ty.terminal] + error-on-warning = true + output-format = "concise" + + [tool.uv] + default-groups = ["dev", "docs"] + + [tool.uv-dynamic-versioning] + pattern = "default-unprefixed" + fallback-version = "0.0.0" + + ''' +# --- +# name: test_pyproject_versions[3.14-3.10] + ''' + [build-system] + requires = ["hatchling", "uv-dynamic-versioning"] + build-backend = "hatchling.build" + + [project] + name = "PKFire" + dynamic = ["version"] + description = "Onett Little League" + readme = "README.md" + license = "MIT" + license-files = ["LICENSE"] + requires-python = ">=3.10,<3.15" + authors = [ + { name = "Ness", email = "ness@onett.example.com" }, + ] + classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "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 :: Documentation", + "Topic :: Software Development", + "Topic :: Utilities", + "Typing :: Typed", + ] + keywords = [] + dependencies = [] + + [project.urls] + Homepage = "https://ness.github.io/PKFire" + Repository = "https://github.com/ness/PKFire" + Issues = "https://github.com/ness/PKFire/issues" + + [dependency-groups] + dev = [ + "poethepoet>=0.42", + "prek>=0.3", + "pysentry-rs>=0.4", + "pytest>=9.0", + "pytest-cov>=7.0", + "pytest-sugar>=1.0", + "ruff>=0.15", + "ty>=0.0.26", + ] + docs = [ + "mkdocstrings[python]>=1", + "zensical>=0.0.30", + ] + + [tool.hatch.build.targets.sdist] + include = ["/pkfire", "/tests", "*.md", "LICENSE"] + + [tool.hatch.version] + source = "uv-dynamic-versioning" + + [tool.poe.executor] + type = "uv" + + [tool.poe.tasks.audit] + cmd = "pysentry-rs" + help = "Run dependencies security audit" + + [tool.poe.tasks.build] + cmd = "uv build" + help = "Build distribution package" + + [tool.poe.tasks.docs-build] + cmd = "zensical build -c" + help = "Build documentation site" + + [tool.poe.tasks.docs] + sequence = [ + {ref = "docs-build"}, + {cmd = "zensical serve -a \"${host}:${port}\""}, + ] + help = "Run documentation site locally" + + [tool.poe.tasks.docs.args.host] + options = ['--host', '-h'] + help = "Bind IP" + default = "localhost" + + [tool.poe.tasks.docs.args.port] + positional = true + help = "Bind Port" + default = "8000" + + [tool.poe.tasks.init] + sequence = [ + {shell = "[ ! -d .git ] || { echo \"git repository already exists\"; exit 1; }"}, + {cmd = "git init"}, + {ref = "setup"}, + {cmd = "git remote add origin https://github.com/ness/PKFire"}, + {cmd = "git config --local remote.origin.pushurl git@github.com:ness/PKFire.git"}, + {cmd = "git add ."}, + {cmd = "git commit -m 'Create project from template'"}, + ] + help = "Initialize git repository once on project creation" + + [tool.poe.tasks.lint] + cmd = "prek run --all-files" + help = "Run all formatters and static checks" + + [tool.poe.tasks.lt] + sequence = [{ref = "lint"}, {ref = "test"}] + help = "Run all formatters, static checks and tests" + + [tool.poe.tasks.setup] + sequence = [ + {cmd = "uv sync"}, + {cmd = "prek install"}, + ] + help = "Install project dependencies and git hooks" + + [tool.poe.tasks.test] + cmd = "pytest" + help = "Run tests" + + [tool.poe.tasks.test-versions] + sequence = ["test-py310", "test-py311", "test-py312", "test-py313", "test-py314"] + help = "Run tests across all Python versions" + + [tool.poe.tasks.test-py310] + cmd = "pytest" + executor = {isolated = true, python = "3.10"} + help = "Run tests using Python 3.10" + + [tool.poe.tasks.test-py311] + cmd = "pytest" + executor = {isolated = true, python = "3.11"} + help = "Run tests using Python 3.11" + + [tool.poe.tasks.test-py312] + cmd = "pytest" + executor = {isolated = true, python = "3.12"} + help = "Run tests using Python 3.12" + + [tool.poe.tasks.test-py313] + cmd = "pytest" + executor = {isolated = true, python = "3.13"} + help = "Run tests using Python 3.13" + + [tool.poe.tasks.test-py314] + cmd = "pytest" + executor = {isolated = true, python = "3.14"} + help = "Run tests using Python 3.14" + + [tool.pytest] + addopts = [ + "-ra", + "--cov", + "--strict-config", + "--strict-markers", + ] + testpaths = ["tests"] + + [tool.coverage.run] + source = ["pkfire"] + branch = true + + [tool.coverage.report] + show_missing = true + skip_covered = true + exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "@abstractmethod", + ] + + [tool.ruff] + line-length = 79 + output-format = "concise" + target-version = "py310" + + [tool.ruff.lint] + select = ["ALL"] + ignore = [ + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed + "COM812", # Trailing comma missing + "D1", # Missing docstring presence rules + "EM", # flake8-errmsg + "G004", # Logging statement uses f-string + "PLR", # pylint Refactor + "S607", # Starting a process with a partial executable path + "TD", # flake8-todos + "TRY003", # Avoid specifying long messages outside the exception class + ] + + [tool.ruff.lint.isort] + known-first-party = ["pkfire"] + + [tool.ruff.lint.per-file-ignores] + "tests/**/*.py" = [ + "ARG", # flake8-unused-arguments + "S101", # Use of assert detected + ] + + [tool.ruff.lint.pydocstyle] + convention = "google" + + [tool.ty.terminal] + error-on-warning = true + output-format = "concise" + + [tool.uv] + default-groups = ["dev", "docs"] + + [tool.uv-dynamic-versioning] + pattern = "default-unprefixed" + fallback-version = "0.0.0" + + ''' +# --- +# name: test_pyproject_versions[3.14-3.12] + ''' + [build-system] + requires = ["hatchling", "uv-dynamic-versioning"] + build-backend = "hatchling.build" + + [project] + name = "PKFire" + dynamic = ["version"] + description = "Onett Little League" + readme = "README.md" + license = "MIT" + license-files = ["LICENSE"] + requires-python = ">=3.12,<3.15" + authors = [ + { name = "Ness", email = "ness@onett.example.com" }, + ] + classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Documentation", + "Topic :: Software Development", + "Topic :: Utilities", + "Typing :: Typed", + ] + keywords = [] + dependencies = [] + + [project.urls] + Homepage = "https://ness.github.io/PKFire" + Repository = "https://github.com/ness/PKFire" + Issues = "https://github.com/ness/PKFire/issues" + + [dependency-groups] + dev = [ + "poethepoet>=0.42", + "prek>=0.3", + "pysentry-rs>=0.4", + "pytest>=9.0", + "pytest-cov>=7.0", + "pytest-sugar>=1.0", + "ruff>=0.15", + "ty>=0.0.26", + ] + docs = [ + "mkdocstrings[python]>=1", + "zensical>=0.0.30", + ] + + [tool.hatch.build.targets.sdist] + include = ["/pkfire", "/tests", "*.md", "LICENSE"] + + [tool.hatch.version] + source = "uv-dynamic-versioning" + + [tool.poe.executor] + type = "uv" + + [tool.poe.tasks.audit] + cmd = "pysentry-rs" + help = "Run dependencies security audit" + + [tool.poe.tasks.build] + cmd = "uv build" + help = "Build distribution package" + + [tool.poe.tasks.docs-build] + cmd = "zensical build -c" + help = "Build documentation site" + + [tool.poe.tasks.docs] + sequence = [ + {ref = "docs-build"}, + {cmd = "zensical serve -a \"${host}:${port}\""}, + ] + help = "Run documentation site locally" + + [tool.poe.tasks.docs.args.host] + options = ['--host', '-h'] + help = "Bind IP" + default = "localhost" + + [tool.poe.tasks.docs.args.port] + positional = true + help = "Bind Port" + default = "8000" + + [tool.poe.tasks.init] + sequence = [ + {shell = "[ ! -d .git ] || { echo \"git repository already exists\"; exit 1; }"}, + {cmd = "git init"}, + {ref = "setup"}, + {cmd = "git remote add origin https://github.com/ness/PKFire"}, + {cmd = "git config --local remote.origin.pushurl git@github.com:ness/PKFire.git"}, + {cmd = "git add ."}, + {cmd = "git commit -m 'Create project from template'"}, + ] + help = "Initialize git repository once on project creation" + + [tool.poe.tasks.lint] + cmd = "prek run --all-files" + help = "Run all formatters and static checks" + + [tool.poe.tasks.lt] + sequence = [{ref = "lint"}, {ref = "test"}] + help = "Run all formatters, static checks and tests" + + [tool.poe.tasks.setup] + sequence = [ + {cmd = "uv sync"}, + {cmd = "prek install"}, + ] + help = "Install project dependencies and git hooks" + + [tool.poe.tasks.test] + cmd = "pytest" + help = "Run tests" + + [tool.poe.tasks.test-versions] + sequence = ["test-py312", "test-py313", "test-py314"] + help = "Run tests across all Python versions" + + [tool.poe.tasks.test-py312] + cmd = "pytest" + executor = {isolated = true, python = "3.12"} + help = "Run tests using Python 3.12" + + [tool.poe.tasks.test-py313] + cmd = "pytest" + executor = {isolated = true, python = "3.13"} + help = "Run tests using Python 3.13" + + [tool.poe.tasks.test-py314] + cmd = "pytest" + executor = {isolated = true, python = "3.14"} + help = "Run tests using Python 3.14" + + [tool.pytest] + addopts = [ + "-ra", + "--cov", + "--strict-config", + "--strict-markers", + ] + testpaths = ["tests"] + + [tool.coverage.run] + source = ["pkfire"] + branch = true + + [tool.coverage.report] + show_missing = true + skip_covered = true + exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "@abstractmethod", + ] + + [tool.ruff] + line-length = 79 + output-format = "concise" + target-version = "py312" + + [tool.ruff.lint] + select = ["ALL"] + ignore = [ + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed + "COM812", # Trailing comma missing + "D1", # Missing docstring presence rules + "EM", # flake8-errmsg + "G004", # Logging statement uses f-string + "PLR", # pylint Refactor + "S607", # Starting a process with a partial executable path + "TD", # flake8-todos + "TRY003", # Avoid specifying long messages outside the exception class + ] + + [tool.ruff.lint.isort] + known-first-party = ["pkfire"] + + [tool.ruff.lint.per-file-ignores] + "tests/**/*.py" = [ + "ARG", # flake8-unused-arguments + "S101", # Use of assert detected + ] + + [tool.ruff.lint.pydocstyle] + convention = "google" + + [tool.ty.terminal] + error-on-warning = true + output-format = "concise" + + [tool.uv] + default-groups = ["dev", "docs"] + + [tool.uv-dynamic-versioning] + pattern = "default-unprefixed" + fallback-version = "0.0.0" + + ''' +# --- +# name: test_pyproject_versions[No maximum-3.10] + ''' + [build-system] + requires = ["hatchling", "uv-dynamic-versioning"] + build-backend = "hatchling.build" + + [project] + name = "PKFire" + dynamic = ["version"] + description = "Onett Little League" + readme = "README.md" + license = "MIT" + license-files = ["LICENSE"] + requires-python = ">=3.10" + authors = [ + { name = "Ness", email = "ness@onett.example.com" }, + ] + classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "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 :: Documentation", + "Topic :: Software Development", + "Topic :: Utilities", + "Typing :: Typed", + ] + keywords = [] + dependencies = [] + + [project.urls] + Homepage = "https://ness.github.io/PKFire" + Repository = "https://github.com/ness/PKFire" + Issues = "https://github.com/ness/PKFire/issues" + + [dependency-groups] + dev = [ + "poethepoet>=0.42", + "prek>=0.3", + "pysentry-rs>=0.4", + "pytest>=9.0", + "pytest-cov>=7.0", + "pytest-sugar>=1.0", + "ruff>=0.15", + "ty>=0.0.26", + ] + docs = [ + "mkdocstrings[python]>=1", + "zensical>=0.0.30", + ] + + [tool.hatch.build.targets.sdist] + include = ["/pkfire", "/tests", "*.md", "LICENSE"] + + [tool.hatch.version] + source = "uv-dynamic-versioning" + + [tool.poe.executor] + type = "uv" + + [tool.poe.tasks.audit] + cmd = "pysentry-rs" + help = "Run dependencies security audit" + + [tool.poe.tasks.build] + cmd = "uv build" + help = "Build distribution package" + + [tool.poe.tasks.docs-build] + cmd = "zensical build -c" + help = "Build documentation site" + + [tool.poe.tasks.docs] + sequence = [ + {ref = "docs-build"}, + {cmd = "zensical serve -a \"${host}:${port}\""}, + ] + help = "Run documentation site locally" + + [tool.poe.tasks.docs.args.host] + options = ['--host', '-h'] + help = "Bind IP" + default = "localhost" + + [tool.poe.tasks.docs.args.port] + positional = true + help = "Bind Port" + default = "8000" + + [tool.poe.tasks.init] + sequence = [ + {shell = "[ ! -d .git ] || { echo \"git repository already exists\"; exit 1; }"}, + {cmd = "git init"}, + {ref = "setup"}, + {cmd = "git remote add origin https://github.com/ness/PKFire"}, + {cmd = "git config --local remote.origin.pushurl git@github.com:ness/PKFire.git"}, + {cmd = "git add ."}, + {cmd = "git commit -m 'Create project from template'"}, + ] + help = "Initialize git repository once on project creation" + + [tool.poe.tasks.lint] + cmd = "prek run --all-files" + help = "Run all formatters and static checks" + + [tool.poe.tasks.lt] + sequence = [{ref = "lint"}, {ref = "test"}] + help = "Run all formatters, static checks and tests" + + [tool.poe.tasks.setup] + sequence = [ + {cmd = "uv sync"}, + {cmd = "prek install"}, + ] + help = "Install project dependencies and git hooks" + + [tool.poe.tasks.test] + cmd = "pytest" + help = "Run tests" + + [tool.poe.tasks.test-versions] + sequence = ["test-py310", "test-py311", "test-py312", "test-py313", "test-py314"] + help = "Run tests across all Python versions" + + [tool.poe.tasks.test-py310] + cmd = "pytest" + executor = {isolated = true, python = "3.10"} + help = "Run tests using Python 3.10" + + [tool.poe.tasks.test-py311] + cmd = "pytest" + executor = {isolated = true, python = "3.11"} + help = "Run tests using Python 3.11" + + [tool.poe.tasks.test-py312] + cmd = "pytest" + executor = {isolated = true, python = "3.12"} + help = "Run tests using Python 3.12" + + [tool.poe.tasks.test-py313] + cmd = "pytest" + executor = {isolated = true, python = "3.13"} + help = "Run tests using Python 3.13" + + [tool.poe.tasks.test-py314] + cmd = "pytest" + executor = {isolated = true, python = "3.14"} + help = "Run tests using Python 3.14" + + [tool.pytest] + addopts = [ + "-ra", + "--cov", + "--strict-config", + "--strict-markers", + ] + testpaths = ["tests"] + + [tool.coverage.run] + source = ["pkfire"] + branch = true + + [tool.coverage.report] + show_missing = true + skip_covered = true + exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "@abstractmethod", + ] + + [tool.ruff] + line-length = 79 + output-format = "concise" + target-version = "py310" + + [tool.ruff.lint] + select = ["ALL"] + ignore = [ + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed + "COM812", # Trailing comma missing + "D1", # Missing docstring presence rules + "EM", # flake8-errmsg + "G004", # Logging statement uses f-string + "PLR", # pylint Refactor + "S607", # Starting a process with a partial executable path + "TD", # flake8-todos + "TRY003", # Avoid specifying long messages outside the exception class + ] + + [tool.ruff.lint.isort] + known-first-party = ["pkfire"] + + [tool.ruff.lint.per-file-ignores] + "tests/**/*.py" = [ + "ARG", # flake8-unused-arguments + "S101", # Use of assert detected + ] + + [tool.ruff.lint.pydocstyle] + convention = "google" + + [tool.ty.terminal] + error-on-warning = true + output-format = "concise" + + [tool.uv] + default-groups = ["dev", "docs"] + + [tool.uv-dynamic-versioning] + pattern = "default-unprefixed" + fallback-version = "0.0.0" + + ''' +# --- +# name: test_pyproject_versions[No maximum-3.12] + ''' + [build-system] + requires = ["hatchling", "uv-dynamic-versioning"] + build-backend = "hatchling.build" + + [project] + name = "PKFire" + dynamic = ["version"] + description = "Onett Little League" + readme = "README.md" + license = "MIT" + license-files = ["LICENSE"] + requires-python = ">=3.12" + authors = [ + { name = "Ness", email = "ness@onett.example.com" }, + ] + classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Documentation", + "Topic :: Software Development", + "Topic :: Utilities", + "Typing :: Typed", + ] + keywords = [] + dependencies = [] + + [project.urls] + Homepage = "https://ness.github.io/PKFire" + Repository = "https://github.com/ness/PKFire" + Issues = "https://github.com/ness/PKFire/issues" + + [dependency-groups] + dev = [ + "poethepoet>=0.42", + "prek>=0.3", + "pysentry-rs>=0.4", + "pytest>=9.0", + "pytest-cov>=7.0", + "pytest-sugar>=1.0", + "ruff>=0.15", + "ty>=0.0.26", + ] + docs = [ + "mkdocstrings[python]>=1", + "zensical>=0.0.30", + ] + + [tool.hatch.build.targets.sdist] + include = ["/pkfire", "/tests", "*.md", "LICENSE"] + + [tool.hatch.version] + source = "uv-dynamic-versioning" + + [tool.poe.executor] + type = "uv" + + [tool.poe.tasks.audit] + cmd = "pysentry-rs" + help = "Run dependencies security audit" + + [tool.poe.tasks.build] + cmd = "uv build" + help = "Build distribution package" + + [tool.poe.tasks.docs-build] + cmd = "zensical build -c" + help = "Build documentation site" + + [tool.poe.tasks.docs] + sequence = [ + {ref = "docs-build"}, + {cmd = "zensical serve -a \"${host}:${port}\""}, + ] + help = "Run documentation site locally" + + [tool.poe.tasks.docs.args.host] + options = ['--host', '-h'] + help = "Bind IP" + default = "localhost" + + [tool.poe.tasks.docs.args.port] + positional = true + help = "Bind Port" + default = "8000" + + [tool.poe.tasks.init] + sequence = [ + {shell = "[ ! -d .git ] || { echo \"git repository already exists\"; exit 1; }"}, + {cmd = "git init"}, + {ref = "setup"}, + {cmd = "git remote add origin https://github.com/ness/PKFire"}, + {cmd = "git config --local remote.origin.pushurl git@github.com:ness/PKFire.git"}, + {cmd = "git add ."}, + {cmd = "git commit -m 'Create project from template'"}, + ] + help = "Initialize git repository once on project creation" + + [tool.poe.tasks.lint] + cmd = "prek run --all-files" + help = "Run all formatters and static checks" + + [tool.poe.tasks.lt] + sequence = [{ref = "lint"}, {ref = "test"}] + help = "Run all formatters, static checks and tests" + + [tool.poe.tasks.setup] + sequence = [ + {cmd = "uv sync"}, + {cmd = "prek install"}, + ] + help = "Install project dependencies and git hooks" + + [tool.poe.tasks.test] + cmd = "pytest" + help = "Run tests" + + [tool.poe.tasks.test-versions] + sequence = ["test-py312", "test-py313", "test-py314"] + help = "Run tests across all Python versions" + + [tool.poe.tasks.test-py312] + cmd = "pytest" + executor = {isolated = true, python = "3.12"} + help = "Run tests using Python 3.12" + + [tool.poe.tasks.test-py313] + cmd = "pytest" + executor = {isolated = true, python = "3.13"} + help = "Run tests using Python 3.13" + + [tool.poe.tasks.test-py314] + cmd = "pytest" + executor = {isolated = true, python = "3.14"} + help = "Run tests using Python 3.14" + + [tool.pytest] + addopts = [ + "-ra", + "--cov", + "--strict-config", + "--strict-markers", + ] + testpaths = ["tests"] + + [tool.coverage.run] + source = ["pkfire"] + branch = true + + [tool.coverage.report] + show_missing = true + skip_covered = true + exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "@abstractmethod", + ] + + [tool.ruff] + line-length = 79 + output-format = "concise" + target-version = "py312" + + [tool.ruff.lint] + select = ["ALL"] + ignore = [ + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed + "COM812", # Trailing comma missing + "D1", # Missing docstring presence rules + "EM", # flake8-errmsg + "G004", # Logging statement uses f-string + "PLR", # pylint Refactor + "S607", # Starting a process with a partial executable path + "TD", # flake8-todos + "TRY003", # Avoid specifying long messages outside the exception class + ] + + [tool.ruff.lint.isort] + known-first-party = ["pkfire"] + + [tool.ruff.lint.per-file-ignores] + "tests/**/*.py" = [ + "ARG", # flake8-unused-arguments + "S101", # Use of assert detected + ] + + [tool.ruff.lint.pydocstyle] + convention = "google" + + [tool.ty.terminal] + error-on-warning = true + output-format = "concise" + + [tool.uv] + default-groups = ["dev", "docs"] + + [tool.uv-dynamic-versioning] + pattern = "default-unprefixed" + fallback-version = "0.0.0" + + ''' +# --- diff --git a/tests/test_pyproject.py b/tests/test_pyproject.py index 6674986..1f273aa 100644 --- a/tests/test_pyproject.py +++ b/tests/test_pyproject.py @@ -27,3 +27,21 @@ def test_pyproject_features( enable_docs=enable_docs, ) assert (rendered / "pyproject.toml").read_text() == snapshot + + +@pytest.mark.parametrize("python_version_minimum", ["3.10", "3.12"]) +@pytest.mark.parametrize( + "python_version_maximum", ["3.12", "3.14", "No maximum"] +) +def test_pyproject_versions( + render_template: Callable[..., Path], + snapshot: SnapshotAssertion, + *, + python_version_minimum: str, + python_version_maximum: str, +) -> None: + rendered = render_template( + python_version_minimum=python_version_minimum, + python_version_maximum=python_version_maximum, + ) + assert (rendered / "pyproject.toml").read_text() == snapshot