Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion hooks/pre_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
namespace_import = "{{ cookiecutter.project_namespace_import }}"
if namespace_import and not re.match(NAMESPACE_REGEX, namespace_import):
print(f"ERROR: '{namespace_import}' is not a valid Python namespace import path!")
print(f" It must follow regex '{NAMESPACE_REGEX}', i.e. 'one_two' or 'one_two.three'")
print(
f" It must follow regex '{NAMESPACE_REGEX}', i.e. 'one_two' or 'one_two.three'"
)
sys.exit(1)


Expand Down
3 changes: 2 additions & 1 deletion {{cookiecutter.project_slug}}/.github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ jobs:
strategy:
matrix:
python:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
Comment thread
DarkaMaul marked this conversation as resolved.
- "3.13"
- "3.14"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Expand Down
46 changes: 46 additions & 0 deletions {{cookiecutter.project_slug}}/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Pre-commit hooks for code quality
# See https://pre-commit.com for more information
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.8
hooks:
# Run the linter.
- id: ruff-check
args: [--fix]
# Run the formatter.
- id: ruff-format
Comment thread
DarkaMaul marked this conversation as resolved.
Outdated

- repo: local
hooks:
- id: pyright
name: pyright type check
entry: uv run pyright
language: system
types: [python]
pass_filenames: false
require_serial: true

{%- if cookiecutter.docstring_coverage %}
- id: interrogate
name: interrogate docstring coverage
entry: uv run interrogate -c pyproject.toml
language: system
types: [python]
pass_filenames: false
{%- endif %}

- id: pytest
name: pytest
entry: uv run pytest
language: system
types: [python]
pass_filenames: false
require_serial: true
# Only run on pre-commit, not on push
stages: [pre-commit]
Comment thread
DarkaMaul marked this conversation as resolved.
Outdated

# Configuration
ci:
autofix_prs: true
autoupdate_schedule: weekly
21 changes: 15 additions & 6 deletions {{cookiecutter.project_slug}}/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ all:

.PHONY: dev
dev: $(VENV)/pyvenv.cfg
uv run pre-commit install

{%- if cookiecutter.entry_point %}
.PHONY: run
Expand All @@ -59,17 +60,25 @@ $(VENV)/pyvenv.cfg: pyproject.toml
lint: $(VENV)/pyvenv.cfg
uv run ruff format --check && \
uv run ruff check && \
uv run mypy
uv run pyright{% if cookiecutter.docstring_coverage %} && \
uv run interrogate -c pyproject.toml .{% endif %}

{%- if cookiecutter.docstring_coverage %}
uv run interrogate -c pyproject.toml .
{%- endif %}
.PHONY: check
check: lint test

.PHONY: reformat
reformat:
.PHONY: typecheck
typecheck: $(VENV)/pyvenv.cfg
uv run pyright
Comment thread
ekilmer marked this conversation as resolved.
Outdated

.PHONY: fix
fix:
uv run ruff format && \
uv run ruff check --fix

# Alias for backwards compatibility
.PHONY: reformat
reformat: fix

.PHONY: test tests
test tests: $(VENV)/pyvenv.cfg
uv run pytest --cov=$(PY_IMPORT) $(T) $(TEST_ARGS)
Expand Down
76 changes: 47 additions & 29 deletions {{cookiecutter.project_slug}}/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ dynamic = ["version"]
description = "{{ cookiecutter.project_description }}"
readme = "README.md"
license-files = ["LICENSE"]

{%- if cookiecutter.license == "Apache 2.0" %}
license = "Apache-2.0"
{%- elif cookiecutter.license == "AGPL v3" %}
Expand All @@ -20,7 +19,7 @@ classifiers = [
"Programming Language :: Python :: 3",
]
dependencies = []
requires-python = ">=3.9"
requires-python = ">=3.10"

[tool.setuptools.dynamic]
version = { attr = "{{ cookiecutter.__project_import }}.__version__" }
Expand All @@ -35,16 +34,16 @@ test = ["pytest", "pytest-cov", "pretend", "coverage[toml]"]
lint = [
# NOTE: ruff is under active development, so we pin conservatively here
# and let Dependabot periodically perform this update.
"ruff ~= 0.6.2",
"mypy >= 1.0",
"ruff ~= 0.14.8",
"pyright >= 1.1",
Comment thread
Ninja3047 marked this conversation as resolved.
Outdated
"types-html5lib",
"types-requests",
"types-toml",
{%- if cookiecutter.docstring_coverage %}
"interrogate",
{%- endif %}
]
dev = ["{{ cookiecutter.project_slug }}[doc,test,lint]", "twine", "build"]
dev = ["{{ cookiecutter.project_slug }}[doc,test,lint]", "twine", "build", "pre-commit"]

{% if cookiecutter.entry_point -%}
[project.scripts]
Expand All @@ -61,46 +60,59 @@ Source = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.
# don't attempt code coverage for the CLI entrypoints
omit = ["{{ cookiecutter.__project_src_path }}/_cli.py"]

[tool.mypy]
mypy_path = "src"
packages = "{{ cookiecutter.__project_import }}"
allow_redefinition = true
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_defs = true
ignore_missing_imports = true
no_implicit_optional = true
show_error_codes = true
sqlite_cache = true
strict_equality = true
warn_no_return = true
warn_redundant_casts = true
warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
warn_unused_ignores = true
[tool.pyright]
# Type checking configuration
include = ["src", "test"]
pythonVersion = "3.10"
typeCheckingMode = "strict"
useLibraryCodeForTypes = true
reportMissingTypeStubs = false

[tool.ruff]
line-length = 100
include = ["src/**/*.py", "test/**/*.py"]
target-version = "py310"

[tool.ruff.format]
line-ending = "lf"
quote-style = "double"

[tool.ruff.lint]
select = ["ALL"]
# D203 and D213 are incompatible with D211 and D212 respectively.
# COM812 and ISC001 can cause conflicts when using ruff as a formatter.
# See https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules.
ignore = ["D203", "D213", "COM812", "ISC001"]
ignore = [
"D203", # Incompatible with D211
"D213", # Incompatible with D212
"COM812", # Can conflict with formatter
"ISC001", # Can conflict with formatter
]

[tool.ruff.lint.mccabe]
# Maximum cyclomatic complexity
max-complexity = 8

[tool.ruff.lint.pydocstyle]
# Use Google-style docstrings
convention = "google"

[tool.ruff.lint.pylint]
# Maximum number of branches for function or method
max-branches = 12
# Maximum number of return statements in function or method
max-returns = 6
# Maximum number of positional arguments for function or method
max-positional-args = 5

[tool.ruff.lint.per-file-ignores]
{% if cookiecutter.entry_point -%}
"{{ cookiecutter.__project_src_path }}/_cli.py" = [
"T201", # allow `print` in cli module
"T201", # allow print in cli module
]
{%- endif %}
"test/**/*.py" = [
"D", # no docstrings in tests
"S101", # asserts are expected in tests
"PLR2004", # Allow magic values in tests
]
"**/conftest.py" = ["D"] # No docstrings in pytest config

{%- if cookiecutter.docstring_coverage %}
[tool.interrogate]
Expand All @@ -110,3 +122,9 @@ exclude = ["env", "test", "{{ cookiecutter.__project_src_path }}/_cli.py"]
ignore-semiprivate = true
fail-under = 100
{%- endif %}

[tool.pytest.ini_options]
testpaths = ["test"]
python_files = ["test_*.py"]
# Show test durations
addopts = "--durations=10"