diff --git a/{{ cookiecutter.__project_name_kebab_case }}/.github/workflows/test.yml b/{{ cookiecutter.__project_name_kebab_case }}/.github/workflows/test.yml index b436a01..a67e33a 100644 --- a/{{ cookiecutter.__project_name_kebab_case }}/.github/workflows/test.yml +++ b/{{ cookiecutter.__project_name_kebab_case }}/.github/workflows/test.yml @@ -40,8 +40,3 @@ jobs: - name: Test {{ cookiecutter.project_type }} run: devcontainer exec --workspace-folder . poe test - - - name: Upload coverage - uses: codecov/codecov-action@v5 - with: - files: reports/coverage.xml diff --git a/{{ cookiecutter.__project_name_kebab_case }}/.pre-commit-config.yaml b/{{ cookiecutter.__project_name_kebab_case }}/.pre-commit-config.yaml index c665b51..6101881 100644 --- a/{{ cookiecutter.__project_name_kebab_case }}/.pre-commit-config.yaml +++ b/{{ cookiecutter.__project_name_kebab_case }}/.pre-commit-config.yaml @@ -16,13 +16,14 @@ repos: - id: rst-inline-touching-normal - id: text-unicode-replacement-char - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: check-added-large-files - id: check-ast - id: check-builtin-literals - id: check-case-conflict - id: check-docstring-first + - id: check-illegal-windows-names - id: check-json - id: check-merge-conflict - id: check-shebang-scripts-are-executable diff --git a/{{ cookiecutter.__project_name_kebab_case }}/Dockerfile b/{{ cookiecutter.__project_name_kebab_case }}/Dockerfile index 88aa063..1d2e38e 100644 --- a/{{ cookiecutter.__project_name_kebab_case }}/Dockerfile +++ b/{{ cookiecutter.__project_name_kebab_case }}/Dockerfile @@ -37,7 +37,7 @@ FROM base AS poetry USER root # Install Poetry in separate venv so it doesn't pollute the main venv. -ENV POETRY_VERSION 1.8.0 +ENV POETRY_VERSION 2.0.1 ENV POETRY_VIRTUAL_ENV /opt/poetry-env RUN --mount=type=cache,target=/root/.cache/pip/ \ python -m venv $POETRY_VIRTUAL_ENV && \ @@ -60,7 +60,7 @@ RUN --mount=type=cache,uid=$UID,gid=$GID,target=/home/user/.cache/pypoetry/ \ {%- if cookiecutter.private_package_repository_name %} --mount=type=secret,id=poetry-auth,uid=$UID,gid=$GID,target=/home/user/.config/pypoetry/auth.toml \ {%- endif %} - poetry install --only main --all-extras --no-interaction + poetry install --without test,dev --all-extras --no-interaction @@ -92,7 +92,7 @@ RUN mkdir -p /opt/build/poetry/ && cp poetry.lock /opt/build/poetry/ && \ mkdir -p /opt/build/git/ && cp .git/hooks/commit-msg .git/hooks/pre-commit /opt/build/git/ # Configure the non-root user's shell. -ENV ANTIDOTE_VERSION 1.8.6 +ENV ANTIDOTE_VERSION 1.9.7 RUN git clone --branch v$ANTIDOTE_VERSION --depth=1 https://github.com/mattmc3/antidote.git ~/.antidote/ && \ echo 'zsh-users/zsh-syntax-highlighting' >> ~/.zsh_plugins.txt && \ echo 'zsh-users/zsh-autosuggestions' >> ~/.zsh_plugins.txt && \ diff --git a/{{ cookiecutter.__project_name_kebab_case }}/pyproject.toml b/{{ cookiecutter.__project_name_kebab_case }}/pyproject.toml index 372a2e2..f547bb4 100644 --- a/{{ cookiecutter.__project_name_kebab_case }}/pyproject.toml +++ b/{{ cookiecutter.__project_name_kebab_case }}/pyproject.toml @@ -2,70 +2,72 @@ requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" -[tool.poetry] # https://python-poetry.org/docs/pyproject/ +[project] # https://packaging.python.org/en/latest/specifications/pyproject-toml/ name = "{{ cookiecutter.__project_name_kebab_case }}" version = "0.0.0" description = "{{ cookiecutter.project_description }}" -authors = ["{{ cookiecutter.author_name }} <{{ cookiecutter.author_email }}>"] readme = "README.md" -repository = "{{ cookiecutter.project_url }}" -{%- if cookiecutter.with_conventional_commits|int %} - -[tool.commitizen] # https://commitizen-tools.github.io/commitizen/config/ -bump_message = "bump(release): v$current_version → v$new_version" -tag_format = "v$version" -update_changelog_on_bump = true -version_provider = "poetry" -{%- endif %} +license-files = ["LICENSE*"] +authors = [ + { name = "{{ cookiecutter.author_name }}", email = "{{ cookiecutter.author_email }}" }, +] +requires-python = ">={{ cookiecutter.python_version }},<4.0" +dependencies = [ + {%- if cookiecutter.with_fastapi_api|int %} + "fastapi[all] (>=0.115.6)", + "gunicorn (>=23.0.0)", + {%- endif %} + {%- if cookiecutter.project_type == "app" %} + "poethepoet (>=0.32.1)", + {%- endif %} + {%- if cookiecutter.with_typer_cli|int %} + "typer[all] (>=0.15.1)", + {%- endif %} + {%- if cookiecutter.with_fastapi_api|int %} + "uvicorn[standard] (>=0.34.0)", + {%- endif %} +] {%- if cookiecutter.with_typer_cli|int %} -[tool.poetry.scripts] # https://python-poetry.org/docs/pyproject/#scripts +[project.scripts] # https://python-poetry.org/docs/pyproject/#scripts {{ cookiecutter.__project_name_kebab_case }} = "{{ cookiecutter.__project_name_snake_case }}.cli:app" {%- endif %} -[tool.poetry.dependencies] # https://python-poetry.org/docs/dependency-specification/ -{%- if cookiecutter.with_fastapi_api|int %} -coloredlogs = ">=15.0.1" -fastapi = { extras = ["all"], version = ">=0.110.1" } -gunicorn = ">=21.2.0" -{%- endif %} -{%- if cookiecutter.project_type == "app" %} -poethepoet = ">=0.25.0" -{%- endif %} -python = ">={{ cookiecutter.python_version }},<4.0" -{%- if cookiecutter.with_typer_cli|int %} -typer = { extras = ["all"], version = ">=0.12.0" } -{%- endif %} -{%- if cookiecutter.with_fastapi_api|int %} -uvicorn = { extras = ["standard"], version = ">=0.29.0" } +[project.urls] # https://packaging.python.org/en/latest/specifications/well-known-project-urls/#well-known-labels +homepage = "{{ cookiecutter.project_url }}" +source = "{{ cookiecutter.project_url }}" +{%- if cookiecutter.with_conventional_commits|int %} +changelog = "{{ cookiecutter.project_url }}/{% if cookiecutter.continuous_integration == "GitLab" %}-/{% endif %}blob/main/CHANGELOG.md" {%- endif %} +releasenotes = "{{ cookiecutter.project_url }}/{% if cookiecutter.continuous_integration == "GitLab" %}-/{% endif %}releases" +documentation = "{{ cookiecutter.project_url }}" +issues = "{{ cookiecutter.project_url }}/{% if cookiecutter.continuous_integration == "GitLab" %}-/{% endif %}issues" -[tool.poetry.group.test.dependencies] # https://python-poetry.org/docs/master/managing-dependencies/ +[tool.poetry.group.test.dependencies] # https://python-poetry.org/docs/managing-dependencies#dependency-groups {%- if cookiecutter.with_conventional_commits|int %} -commitizen = ">=3.21.3" +commitizen = ">=4.1.0" {%- endif %} -coverage = { extras = ["toml"], version = ">=7.4.4" } -mypy = ">=1.9.0" +coverage = { extras = ["toml"], version = ">=7.6.10" } +mypy = ">=1.14.1" {%- if cookiecutter.project_type == "package" %} -poethepoet = ">=0.25.0" +poethepoet = ">=0.32.1" {%- endif %} -pre-commit = ">=3.7.0" -pytest = ">=8.1.1" +pre-commit = ">=4.0.1" +pytest = ">=8.3.4" pytest-mock = ">=3.14.0" -pytest-xdist = ">=3.5.0" -ruff = ">=0.5.7" +pytest-xdist = ">=3.6.1" +ruff = ">=0.9.2" {%- if cookiecutter.development_environment == "strict" %} -safety = ">=3.1.0" shellcheck-py = ">=0.10.0.1" -typeguard = ">=4.2.1" +typeguard = ">=4.4.1" {%- endif %} -[tool.poetry.group.dev.dependencies] # https://python-poetry.org/docs/master/managing-dependencies/ -cruft = ">=2.15.0" +[tool.poetry.group.dev.dependencies] # https://python-poetry.org/docs/managing-dependencies#dependency-groups +cruft = ">=2.16.0" ipykernel = ">=6.29.4" ipython = ">=8.18.0" ipywidgets = ">=8.1.2" -pdoc = ">=14.4.0" +pdoc = ">=15.0.1" {%- if cookiecutter.private_package_repository_name %} [[tool.poetry.source]] @@ -77,6 +79,14 @@ name = "{{ cookiecutter.private_package_repository_name|slugify }}" url = "{{ cookiecutter.private_package_repository_url }}" priority = "explicit" {%- endif %} +{%- if cookiecutter.with_conventional_commits|int %} + +[tool.commitizen] # https://commitizen-tools.github.io/commitizen/config/ +bump_message = "bump(release): v$current_version → v$new_version" +tag_format = "v$version" +update_changelog_on_bump = true +version_provider = "poetry" +{%- endif %} [tool.coverage.report] # https://coverage.readthedocs.io/en/latest/config.html#report {%- if cookiecutter.development_environment == "strict" %} @@ -128,15 +138,18 @@ filterwarnings = ["error", "ignore::DeprecationWarning"] testpaths = ["src", "tests"] xfail_strict = true -[tool.ruff] # https://github.com/charliermarsh/ruff +[tool.ruff] # https://docs.astral.sh/ruff/settings/ fix = true line-length = 100 src = ["src", "tests"] target-version = "py{{ cookiecutter.python_version.split('.')[:2]|join }}" +[tool.ruff.format] +docstring-code-format = true + [tool.ruff.lint] {%- if cookiecutter.development_environment == "strict" %} -select = ["A", "ASYNC", "B", "BLE", "C4", "C90", "D", "DTZ", "E", "EM", "ERA", "F", "FBT", "FLY", "FURB", "G", "I", "ICN", "INP", "INT", "ISC", "LOG", "N", "NPY", "PERF", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "Q", "RET", "RSE", "RUF", "S", "SIM", "SLF", "SLOT", "T10", "T20", "TCH", "TID", "TRY", "UP", "W", "YTT"] +select = ["A", "ASYNC", "B", "BLE", "C4", "C90", "D", "DTZ", "E", "EM", "ERA", "F", "FBT", "FLY", "FURB", "G", "I", "ICN", "INP", "INT", "ISC", "LOG", "N", "NPY", "PERF", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "Q", "RET", "RSE", "RUF", "S", "SIM", "SLF", "SLOT", "T10", "T20", "TC", "TID", "TRY", "UP", "W", "YTT"] ignore = ["D203", "D213", "E501", "RET504", "S101", "S307"] unfixable = ["ERA001", "F401", "F841", "T201", "T203"] {%- else %} diff --git a/{{ cookiecutter.__project_name_kebab_case }}/src/{{ cookiecutter.__project_name_snake_case }}/api.py b/{{ cookiecutter.__project_name_kebab_case }}/src/{{ cookiecutter.__project_name_snake_case }}/api.py index 02bd6a9..32bf803 100644 --- a/{{ cookiecutter.__project_name_kebab_case }}/src/{{ cookiecutter.__project_name_snake_case }}/api.py +++ b/{{ cookiecutter.__project_name_kebab_case }}/src/{{ cookiecutter.__project_name_snake_case }}/api.py @@ -5,19 +5,15 @@ from collections.abc import AsyncGenerator from contextlib import asynccontextmanager -import coloredlogs from fastapi import FastAPI @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: """Handle FastAPI startup and shutdown events.""" - # Startup events: - # - Remove all handlers associated with the root logger object. + # Startup events. for handler in logging.root.handlers: logging.root.removeHandler(handler) - # - Add coloredlogs' colored StreamHandler to the root logger. - coloredlogs.install() yield # Shutdown events.