diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8956a36..fda07d6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -19,7 +19,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 - name: Set up Python uses: actions/setup-python@v6 @@ -32,7 +35,7 @@ jobs: sudo chmod +x /usr/local/bin/yq - name: Install cookiecutter - run: pip install cookiecutter + run: uv pip install --system cookiecutter - name: Run cookiecutter template run: | diff --git a/AGENTS.md b/AGENTS.md index df41210..1463a33 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,3 +7,7 @@ Since this is a Cookiecutter template you should expect to encounter Jinja2 temp When being asked to test functionality that requires you to create a new project from the template create them in the `workspaces` directory. When creating new files for optional services make sure you include them in the post_gen_project.py configuration so that unneeded files are removed. For example, if the caching functionality is not enabled the system should remove all of the caching related files. + +## Key Technologies + +This template uses **uv** for Python version management and package installation instead of pyenv and pip. uv provides 10-100x faster installations and automatically handles Python version downloads. All makefiles, dockerfiles, and CI workflows use uv. diff --git a/README.md b/README.md index 21870cf..69a92bc 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Transform any Python idea into a production-ready project in minutes. This Cooki - **AI Ready**: Pre-configured AGENTS.md file following the open standard compatible with Cursor, Aider, GitHub Copilot, and other AI coding assistants - **Incredibly Flexible**: Mix and match features to create exactly what you need, from simple libraries to full web applications with automatic cleanup of unused code - **Modern Python Standards**: Python 3.10+ with async/await support, type hints, contemporary best practices, and pyproject.toml configuration +- **Zero Manual Setup**: UV automatically downloads and installs the correct Python version - no need for pyenv or manual Python installation ## Quick Start @@ -34,7 +35,8 @@ Every project created with this template includes these essential components: - **[Makefile](https://www.gnu.org/software/make/manual/html_node/Introduction.html) automation**: Powerful task automation covering setup, testing, formatting, linting, and deployment with simple commands like `make install`, `make test`, and `make publish` - **Modern pyproject.toml**: Centralized project configuration using the modern Python standard, eliminating legacy setup.py files and consolidating all metadata, dependencies, and tool settings in one place -- **Virtual environment management**: Seamless virtual environment creation and activation with automatic [pyenv](https://github.com/pyenv/pyenv) integration for managing Python versions across development teams +- **[uv](https://docs.astral.sh/uv/)**: Lightning-fast Python package installer and virtual environment manager written in Rust, providing 10-100x faster installations than pip with automatic Python version management +- **Virtual environment management**: Seamless virtual environment creation using uv, automatically installing the correct Python version from `.python-version` and managing dependencies efficiently - **[Pre-commit](https://pre-commit.com/) hooks**: Automated code quality checks that run before every commit, catching formatting issues, security vulnerabilities, and common mistakes before they enter your repository ### Code Quality diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index eae75b6..e2b67a2 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -96,7 +96,6 @@ remove_paths.add(f'docs/dev/github.md') if not INCLUDE_REQUIREMENTS_FILES: - remove_paths.add('.github/workflows/lockfiles.yaml') remove_paths.add(f'docs/dev/dependencies.md') if not INCLUDE_AGENT_INSTRUCTIONS: @@ -132,6 +131,5 @@ def run_command(command): run_command('make all') -if INCLUDE_REQUIREMENTS_FILES: - run_command('make dependencies') +run_command('make lock') run_command('make chores') diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py index 38d63ee..8b30aac 100644 --- a/hooks/pre_gen_project.py +++ b/hooks/pre_gen_project.py @@ -1,4 +1,5 @@ import re +import shutil import sys MODULE_REGEX = r"^[_a-zA-Z][_a-zA-Z0-9]+$" @@ -7,6 +8,27 @@ if not re.match(MODULE_REGEX, module_name): print("ERROR: %s is not a valid Python module name!" % module_name) - # exits with status 1 to indicate failure sys.exit(1) + +# Check if UV is installed +if not shutil.which("uv"): + print("\n" + "=" * 80) + print("ERROR: UV is not installed!") + print("=" * 80) + print("\nThis template requires UV for Python package management.") + print("\nTo install UV, run one of the following commands:") + print("\n # On macOS and Linux:") + print(" curl -LsSf https://astral.sh/uv/install.sh | sh") + print("\n # Using pip:") + print(" pip install uv") + print("\n # Using pipx:") + print(" pipx install uv") + print("\n # Using Homebrew:") + print(" brew install uv") + print("\nFor more installation options, visit: https://docs.astral.sh/uv/getting-started/installation/") + print("\nAfter installing UV, you can regenerate this project using cookiecutter's replay feature:") + print(" cookiecutter --replay gh:tedivm/robs_awesome_python_template") + print("\nOr run the original cookiecutter command again.") + print("=" * 80 + "\n") + sys.exit(1) diff --git a/{{cookiecutter.__package_slug}}/.dockerignore b/{{cookiecutter.__package_slug}}/.dockerignore index c197f36..48f1ef5 100644 --- a/{{cookiecutter.__package_slug}}/.dockerignore +++ b/{{cookiecutter.__package_slug}}/.dockerignore @@ -12,4 +12,4 @@ !/pyproject.toml !/README.md !/setup.* -!/requirements* +!/uv.lock diff --git a/{{cookiecutter.__package_slug}}/.github/dependabot.yml b/{{cookiecutter.__package_slug}}/.github/dependabot.yml index 718572b..2eb714f 100644 --- a/{{cookiecutter.__package_slug}}/.github/dependabot.yml +++ b/{{cookiecutter.__package_slug}}/.github/dependabot.yml @@ -5,3 +5,37 @@ updates: directory: "/" schedule: interval: "weekly" + cooldown: + default-days: 7 + open-pull-requests-limit: 5 + rebase-strategy: auto + groups: + github-actions: + patterns: + - "*" + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + cooldown: + default-days: 7 + open-pull-requests-limit: 5 + rebase-strategy: auto + groups: + python-dependencies: + patterns: + - "*" + + - package-ecosystem: "uv" + directory: "/" + schedule: + interval: "weekly" + cooldown: + default-days: 7 + open-pull-requests-limit: 5 + rebase-strategy: auto + groups: + uv-dependencies: + patterns: + - "*" diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/alembic.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/alembic.yaml index 73f4ad9..9443579 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/alembic.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/alembic.yaml @@ -8,7 +8,11 @@ jobs: alembic: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + +{%- raw %} + - name: Install uv + uses: astral-sh/setup-uv@v7 - uses: actions/setup-python@v6 with: @@ -19,3 +23,4 @@ jobs: - name: Check for Un-Generated Migrations run: make check_ungenerated_migrations +{%- endraw %} diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/black.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/black.yaml index 568a82e..efbde50 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/black.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/black.yaml @@ -8,7 +8,10 @@ jobs: black: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 - uses: actions/setup-python@v6 with: diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/dapperdata.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/dapperdata.yaml index b2d8cd9..e83e5a3 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/dapperdata.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/dapperdata.yaml @@ -8,7 +8,10 @@ jobs: dapperdata: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 - uses: actions/setup-python@v6 with: diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/docker.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/docker.yaml index 5431d4c..e467268 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/docker.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/docker.yaml @@ -40,7 +40,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Check out the repo - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Log in to the Container registry uses: docker/login-action@v3 diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/lockfiles.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/lockfiles.yaml deleted file mode 100644 index 1627fb4..0000000 --- a/{{cookiecutter.__package_slug}}/.github/workflows/lockfiles.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: Update Dependencies and Push to Github - -on: - # Allow API to be hit to trigger workflow. - workflow_dispatch: - - # Every Monday at 1PM UTC (7AM EST) - schedule: - - cron: "0 11 * * 1" - -jobs: - push-update: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v5 - - - uses: actions/setup-python@v6 - with: - python-version-file: .python-version - - - name: "Update Lockfiles and Open PR" - uses: tedivm/action-python-lockfile-update@v2 - with: - pip_extras: "dev" - # This key will bypass workflow limitations to ensure tests are run. - # deploy_key: {% raw %}${{ secrets.WRITEABLE_DEPLOY_KEY }}{% endraw %} - - env: - # Needed to open pull request- automatically set for all actions. - GITHUB_TOKEN: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %} diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/mypy.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/mypy.yaml index 298aa43..899844e 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/mypy.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/mypy.yaml @@ -8,7 +8,10 @@ jobs: mypy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 - uses: actions/setup-python@v6 with: diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/paracelsus.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/paracelsus.yaml index b1dbfe2..a5670db 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/paracelsus.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/paracelsus.yaml @@ -8,7 +8,10 @@ jobs: paracelsus: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 - uses: actions/setup-python@v6 with: diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/pypi.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/pypi.yaml index 324e993..6de86c3 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/pypi.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/pypi.yaml @@ -21,11 +21,14 @@ jobs: permissions: id-token: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true + - name: Install uv + uses: astral-sh/setup-uv@v7 + - uses: actions/setup-python@v6 with: python-version-file: .python-version diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/pytest.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/pytest.yaml index 002f44b..9f7eec8 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/pytest.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/pytest.yaml @@ -15,7 +15,10 @@ jobs: matrix: version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 - uses: actions/setup-python@v6 with: diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/ruff.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/ruff.yaml index f3ea583..8e6d6ec 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/ruff.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/ruff.yaml @@ -8,7 +8,10 @@ jobs: ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 - uses: actions/setup-python@v6 with: diff --git a/{{cookiecutter.__package_slug}}/.github/workflows/tomlsort.yaml b/{{cookiecutter.__package_slug}}/.github/workflows/tomlsort.yaml index f4acc99..c31370d 100644 --- a/{{cookiecutter.__package_slug}}/.github/workflows/tomlsort.yaml +++ b/{{cookiecutter.__package_slug}}/.github/workflows/tomlsort.yaml @@ -8,7 +8,10 @@ jobs: tomlsort: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 - uses: actions/setup-python@v6 with: diff --git a/{{cookiecutter.__package_slug}}/AGENTS.md b/{{cookiecutter.__package_slug}}/AGENTS.md index 52aad30..0179529 100644 --- a/{{cookiecutter.__package_slug}}/AGENTS.md +++ b/{{cookiecutter.__package_slug}}/AGENTS.md @@ -4,6 +4,142 @@ You should always follow the best practices outlined in this document. If there Before beginning any task, make sure you review the documentation (`docs/dev/` and `README.md`), the existing tests to understand the project, and the task runner (Makefile) to understand what dev tools are available and how to use them. You should review code related to your request to understand preferred style: for example, you should review other tests before writing a new test suite, or review existing routers before creating a new one. +## Common Commands + +### Development Setup + +```bash +# Create virtual environment and install dependencies +make install + +# Update lockfile with latest compatible versions +make lock + +# Install updated dependencies +make sync +``` + +### Testing & Quality + +```bash +# Run test suite with coverage +make pytest + +# Run all quality checks (tests, type checking, linting, formatting) +make tests + +# Type checking with mypy +make mypy_check + +# Linting with ruff (check only) +make ruff_check + +# Format checking with ruff +make black_check + +# Auto-fix linting and formatting issues +make chores +``` + +### Code Formatting + +```bash +# Run all formatting fixes (ruff, dapperdata, toml-sort) +make chores + +# Fix ruff linting issues +make ruff_fixes + +# Format code with ruff +make black_fixes + +# Fix YAML/JSON formatting with dapperdata +make dapperdata_fixes + +# Sort TOML files +make tomlsort_fixes +``` + +### Dependency Management + +```bash +# Add a new dependency (edit pyproject.toml, then run) +make lock +make sync + +# Add a dev dependency (add to [dependency-groups] dev in pyproject.toml, then run) +make lock +make sync + +# Check if lockfile is up to date +make lock-check +``` + +{%- if cookiecutter.include_sqlalchemy == "y" %} + +### Database Operations + +```bash +# Run database migrations +make run_migrations + +# Create a new migration +make create_migration MESSAGE="description of changes" + +# Check for ungenerated migrations +make check_ungenerated_migrations + +# Reset database (clear and run migrations) +make reset_db + +# Clear database +make clear_db + +# Update database schema documentation +make document_schema +``` + +{%- endif %} + +{%- if cookiecutter.publish_to_pypi == "y" %} + +### Building & Packaging + +```bash +# Build package +make build +``` + +{%- endif %} + +### Using UV Directly + +```bash +# Run Python module +uv run python -m module_name + +# Run script +uv run python script.py + +# Run pytest +uv run pytest + +# Add package to project dependencies +uv add package_name + +# Remove package +uv remove package_name + +# Update all dependencies +uv lock --upgrade + +# Sync dependencies from lockfile +uv sync + +# Sync with dev dependencies +uv sync --group dev +``` + ## Best Practices ### General @@ -123,3 +259,4 @@ Before beginning any task, make sure you review the documentation (`docs/dev/` a * Developer settings should live in the `.env` file, which should be in `.gitignore`. * A `.env.example` file should exist as a template for new developers to create their `.env` file and learn what variables to set. * Python projects should always use virtual environments at `.venv` in the project root. This should be activated before running tests. +* Use `uv` for Python version management and package installation instead of pyenv and pip for significantly faster installations and automatic Python version handling. diff --git a/{{cookiecutter.__package_slug}}/dockerfile.celery b/{{cookiecutter.__package_slug}}/dockerfile.celery index 7adad18..3407895 100644 --- a/{{cookiecutter.__package_slug}}/dockerfile.celery +++ b/{{cookiecutter.__package_slug}}/dockerfile.celery @@ -3,12 +3,34 @@ FROM ghcr.io/multi-py/python-celery:py${PYTHON_VERSION}-slim-LATEST ENV APP_MODULE={{ cookiecutter.__package_slug }}.celery:celery -COPY requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt +# Install uv for fast package installation +RUN pip install --no-cache-dir uv {%- if cookiecutter.include_sqlalchemy == "y" %} RUN apt-get update && apt-get install -y netcat-traditional && rm -rf /var/lib/apt/lists/* {%- endif %} +# Configure UV to compile bytecode and skip dev dependencies +ENV UV_COMPILE_BYTECODE=1 +ENV UV_NO_DEV=1 +ENV UV_LINK_MODE=copy +ENV UV_TOOL_BIN_DIR=/usr/local/bin + +# Set working directory +WORKDIR /app + +# Add venv to PATH +ENV PATH="/app/.venv/bin:$PATH" + +# Copy dependency files +COPY pyproject.toml uv.lock /app/ + +# Install dependencies only (cached layer) +RUN uv sync --frozen --no-install-project --python /usr/local/bin/python + +# Copy application code COPY ./docker/celery/prestart.sh /app/prestart.sh -COPY ./ /app +COPY . /app/ + +# Install project +RUN uv sync --frozen --python /usr/local/bin/python diff --git a/{{cookiecutter.__package_slug}}/dockerfile.qq b/{{cookiecutter.__package_slug}}/dockerfile.qq index 3f62f94..b1691ae 100644 --- a/{{cookiecutter.__package_slug}}/dockerfile.qq +++ b/{{cookiecutter.__package_slug}}/dockerfile.qq @@ -3,7 +3,33 @@ FROM ghcr.io/multi-py/python-quasiqueue:py${PYTHON_VERSION}-slim-LATEST ENV MODULE_NAME={{ cookiecutter.__package_slug }}.qq -COPY requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt +# Install uv for fast package installation +RUN pip install --no-cache-dir uv -COPY ./ /app +{%- if cookiecutter.include_sqlalchemy == "y" %} +RUN apt-get update && apt-get install -y netcat-traditional && rm -rf /var/lib/apt/lists/* +{%- endif %} + +# Configure UV to compile bytecode and skip dev dependencies +ENV UV_COMPILE_BYTECODE=1 +ENV UV_NO_DEV=1 +ENV UV_LINK_MODE=copy +ENV UV_TOOL_BIN_DIR=/usr/local/bin + +# Set working directory +WORKDIR /app + +# Add venv to PATH +ENV PATH="/app/.venv/bin:$PATH" + +# Copy dependency files +COPY pyproject.toml uv.lock /app/ + +# Install dependencies only (cached layer) +RUN uv sync --frozen --no-install-project --python /usr/local/bin/python + +# Copy application code +COPY . /app/ + +# Install project +RUN uv sync --frozen --python /usr/local/bin/python diff --git a/{{cookiecutter.__package_slug}}/dockerfile.www b/{{cookiecutter.__package_slug}}/dockerfile.www index 37936c7..6a79a96 100644 --- a/{{cookiecutter.__package_slug}}/dockerfile.www +++ b/{{cookiecutter.__package_slug}}/dockerfile.www @@ -3,12 +3,34 @@ FROM ghcr.io/multi-py/python-uvicorn:py${PYTHON_VERSION}-slim-LATEST ENV APP_MODULE={{ cookiecutter.__package_slug }}.www:app -COPY requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt +# Install uv for fast package installation +RUN pip install --no-cache-dir uv {%- if cookiecutter.include_sqlalchemy == "y" %} RUN apt-get update && apt-get install -y netcat-traditional && rm -rf /var/lib/apt/lists/* {%- endif %} +# Configure UV to compile bytecode and skip dev dependencies +ENV UV_COMPILE_BYTECODE=1 +ENV UV_NO_DEV=1 +ENV UV_LINK_MODE=copy +ENV UV_TOOL_BIN_DIR=/usr/local/bin + +# Set working directory +WORKDIR /app + +# Add venv to PATH +ENV PATH="/app/.venv/bin:$PATH" + +# Copy dependency files +COPY pyproject.toml uv.lock /app/ + +# Install dependencies only (cached layer) +RUN uv sync --frozen --no-install-project --python /usr/local/bin/python + +# Copy application code COPY ./docker/www/prestart.sh /app/prestart.sh -COPY ./ /app +COPY . /app/ + +# Install project +RUN uv sync --frozen --python /usr/local/bin/python diff --git a/{{cookiecutter.__package_slug}}/docs/dev/dependencies.md b/{{cookiecutter.__package_slug}}/docs/dev/dependencies.md index 84befec..b98dcda 100644 --- a/{{cookiecutter.__package_slug}}/docs/dev/dependencies.md +++ b/{{cookiecutter.__package_slug}}/docs/dev/dependencies.md @@ -271,62 +271,77 @@ dependencies = [ {%- if cookiecutter.include_requirements_files == "y" %} -## Requirements Files +## Lockfile Management -This project can optionally generate `requirements.txt` files for compatibility with tools that don't support `pyproject.toml`: +This project uses `uv.lock` for deterministic, reproducible dependency resolution across all environments. -### Generate Requirements Files +### What is uv.lock? + +The `uv.lock` file is a cross-platform lockfile that: + +- **Pins exact versions** of all dependencies and transitive dependencies +- **Supports multiple platforms** (Linux, macOS, Windows, ARM64, AMD64) +- **Ensures reproducibility** across development, CI, and production +- **Faster than pip-tools** with built-in conflict resolution +- **Version controlled** - should be committed to git + +### Generate/Update Lockfile ```bash -# Generate both requirements files -make dependencies +# Update lockfile with latest compatible versions +make lock # Or manually: -make rebuild_dependencies -``` +uv lock --upgrade -This creates: - -- `requirements.txt`: Runtime dependencies only -- `requirements-dev.txt`: Runtime + development dependencies +# Check if lockfile is up-to-date +make lock-check +``` ### How It Works -Uses [uv](https://github.com/astral-sh/uv) for fast dependency resolution: +uv reads `pyproject.toml` and generates a comprehensive lockfile: ```bash -# Generate runtime requirements -uv pip compile --output-file=requirements.txt pyproject.toml +# Generate lockfile from pyproject.toml +uv lock -# Generate dev requirements -uv pip compile --extra=dev --output-file=requirements-dev.txt pyproject.toml -``` +# Update all dependencies to latest compatible versions +uv lock --upgrade -### When to Use Requirements Files - -**Use `pyproject.toml`** (preferred): +# Verify lockfile is in sync with pyproject.toml +uv lock --check +``` -- Modern Python projects -- Publishing to PyPI -- Editable installs (`pip install -e .`) +### Installing from Lockfile -**Use `requirements.txt`**: +**Development** (with dev dependencies): -- Legacy CI/CD systems -- Docker images (for layer caching optimization) -- Tools that don't support pyproject.toml -- Exact reproducible environments +```bash +# Install from lockfile with dev dependencies +make sync +# Or: uv sync --extra dev +``` -### Installing from Requirements Files +**Production** (no dev dependencies): ```bash -# Install runtime requirements -pip install -r requirements.txt +# Install from lockfile without dev dependencies +uv sync --frozen --no-dev -# Install dev requirements -pip install -r requirements-dev.txt +# Docker uses this for production images ``` +### Why use uv.lock? + +**Advantages over requirements.txt**: + +- **Multi-platform**: Single file works on Linux, macOS, Windows, ARM64, AMD64 +- **Faster resolution**: Rust-powered performance for lockfile generation +- **Better conflict detection**: Catches dependency conflicts earlier +- **Complete dependency tree**: Includes all transitive dependencies with hashes +- **Integrated workflow**: Seamless integration with uv commands + {%- endif %} ## Updating Dependencies @@ -334,32 +349,37 @@ pip install -r requirements-dev.txt ### Update All Dependencies ```bash -# Update all packages to latest compatible versions -pip install --upgrade -e .[dev] +# Update lockfile with latest compatible versions +make lock -# Verify updates -pip list --outdated +# Or manually +uv lock --upgrade + +# Then sync to install updated dependencies +make sync ``` {%- if cookiecutter.include_requirements_files == "y" %} -### Rebuild Requirements Files with Updates +### Regenerate Lockfile After Changes ```bash -# Force update of all dependencies in requirements files -make rebuild_dependencies +# After modifying pyproject.toml +make lock ``` {%- endif %} ### Update Specific Dependency +To update a specific package, modify its version constraint in `pyproject.toml`, then: + ```bash -# Update one package -pip install --upgrade package-name +# Update lockfile +make lock -# Verify new version -pip show package-name +# Install the update +make sync ``` ### Check for Outdated Packages @@ -413,13 +433,19 @@ See [GitHub Actions Documentation](./github.md) for more details. ### Creating a Virtual Environment ```bash -# Create virtual environment -python -m venv .venv +# Create virtual environment with uv (automatically installs Python if needed) +uv venv -# Or using make +# Or using make (recommended) make install ``` +uv will automatically: + +- Read the Python version from `.python-version` +- Download and install that Python version if not present +- Create a virtual environment in `.venv` + ### Activating the Virtual Environment ```bash @@ -430,21 +456,27 @@ source .venv/bin/activate .venv\Scripts\activate ``` -### Using pyenv for Python Version Management +### Using uv for Python Version Management -This project uses pyenv to manage Python versions: +This project uses uv to manage Python versions and virtual environments: ```bash -# Install Python version specified in .python-version -make pyenv +# uv automatically installs the correct Python version when creating venv +make install -# Or manually -pyenv install $(cat .python-version) +# Or manually create venv with specific Python version +uv venv --python $(cat .python-version) -# Set local Python version -pyenv local 3.11.0 +# uv will download and install Python if not present ``` +uv provides several advantages over traditional tools: + +- **Automatic Python installation**: No need for separate pyenv setup +- **10-100x faster**: Rust-powered performance for package installation +- **Drop-in pip replacement**: Compatible with existing workflows +- **Built-in virtual environments**: Integrated venv management + ### Checking Virtual Environment ```bash @@ -478,25 +510,27 @@ that are installed. This behavior is the source of the following dependency conf ### Troubleshooting Steps -1. **Update pip**: +1. **Update uv**: ```bash - pip install --upgrade pip + pip install --upgrade uv + # Or reinstall via the installer + curl -LsSf https://astral.sh/uv/install.sh | sh ``` 2. **Check for incompatible versions**: ```bash - pip check + uv pip check ``` 3. **Create fresh virtual environment**: ```bash rm -rf .venv - python -m venv .venv + uv venv source .venv/bin/activate - pip install -e .[dev] + uv pip install -e .[dev] ``` 4. **Install dependencies one at a time**: @@ -534,10 +568,10 @@ pip install -r requirements.lock ### Development Installation ```bash -# Install with all dev dependencies -pip install -e .[dev] +# Install with all dev dependencies using uv +uv pip install -e .[dev] -# Or use make +# Or use make (recommended) make install ``` @@ -551,10 +585,13 @@ make install ### Production Installation ```bash -# Install only runtime dependencies -pip install . +# Install only runtime dependencies using uv +uv pip install . # Or from PyPI +uv pip install {{cookiecutter.__package_slug}} + +# Traditional pip also works pip install {{cookiecutter.__package_slug}} ``` @@ -566,11 +603,14 @@ pip install {{cookiecutter.__package_slug}} ### Docker Production Images -Production Docker images should only install runtime dependencies: +Production Docker images use uv for faster installation: ```dockerfile +# Install uv for fast package installation +RUN pip install --no-cache-dir uv + # Install only runtime dependencies (no [dev]) -RUN pip install --no-cache-dir -r requirements.txt +RUN uv pip install --system --no-cache -r /requirements.txt ``` ## Optional Dependency Groups @@ -599,13 +639,13 @@ Install specific groups: ```bash # Install dev dependencies -pip install -e .[dev] +uv pip install -e .[dev] # Install multiple groups -pip install -e .[dev,docs] +uv pip install -e .[dev,docs] # Install all optional dependencies -pip install -e .[dev,docs,performance] +uv pip install -e .[dev,docs,performance] ``` ## Build System Configuration @@ -688,43 +728,81 @@ python -m build ```bash # Reinstall to pick up new dependencies -pip install -e .[dev] +uv pip install -e .[dev] # Verify package is installed -pip show package-name +uv pip show package-name ``` ### "No module named 'setuptools_scm'" ```bash -# Update pip and install build dependencies -pip install --upgrade pip setuptools wheel -pip install -e .[dev] +# Update uv and install build dependencies +pip install --upgrade uv +uv pip install -e .[dev] ``` -### Slow Dependency Resolution +### uv Not Found ```bash -# Use uv for faster dependency resolution +# Install uv via pip pip install uv -uv pip install -e .[dev] + +# Or use the standalone installer (recommended) +curl -LsSf https://astral.sh/uv/install.sh | sh + +# On Windows (PowerShell) +irm https://astral.sh/uv/install.ps1 | iex ``` ### Conflicting Dependencies ```bash # Show dependency tree -pip install pipdeptree +uv pip install pipdeptree pipdeptree # Find conflicts pipdeptree --warn conflicts ``` +## Why uv? + +This project uses [uv](https://docs.astral.sh/uv/) as the primary package manager for several compelling reasons: + +### Performance + +- **10-100x faster** than pip for package installation +- Written in Rust for maximum performance +- Parallel downloads and installations +- Advanced caching strategies + +### Convenience + +- **Automatic Python management**: Downloads and installs Python versions as needed +- **Drop-in pip replacement**: Compatible with existing pip commands +- **Integrated virtual environments**: Built-in venv management +- **Cross-platform**: Works on Linux, macOS, and Windows + +### Reliability + +- **Better dependency resolution**: More accurate conflict detection +- **Lockfile generation**: Create reproducible environments +- **Offline mode**: Cache packages for offline installation + +### Commands Comparison + +| Task | pip | uv | +|------|-----|-----| +| Install package | `pip install package` | `uv pip install package` | +| Create venv | `python -m venv .venv` | `uv venv` | +| Install Python | Requires pyenv/installer | `uv venv --python 3.14` (auto-downloads) | +| Compile requirements | Requires pip-tools | `uv pip compile` (built-in) | +| Speed | Baseline | 10-100x faster | + ## References - [PEP 621 - Project Metadata](https://peps.python.org/pep-0621/) - [Python Packaging User Guide](https://packaging.python.org/) -- [pip Documentation](https://pip.pypa.io/) -- [pyenv Documentation](https://github.com/pyenv/pyenv) -- [uv - Fast Python Package Installer](https://github.com/astral-sh/uv) +- [uv Documentation](https://docs.astral.sh/uv/) +- [uv GitHub Repository](https://github.com/astral-sh/uv) diff --git a/{{cookiecutter.__package_slug}}/docs/dev/docker.md b/{{cookiecutter.__package_slug}}/docs/dev/docker.md index 09f05d0..1d9bf37 100644 --- a/{{cookiecutter.__package_slug}}/docs/dev/docker.md +++ b/{{cookiecutter.__package_slug}}/docs/dev/docker.md @@ -28,11 +28,19 @@ FROM ghcr.io/multi-py/python-uvicorn:py${PYTHON_VERSION}-slim-LATEST ENV APP_MODULE={{ cookiecutter.__package_slug }}.www:app -COPY requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt +# Install uv for fast package installation +RUN pip install --no-cache-dir uv +# Copy dependency files +COPY pyproject.toml uv.lock /app/ +WORKDIR /app + +# Install dependencies from lockfile (no dev dependencies) +RUN uv sync --frozen --no-dev + +# Copy application code COPY ./docker/www/prestart.sh /app/prestart.sh -COPY ./ /app +COPY . /app/ ``` **Key Features**: @@ -65,11 +73,19 @@ FROM ghcr.io/multi-py/python-celery:py${PYTHON_VERSION}-slim-LATEST ENV APP_MODULE={{ cookiecutter.__package_slug }}.celery:celery -COPY requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt +# Install uv for fast package installation +RUN pip install --no-cache-dir uv + +# Copy dependency files +COPY pyproject.toml uv.lock /app/ +WORKDIR /app + +# Install dependencies from lockfile (no dev dependencies) +RUN uv sync --frozen --no-dev +# Copy application code COPY ./docker/celery/prestart.sh /app/prestart.sh -COPY ./ /app +COPY . /app/ ``` **Key Features**: @@ -94,12 +110,19 @@ The QuasiQueue image provides a containerized environment for running multiproce ARG PYTHON_VERSION={{ cookiecutter.__python_short_version }} FROM python:${PYTHON_VERSION}-slim -COPY requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt +# Install uv for fast package installation +RUN pip install --no-cache-dir uv -COPY ./ /app +# Copy dependency files +COPY pyproject.toml uv.lock /app/ WORKDIR /app +# Install dependencies from lockfile (no dev dependencies) +RUN uv sync --frozen --no-dev + +# Copy application code +COPY . /app/ + CMD ["python", "-m", "{{ cookiecutter.__package_slug }}.qq"] ``` @@ -430,9 +453,11 @@ FROM ghcr.io/multi-py/python-uvicorn:py{{ cookiecutter.__python_short_version }} # Install build dependencies RUN apt-get update && apt-get install -y gcc g++ make -# Install Python packages -COPY requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt +# Install uv and Python packages +RUN pip install --no-cache-dir uv +COPY pyproject.toml uv.lock /app/ +WORKDIR /app +RUN uv sync --frozen --no-dev # Final stage - copy only what's needed FROM ghcr.io/multi-py/python-uvicorn:py{{ cookiecutter.__python_short_version }}-slim-LATEST @@ -778,9 +803,14 @@ docker system prune -a --volumes 2. **Layer caching**: Order Dockerfile commands from least to most frequently changed ```dockerfile - COPY requirements.txt /requirements.txt - RUN pip install -r /requirements.txt - COPY ./ /app # Do this last + # Install uv first (changes rarely) + RUN pip install --no-cache-dir uv + # Install dependencies (changes when pyproject.toml or lockfile changes) + COPY pyproject.toml uv.lock /app/ + WORKDIR /app + RUN uv sync --frozen --no-dev + # Copy code (changes frequently) + COPY . /app/ ``` 3. **Don't run as root**: Use non-root users in production (Multi-Py images handle this) diff --git a/{{cookiecutter.__package_slug}}/docs/dev/github.md b/{{cookiecutter.__package_slug}}/docs/dev/github.md index 9b3179a..fcf4580 100644 --- a/{{cookiecutter.__package_slug}}/docs/dev/github.md +++ b/{{cookiecutter.__package_slug}}/docs/dev/github.md @@ -67,17 +67,6 @@ The project includes the following GitHub Actions workflows in `.github/workflow - **Command**: `make paracelsus_check` {%- endif %} -{%- if cookiecutter.include_requirements_files == "y" %} - -### Dependency Workflows - -**lockfiles.yaml** - Requirements File Validation - -- **Trigger**: Every push and pull request -- **Purpose**: Ensures requirements.txt files are synchronized with pyproject.toml -- **Command**: `make dependencies` -{%- endif %} - {%- if cookiecutter.include_docker == "y" %} ### Build and Deployment Workflows diff --git a/{{cookiecutter.__package_slug}}/docs/dev/makefile.md b/{{cookiecutter.__package_slug}}/docs/dev/makefile.md index ad23606..caf7382 100644 --- a/{{cookiecutter.__package_slug}}/docs/dev/makefile.md +++ b/{{cookiecutter.__package_slug}}/docs/dev/makefile.md @@ -86,9 +86,9 @@ make build **What it does**: -1. Installs the correct Python version using pyenv (local only) -2. Creates a virtual environment (`.venv`) -3. Installs the package and all development dependencies +1. Ensures uv is installed +2. Creates a virtual environment using uv (automatically installs Python if needed) +3. Installs the package and all development dependencies using uv **Usage**: @@ -103,40 +103,41 @@ make install **Notes**: - Safe to run multiple times (idempotent) -- In CI environments, skips pyenv and uses system Python +- uv automatically installs the correct Python version from `.python-version` - Creates `.venv` directory if it doesn't exist +- Much faster than traditional pip installation (10-100x speedup) -### `make pip` +### `make sync` **Purpose**: Install or update Python dependencies. **What it does**: -- Installs the package in editable mode with development extras +- Installs the package in editable mode with development extras using uv - Updates dependencies if `pyproject.toml` has changed **Usage**: ```bash # Update dependencies after pyproject.toml changes -make pip +make sync ``` -### `make pyenv` +### `make uv` -**Purpose**: Install the project's Python version using pyenv. +**Purpose**: Ensure uv is installed. **What it does**: -- Reads the Python version from `.python-version` -- Installs that version using pyenv -- Skips if the version is already installed +- Checks if uv is available on the system +- Installs uv via pip if not found +- This is automatically called by `make install` **Usage**: ```bash -# Install Python version (usually done automatically by `make install`) -make pyenv +# Ensure uv is installed (usually not needed directly) +make uv ``` ## Formatting Targets @@ -394,56 +395,55 @@ make dapperdata_check make tomlsort_check ``` -{%- if cookiecutter.include_requirements_files == "y" %} - ## Dependency Management -### `make dependencies` +### `make lock` -**Purpose**: Compile dependency lock files from `pyproject.toml`. +**Purpose**: Update the uv.lock lockfile with latest compatible versions. **What it does**: -- Generates `requirements.txt` from main dependencies -- Generates `requirements-dev.txt` including development dependencies -- Pins exact versions for reproducible installations -- Only runs if `pyproject.toml` has changed +- Reads `pyproject.toml` for dependency specifications +- Resolves all dependencies and transitive dependencies +- Generates cross-platform `uv.lock` with exact versions and hashes +- Uses `--upgrade` flag to get latest compatible versions **Usage**: ```bash -# Update lock files after changing pyproject.toml -make dependencies +# Update lockfile with latest dependencies +make lock + +# After modifying pyproject.toml +make lock ``` -**Files Generated**: +**File Generated**: -- `requirements.txt` - Production dependencies with pinned versions -- `requirements-dev.txt` - Development dependencies with pinned versions +- `uv.lock` - Cross-platform lockfile with all dependencies, versions, and hashes -### `make rebuild_dependencies` +### `make lock-check` -**Purpose**: Force rebuild of all dependency files with latest versions. +**Purpose**: Verify that uv.lock is up-to-date with pyproject.toml. **What it does**: -- Updates all dependencies to their latest compatible versions -- Regenerates both requirements files -- Uses `--upgrade` flag with uv +- Checks if lockfile needs updating +- Returns error if pyproject.toml changed without updating lock +- Useful in CI/CD to ensure lockfile is committed **Usage**: ```bash -# Update to latest dependency versions -make rebuild_dependencies +# Check if lockfile is current +make lock-check ``` **When to Use**: -- Monthly dependency updates -- After security vulnerability announcements +- In CI/CD pipelines to verify lockfile freshness +- Before committing changes to ensure lock is updated - When you want the latest compatible versions -{%- endif %} ## Packaging Targets @@ -636,9 +636,9 @@ The makefile respects several environment variables: **Effect**: -- Skips pyenv installation - Uses system Python instead of creating `.venv` - Adjusts paths for CI environment +- Still uses uv for fast dependency installation **Usage**: @@ -756,50 +756,27 @@ git commit -m "Add migration: describe changes" ``` {%- endif %} -{%- if cookiecutter.include_requirements_files == "y" %} ### Dependency Updates ```bash -# 1. Update pyproject.toml +# 1. Update pyproject.toml (if needed) # ... modify dependencies ... -# 2. Compile lock files -make dependencies +# 2. Update lockfile +make lock # 3. Install updated dependencies -make install +make sync # 4. Run tests to verify compatibility make tests # 5. Commit changes -git add pyproject.toml requirements*.txt +git add pyproject.toml uv.lock git commit -m "Update dependencies" ``` -### Monthly Maintenance - -```bash -# 1. Update to latest dependency versions -make rebuild_dependencies - -# 2. Install updated dependencies -make install - -# 3. Run full test suite -make tests - -# 4. Fix any breaking changes -# ... update code if needed ... - -# 5. Commit updates -git add requirements*.txt -git commit -m "Update dependencies to latest versions" -``` - -{%- endif %} - ### Pre-Release Checklist ```bash @@ -831,8 +808,8 @@ git push --tags The makefile automatically detects the environment: -- **Local Development**: Uses `.venv` and pyenv -- **CI Environment**: Uses system Python +- **Local Development**: Uses `.venv` with uv for Python version management +- **CI Environment**: Uses system Python with uv for fast dependency installation - **System Python Mode**: Skips virtual environment ### Target Dependencies @@ -862,8 +839,8 @@ All operational targets are marked as `.PHONY` to ensure they run even if files **Solution**: ```bash -# Install Python using pyenv -make pyenv +# uv will automatically install Python when you run +make install # Or install Python via your system package manager # Then run make install diff --git a/{{cookiecutter.__package_slug}}/makefile b/{{cookiecutter.__package_slug}}/makefile index 2077b97..a8d8cf1 100644 --- a/{{cookiecutter.__package_slug}}/makefile +++ b/{{cookiecutter.__package_slug}}/makefile @@ -3,13 +3,7 @@ SHELL := /bin/bash MIGRATION_DATABASE:=./migrate.db {% endif %} PACKAGE_SLUG={{cookiecutter.__package_slug}} -ifdef CI - PYTHON_PYENV := - PYTHON_VERSION := $(shell python --version|cut -d" " -f2) -else - PYTHON_PYENV := pyenv - PYTHON_VERSION := $(shell cat .python-version) -endif +PYTHON_VERSION := $(shell cat .python-version) PYTHON_SHORT_VERSION := $(shell echo $(PYTHON_VERSION) | grep -o '[0-9].[0-9]*') ifeq ($(USE_SYSTEM_PYTHON), true) @@ -17,14 +11,16 @@ ifeq ($(USE_SYSTEM_PYTHON), true) PYTHON_ENV := PYTHON := python PYTHON_VENV := + UV := uv else PYTHON_PACKAGE_PATH:=.venv/lib/python$(PYTHON_SHORT_VERSION)/site-packages PYTHON_ENV := . .venv/bin/activate && PYTHON := . .venv/bin/activate && python PYTHON_VENV := .venv + UV := uv endif -# Used to confirm that pip has run at least once +# Used to confirm that uv has run at least once PACKAGE_CHECK:=$(PYTHON_PACKAGE_PATH)/build PYTHON_DEPS := $(PACKAGE_CHECK) @@ -33,21 +29,24 @@ PYTHON_DEPS := $(PACKAGE_CHECK) all: $(PACKAGE_CHECK) .PHONY: install -install: $(PYTHON_PYENV) $(PYTHON_VENV) pip +install: uv $(PYTHON_VENV) sync .venv: - python -m venv .venv + $(UV) venv --python $(PYTHON_VERSION) + +.PHONY: uv +uv: + @command -v uv >/dev/null 2>&1 || { echo >&2 "uv is not installed. Installing via pip..."; pip install uv; } -.PHONY: pyenv -pyenv: - pyenv install --skip-existing $(PYTHON_VERSION) +.PHONY: sync +sync: $(PYTHON_VENV) uv.lock + $(UV) sync --group dev -.PHONY: pip -pip: $(PYTHON_VENV) - $(PYTHON) -m pip install -e .[dev] +$(PACKAGE_CHECK): $(PYTHON_VENV) uv.lock + $(UV) sync --group dev -$(PACKAGE_CHECK): $(PYTHON_VENV) - $(PYTHON) -m pip install -e .[dev] +uv.lock: pyproject.toml + $(UV) lock .PHONY: pre-commit pre-commit: @@ -67,15 +66,15 @@ chores: ruff_fixes black_fixes dapperdata_fixes tomlsort_fixes .PHONY: ruff_fixes ruff_fixes: - $(PYTHON) -m ruff check . --fix + $(UV) run ruff check . --fix .PHONY: black_fixes black_fixes: - $(PYTHON) -m ruff format . + $(UV) run ruff format . .PHONY: dapperdata_fixes dapperdata_fixes: - $(PYTHON) -m dapperdata.cli pretty . --no-dry-run + $(UV) run python -m dapperdata.cli pretty . --no-dry-run .PHONY: tomlsort_fixes tomlsort_fixes: @@ -95,27 +94,27 @@ tests: install pytest ruff_check black_check mypy_check dapperdata_check tomlsor .PHONY: pytest pytest: - $(PYTHON) -m pytest --cov=./${PACKAGE_SLUG} --cov-report=term-missing tests + $(UV) run pytest --cov=./${PACKAGE_SLUG} --cov-report=term-missing tests .PHONY: pytest_loud pytest_loud: - $(PYTHON) -m pytest --log-cli-level=DEBUG -log_cli=true --cov=./${PACKAGE_SLUG} --cov-report=term-missing tests + $(UV) run pytest --log-cli-level=DEBUG -log_cli=true --cov=./${PACKAGE_SLUG} --cov-report=term-missing tests .PHONY: ruff_check ruff_check: - $(PYTHON) -m ruff check + $(UV) run ruff check .PHONY: black_check black_check: - $(PYTHON) -m ruff format . --check + $(UV) run ruff format . --check .PHONY: mypy_check mypy_check: - $(PYTHON) -m mypy ${PACKAGE_SLUG} + $(UV) run mypy ${PACKAGE_SLUG} .PHONY: dapperdata_check dapperdata_check: - $(PYTHON) -m dapperdata.cli pretty . + $(UV) run python -m dapperdata.cli pretty . .PHONY: tomlsort_check tomlsort_check: @@ -123,34 +122,26 @@ tomlsort_check: -{%- if cookiecutter.include_requirements_files == "y" %} # # Dependencies # -.PHONY: rebuild_dependencies -rebuild_dependencies: - $(PYTHON) -m uv pip compile --upgrade --output-file=requirements.txt pyproject.toml - $(PYTHON) -m uv pip compile --upgrade --output-file=requirements-dev.txt --extra=dev pyproject.toml +.PHONY: lock +lock: + $(UV) lock --upgrade -.PHONY: dependencies -dependencies: requirements.txt requirements-dev.txt +.PHONY: lock-check +lock-check: + $(UV) lock --check -requirements.txt: $(PACKAGE_CHECK) pyproject.toml - $(PYTHON) -m uv pip compile --upgrade --output-file=requirements.txt pyproject.toml -requirements-dev.txt: $(PACKAGE_CHECK) pyproject.toml - $(PYTHON) -m uv pip compile --upgrade --output-file=requirements-dev.txt --extra=dev pyproject.toml - - -{% endif %} # # Packaging # .PHONY: build build: $(PACKAGE_CHECK) - $(PYTHON) -m build + $(UV) run python -m build {%- if cookiecutter.include_sqlalchemy == "y" %} @@ -160,15 +151,15 @@ build: $(PACKAGE_CHECK) .PHONY: document_schema document_schema: - $(PYTHON) -m paracelsus.cli inject docs/dev/database.md $(PACKAGE_SLUG).models.base:Base --import-module "$(PACKAGE_SLUG).models:*" + $(UV) run python -m paracelsus.cli inject docs/dev/database.md $(PACKAGE_SLUG).models.base:Base --import-module "$(PACKAGE_SLUG).models:*" .PHONY: paracelsus_check paracelsus_check: - $(PYTHON) -m paracelsus.cli inject docs/dev/database.md $(PACKAGE_SLUG).models.base:Base --import-module "$(PACKAGE_SLUG).models:*" --check + $(UV) run python -m paracelsus.cli inject docs/dev/database.md $(PACKAGE_SLUG).models.base:Base --import-module "$(PACKAGE_SLUG).models:*" --check .PHONY: run_migrations run_migrations: - $(PYTHON) -m alembic upgrade head + $(UV) run alembic upgrade head .PHONY: reset_db reset_db: clear_db run_migrations @@ -181,13 +172,13 @@ clear_db: create_migration: @if [ -z "$(MESSAGE)" ]; then echo "Please add a message parameter for the migration (make create_migration MESSAGE=\"database migration notes\")."; exit 1; fi rm $(MIGRATION_DATABASE) | true - . .venv/bin/activate && DATABASE_URL=sqlite:///$(MIGRATION_DATABASE) python -m alembic upgrade head - . .venv/bin/activate && DATABASE_URL=sqlite:///$(MIGRATION_DATABASE) python -m alembic revision --autogenerate -m "$(MESSAGE)" + DATABASE_URL=sqlite:///$(MIGRATION_DATABASE) $(UV) run alembic upgrade head + DATABASE_URL=sqlite:///$(MIGRATION_DATABASE) $(UV) run alembic revision --autogenerate -m "$(MESSAGE)" rm $(MIGRATION_DATABASE) - $(PYTHON) -m ruff format ./db + $(UV) run ruff format ./db .PHONY: check_ungenerated_migrations check_ungenerated_migrations: - $(PYTHON) -m alembic check + $(UV) run alembic check {% endif %} diff --git a/{{cookiecutter.__package_slug}}/pyproject.toml b/{{cookiecutter.__package_slug}}/pyproject.toml index d7363d3..c30dc86 100644 --- a/{{cookiecutter.__package_slug}}/pyproject.toml +++ b/{{cookiecutter.__package_slug}}/pyproject.toml @@ -37,6 +37,7 @@ dependencies = [ {%- endif %} {%- if cookiecutter.include_fastapi == "y" %} "fastapi", + "uvicorn[standard]", {%- endif %} {%- if cookiecutter.include_jinja2 == "y" %} "jinja2", @@ -60,7 +61,7 @@ dependencies = [ {%- endif %} ] -[project.optional-dependencies] +[dependency-groups] dev = [ "build", "dapperdata", @@ -80,9 +81,7 @@ dev = [ "ruamel.yaml", "ruff", "toml-sort", -{%- if cookiecutter.include_requirements_files == "y" %} "uv" -{% endif %} ] {% if cookiecutter.include_cli == "y" %}