Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
37 changes: 37 additions & 0 deletions {{cookiecutter.project_slug}}/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Pre-commit hooks for code quality
# Uses prek (https://github.com/j178/prek) - a faster pre-commit alternative
repos:
- repo: builtin
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-toml
- id: check-merge-conflict
- id: detect-private-key

- repo: local
hooks:
- id: format
name: format code
entry: make format
language: system
types: [python]
pass_filenames: false

- id: lint
name: lint code
entry: make lint
language: system
types: [python]
pass_filenames: false
require_serial: true

- id: test
name: run tests
entry: make test
language: system
types: [python]
pass_filenames: false
require_serial: true
stages: [pre-push]
12 changes: 7 additions & 5 deletions {{cookiecutter.project_slug}}/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ endif
all:
@echo "Run my targets individually!"

.PHONY: dev
dev:
uv sync --group dev
uv run prek install

{%- if cookiecutter.entry_point %}
.PHONY: run
run:
Expand All @@ -32,11 +37,8 @@ lint:
uv sync --group lint
uv run ruff format --check && \
uv run ruff check && \
uv run pyright

{%- if cookiecutter.docstring_coverage %}
uv run interrogate -c pyproject.toml .
{%- endif %}
uv run pyright{% if cookiecutter.docstring_coverage %} && \
uv run interrogate -c pyproject.toml .{% endif %}

.PHONY: format
format:
Expand Down
55 changes: 41 additions & 14 deletions {{cookiecutter.project_slug}}/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ version = "{{ cookiecutter.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 Down Expand Up @@ -36,11 +35,8 @@ test = ["pytest", "pytest-cov", "pytest-timeout", "pretend", "coverage[toml]"]
lint = [
# NOTE: ruff is under active development, so we pin conservatively here
# and let Dependabot periodically perform this update.
"ruff ~= 0.12",
"pyright ~= 1.1.407",
"types-html5lib",
"types-requests",
"types-toml",
"ruff ~= 0.14.0",
"pyright ~= 1.1",
{%- if cookiecutter.docstring_coverage %}
"interrogate",
{%- endif %}
Expand All @@ -49,6 +45,7 @@ dev = [
{include-group = "doc"},
{include-group = "test"},
{include-group = "lint"},
"prek",
]

{% if cookiecutter.entry_point -%}
Expand All @@ -68,33 +65,58 @@ omit = ["{{ cookiecutter.__project_src_path }}/_cli.py"]

[tool.pyright]
include = ["src", "test"]
exclude = []
pythonVersion = "3.10"
typeCheckingMode = "strict"
reportUnusedImport = "warning"
reportUnusedVariable = "warning"
reportGeneralTypeIssues = "error"
typeCheckingMode = "strict"
reportMissingTypeStubs = true

[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 @@ -105,6 +127,11 @@ ignore-semiprivate = true
fail-under = 100
{%- endif %}

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

[tool.uv.sources]
{{ cookiecutter.project_slug }} = { workspace = true }

Expand Down