diff --git a/.github/contributors.json b/.github/contributors.json index 9e812d6ac5..78d93669c3 100644 --- a/.github/contributors.json +++ b/.github/contributors.json @@ -1629,5 +1629,40 @@ "name": "Will", "github_login": "novucs", "twitter_username": "" + }, + { + "name": "rxm7706", + "github_login": "rxm7706", + "twitter_username": "" + }, + { + "name": "Marlon Castillo", + "github_login": "mcastle", + "twitter_username": "" + }, + { + "name": "Alex Kanavos", + "github_login": "alexkanavos", + "twitter_username": "" + }, + { + "name": "LJFP", + "github_login": "ljfp", + "twitter_username": "" + }, + { + "name": "Francisco Navarro Morales ", + "github_login": "spothound", + "twitter_username": "" + }, + { + "name": "Mariot Tsitoara", + "github_login": "mariot", + "twitter_username": "" + }, + { + "name": "Christian Jensen", + "github_login": "jensenbox", + "twitter_username": "cjensen" } ] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3582a21253..a24abcfefb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,5 +1,5 @@ # Config for Dependabot updates. See Documentation here: -# https://docs.github.com/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: @@ -96,6 +96,14 @@ updates: labels: - "update" + - package-ecosystem: "docker" + directory: "{{cookiecutter.project_slug}}/compose/production/nginx/" + schedule: + interval: "daily" + versioning-strategy: increase + labels: + - "update" + - package-ecosystem: "docker" directory: "{{cookiecutter.project_slug}}/compose/production/traefik/" schedule: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91d08b1d29..44e31e598c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,14 +23,14 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v3 with: - python-version: "3.12" - cache: pip + enable-cache: "true" - name: Install dependencies - run: pip install -r requirements.txt + run: uv sync - name: Run tests - run: pytest -n auto tests + run: uv run pytest -n auto tests docker: strategy: @@ -54,12 +54,12 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v3 with: - python-version: "3.12" - cache: pip + enable-cache: "true" - name: Install dependencies - run: pip install -r requirements.txt + run: uv sync - name: Docker ${{ matrix.script.name }} run: sh tests/test_docker.sh ${{ matrix.script.args }} @@ -92,7 +92,7 @@ jobs: POSTGRES_PASSWORD: postgres env: - CELERY_BROKER_URL: "redis://localhost:6379/0" + REDIS_URL: "redis://localhost:6379/0" # postgres://user:password@host:port/database DATABASE_URL: "postgres://postgres:postgres@localhost:5432/postgres" @@ -101,13 +101,12 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.12" - cache: pip - cache-dependency-path: | - requirements.txt - {{cookiecutter.project_slug}}/requirements/base.txt - {{cookiecutter.project_slug}}/requirements/local.txt + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: "true" - name: Install dependencies - run: pip install -r requirements.txt + run: uv sync - uses: actions/setup-node@v4 with: node-version: "22" diff --git a/.github/workflows/dependabot-uv-lock.yml b/.github/workflows/dependabot-uv-lock.yml new file mode 100644 index 0000000000..73fb88130c --- /dev/null +++ b/.github/workflows/dependabot-uv-lock.yml @@ -0,0 +1,35 @@ +name: uv + +on: + pull_request: + paths: + - "pyproject.toml" + +permissions: + contents: write + pull-requests: write + +jobs: + lock: + if: ${{ github.actor == 'dependabot[bot]' }} + runs-on: ubuntu-latest + env: + GH_PAT: ${{ secrets.GH_PAT }} + steps: + - name: Checkout with token + uses: actions/checkout@v4 + if: ${{ env.GH_PAT != '' }} + with: + token: ${{ env.GH_PAT }} + + - name: Checkout without token + uses: actions/checkout@v4 + if: ${{ env.GH_PAT == '' }} + + - uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + - run: uv lock + - uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Regenerate uv.lock diff --git a/.github/workflows/django-issue-checker.yml b/.github/workflows/django-issue-checker.yml index 2185da81ba..1d6de43323 100644 --- a/.github/workflows/django-issue-checker.yml +++ b/.github/workflows/django-issue-checker.yml @@ -17,14 +17,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v3 with: - python-version: "3.12" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt + enable-cache: "true" - name: Create Django Major Issue - run: python scripts/create_django_issue.py + run: uv run --frozen scripts/create_django_issue.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 103612cfe3..42579d06ed 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.5.0 + - uses: tiangolo/issue-manager@0.5.1 with: token: ${{ secrets.GITHUB_TOKEN }} config: > diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml index 635d26f230..7ba5693805 100644 --- a/.github/workflows/update-changelog.yml +++ b/.github/workflows/update-changelog.yml @@ -15,20 +15,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v3 with: - python-version: "3.12" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt + enable-cache: "true" - name: Set git details run: | git config --global user.name "github-actions" git config --global user.email "action@github.com" - - name: Update list - run: python scripts/update_changelog.py + - name: Update changelog + run: uv run --frozen scripts/update_changelog.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml index c77bc5e8f8..1073fa4bec 100644 --- a/.github/workflows/update-contributors.yml +++ b/.github/workflows/update-contributors.yml @@ -18,17 +18,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v3 with: - python-version: "3.12" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt + enable-cache: "true" - name: Update list - run: python scripts/update_contributors.py + run: uv run --frozen scripts/update_contributors.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8d601e926..ca6730558c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ exclude: "{{cookiecutter.project_slug}}|.github/contributors.json|CHANGELOG.md|CONTRIBUTORS.md" -default_stages: [commit] +default_stages: [pre-commit] default_language_version: python: python3.12 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -26,14 +26,14 @@ repos: args: ["--tab-width", "2"] - repo: https://github.com/asottile/pyupgrade - rev: v3.17.0 + rev: v3.19.0 hooks: - id: pyupgrade args: [--py312-plus] exclude: hooks/ - repo: https://github.com/psf/black - rev: 24.8.0 + rev: 24.10.0 hooks: - id: black @@ -47,6 +47,11 @@ repos: hooks: - id: flake8 + - repo: https://github.com/tox-dev/pyproject-fmt + rev: "v2.5.0" + hooks: + - id: pyproject-fmt + ci: autoupdate_schedule: weekly skip: [] diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 872e43a2e8..de20c18cc6 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,12 +9,13 @@ build: os: ubuntu-22.04 tools: python: "3.12" + commands: + - asdf plugin add uv + - asdf install uv latest + - asdf global uv latest + - uv sync --only-group docs --frozen + - uv run -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py - -# Declare the Python requirements required to build your docs -python: - install: - - requirements: docs/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index a207749fae..a71b71ef11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,430 @@ All enhancements and patches to Cookiecutter Django will be documented in this f +## 2024.10.30 + + +### Documentation + +- Small spelling correction in comment ([#5502](https://github.com/cookiecutter/cookiecutter-django/pull/5502)) + +### Updated + +- Update djlint to 1.35.3 ([#5503](https://github.com/cookiecutter/cookiecutter-django/pull/5503)) + +- Update whitenoise to 6.8.2 ([#5501](https://github.com/cookiecutter/cookiecutter-django/pull/5501)) + +## 2024.10.29 + + +### Updated + +- Update django-cors-headers to 4.6.0 ([#5499](https://github.com/cookiecutter/cookiecutter-django/pull/5499)) + +- Update whitenoise to 6.8.1 ([#5497](https://github.com/cookiecutter/cookiecutter-django/pull/5497)) + +- Bump traefik from 3.1.6 to 3.2.0 ([#5498](https://github.com/cookiecutter/cookiecutter-django/pull/5498)) + +## 2024.10.26 + + +### Updated + +- Update django-stubs to 5.1.1 ([#5495](https://github.com/cookiecutter/cookiecutter-django/pull/5495)) + +- Update mypy to 1.13.0 ([#5484](https://github.com/cookiecutter/cookiecutter-django/pull/5484)) + +- Update werkzeug to 3.0.6 ([#5492](https://github.com/cookiecutter/cookiecutter-django/pull/5492)) + +## 2024.10.25 + + +### Updated + +- Update werkzeug to 3.0.5 and unpin watchdog<5 ([#5489](https://github.com/cookiecutter/cookiecutter-django/pull/5489)) + +## 2024.10.24 + + +### Updated + +- Update ruff to 0.7.1 ([#5487](https://github.com/cookiecutter/cookiecutter-django/pull/5487)) + +- Update redis to 5.2.0 ([#5486](https://github.com/cookiecutter/cookiecutter-django/pull/5486)) + +- Update django-allauth to 65.1.0 ([#5485](https://github.com/cookiecutter/cookiecutter-django/pull/5485)) + +## 2024.10.22 + + +### Changed + +- Fix broken links in generated README ([#5482](https://github.com/cookiecutter/cookiecutter-django/pull/5482)) + +### Updated + +- Auto-update pre-commit hooks ([#5483](https://github.com/cookiecutter/cookiecutter-django/pull/5483)) + +## 2024.10.21 + + +### Changed + +- Remove CELERY_BROKER_URL in favor of REDIS_URL ([#4861](https://github.com/cookiecutter/cookiecutter-django/pull/4861)) + +### Fixed + +- Fix a number of issues with Azure storage ([#5476](https://github.com/cookiecutter/cookiecutter-django/pull/5476)) + +### Updated + +- Update coverage to 7.6.4 ([#5480](https://github.com/cookiecutter/cookiecutter-django/pull/5480)) + +## 2024.10.17 + + +### Updated + +- Update ruff to 0.7.0 ([#5474](https://github.com/cookiecutter/cookiecutter-django/pull/5474)) + +- Update uvicorn to 0.32.0 ([#5471](https://github.com/cookiecutter/cookiecutter-django/pull/5471)) + +- Update pillow to 11.0.0 ([#5470](https://github.com/cookiecutter/cookiecutter-django/pull/5470)) + +- Update sentry-sdk to 2.17.0 ([#5473](https://github.com/cookiecutter/cookiecutter-django/pull/5473)) + +## 2024.10.15 + + +### Changed + +- [pre-commit.ci] pre-commit autoupdate ([#5468](https://github.com/cookiecutter/cookiecutter-django/pull/5468)) + +## 2024.10.14 + + +### Changed + +- Add requirements for ubuntu 24.04 ([#5467](https://github.com/cookiecutter/cookiecutter-django/pull/5467)) + +### Updated + +- Update sphinx to 8.1.3 ([#5463](https://github.com/cookiecutter/cookiecutter-django/pull/5463)) + +- Update coverage to 7.6.3 ([#5464](https://github.com/cookiecutter/cookiecutter-django/pull/5464)) + +## 2024.10.12 + + +### Changed + +- Pin Python version to 3.12 ([#5456](https://github.com/cookiecutter/cookiecutter-django/pull/5456)) + +### Updated + +- Update sphinx to 8.1.1 ([#5457](https://github.com/cookiecutter/cookiecutter-django/pull/5457)) + +- Auto-update pre-commit hooks ([#5458](https://github.com/cookiecutter/cookiecutter-django/pull/5458)) + +- Update django-cors-headers to 4.5.0 ([#5459](https://github.com/cookiecutter/cookiecutter-django/pull/5459)) + +## 2024.10.11 + + +### Changed + +- Update README.md broken links ([#5454](https://github.com/cookiecutter/cookiecutter-django/pull/5454)) + +- wait-for-it as dependency in the run stage ([#5452](https://github.com/cookiecutter/cookiecutter-django/pull/5452)) + +### Updated + +- Auto-update pre-commit hooks ([#5450](https://github.com/cookiecutter/cookiecutter-django/pull/5450)) + +## 2024.10.10 + + +### Updated + +- Update sentry-sdk to 2.16.0 ([#5444](https://github.com/cookiecutter/cookiecutter-django/pull/5444)) + +- Bump traefik from 3.1.5 to 3.1.6 ([#5449](https://github.com/cookiecutter/cookiecutter-django/pull/5449)) + +## 2024.10.09 + + +### Updated + +- Update coverage to 7.6.2 ([#5448](https://github.com/cookiecutter/cookiecutter-django/pull/5448)) + +## 2024.10.08 + + +### Changed + +- [pre-commit.ci] pre-commit autoupdate ([#5441](https://github.com/cookiecutter/cookiecutter-django/pull/5441)) + +### Updated + +- Update pre-commit to 4.0.1 ([#5445](https://github.com/cookiecutter/cookiecutter-django/pull/5445)) + +- Bump amazon/aws-cli from 2.18.0 to 2.18.1 in /{{cookiecutter.project_slug}}/compose/production/aws ([#5443](https://github.com/cookiecutter/cookiecutter-django/pull/5443)) + +- Auto-update pre-commit hooks ([#5442](https://github.com/cookiecutter/cookiecutter-django/pull/5442)) + +## 2024.10.07 + + +### Documentation + +- Docs: fix Mailgun include in troubleshooting sections ([#5439](https://github.com/cookiecutter/cookiecutter-django/pull/5439)) + +### Updated + +- Update sphinx to 8.0.2 ([#5263](https://github.com/cookiecutter/cookiecutter-django/pull/5263)) + +- Update pre-commit to 4.0.0 ([#5432](https://github.com/cookiecutter/cookiecutter-django/pull/5432)) + +- Auto-update pre-commit hooks ([#5433](https://github.com/cookiecutter/cookiecutter-django/pull/5433)) + +- Update sphinx-rtd-theme to 3.0.0 ([#5437](https://github.com/cookiecutter/cookiecutter-django/pull/5437)) + +- Bump amazon/aws-cli from 2.17.0 to 2.18.0 in /{{cookiecutter.project_slug}}/compose/production/aws ([#5436](https://github.com/cookiecutter/cookiecutter-django/pull/5436)) + +## 2024.10.05 + + +### Changed + +- Replace pip with uv for the project files ([#5356](https://github.com/cookiecutter/cookiecutter-django/pull/5356)) + +- Use wait-for-it in favor of the custom python script when waiting for postgres ([#5327](https://github.com/cookiecutter/cookiecutter-django/pull/5327)) + +### Documentation + +- Spit docs into sections ([#5426](https://github.com/cookiecutter/cookiecutter-django/pull/5426)) + +### Updated + +- Update crispy-bootstrap5 to 2024.10 ([#5430](https://github.com/cookiecutter/cookiecutter-django/pull/5430)) + +## 2024.10.04 + + +### Updated + +- Update ruff to 0.6.9 ([#5428](https://github.com/cookiecutter/cookiecutter-django/pull/5428)) + +- Update redis to 5.1.1 ([#5427](https://github.com/cookiecutter/cookiecutter-django/pull/5427)) + +## 2024.10.03 + + +### Updated + +- Update sphinx-autobuild to 2024.10.3 ([#5420](https://github.com/cookiecutter/cookiecutter-django/pull/5420)) + +- Bump traefik from 3.1.4 to 3.1.5 ([#5423](https://github.com/cookiecutter/cookiecutter-django/pull/5423)) + +## 2024.10.02 + + +### Updated + +- Bump python from 3.12.6 to 3.12.7 in docs Docker image ([#5416](https://github.com/cookiecutter/cookiecutter-django/pull/5416)) + +- Bump python from 3.12.6 to 3.12.7 in local Docker image ([#5415](https://github.com/cookiecutter/cookiecutter-django/pull/5415)) + +- Bump python from 3.12.6 to 3.12.7 production Docker ([#5414](https://github.com/cookiecutter/cookiecutter-django/pull/5414)) + +## 2024.10.01 + + +### Updated + +- Update sentry-sdk to 2.15.0 ([#5413](https://github.com/cookiecutter/cookiecutter-django/pull/5413)) + +## 2024.09.29 + + +### Updated + +- Update psycopg to 3.2.3 ([#5411](https://github.com/cookiecutter/cookiecutter-django/pull/5411)) + +## 2024.09.28 + + +### Updated + +- Update uvicorn to 0.31.0 ([#5408](https://github.com/cookiecutter/cookiecutter-django/pull/5408)) + +## 2024.09.27 + + +### Updated + +- Update redis to 5.1.0 ([#5406](https://github.com/cookiecutter/cookiecutter-django/pull/5406)) + +- Update django-allauth to 65.0.2 ([#5405](https://github.com/cookiecutter/cookiecutter-django/pull/5405)) + +## 2024.09.26 + + +### Updated + +- Update ruff to 0.6.8 ([#5402](https://github.com/cookiecutter/cookiecutter-django/pull/5402)) + +## 2024.09.24 + + +### Updated + +- Update django-stubs to 5.1.0 ([#5400](https://github.com/cookiecutter/cookiecutter-django/pull/5400)) + +## 2024.09.23 + + +### Changed + +- Fix Makefile and make.bat files used for automatic generation of documentation. ([#5347](https://github.com/cookiecutter/cookiecutter-django/pull/5347)) + +### Updated + +- Update django-allauth to 65.0.1 ([#5399](https://github.com/cookiecutter/cookiecutter-django/pull/5399)) + +- Auto-update pre-commit hooks ([#5392](https://github.com/cookiecutter/cookiecutter-django/pull/5392)) + +- Update django-allauth to 65.0.0 ([#5393](https://github.com/cookiecutter/cookiecutter-django/pull/5393)) + +- Update ruff to 0.6.7 ([#5394](https://github.com/cookiecutter/cookiecutter-django/pull/5394)) + +## 2024.09.21 + + +### Updated + +- Auto-update pre-commit hooks ([#5391](https://github.com/cookiecutter/cookiecutter-django/pull/5391)) + +## 2024.09.20 + + +### Updated + +- Update sphinx-autobuild to 2024.9.19 ([#5386](https://github.com/cookiecutter/cookiecutter-django/pull/5386)) + +- Bump traefik from 3.1.3 to 3.1.4 in /{{cookiecutter.project_slug}}/compose/production/traefik ([#5389](https://github.com/cookiecutter/cookiecutter-django/pull/5389)) + +- Update ruff to 0.6.6 ([#5388](https://github.com/cookiecutter/cookiecutter-django/pull/5388)) + +- Update tox to 4.20.0 ([#5387](https://github.com/cookiecutter/cookiecutter-django/pull/5387)) + +## 2024.09.17 + + +### Fixed + +- Set allauth email subject prefix ([#5278](https://github.com/cookiecutter/cookiecutter-django/pull/5278)) + +### Documentation + +- Update postgres plan in Heroku deployment docs ([#5383](https://github.com/cookiecutter/cookiecutter-django/pull/5383)) + +### Updated + +- Update sphinx-autobuild to 2024.9.17 ([#5382](https://github.com/cookiecutter/cookiecutter-django/pull/5382)) + +- Bump traefik from 3.1.2 to 3.1.3 ([#5380](https://github.com/cookiecutter/cookiecutter-django/pull/5380)) + +## 2024.09.16 + + +### Updated + +- Update psycopg to 3.2.2 ([#5378](https://github.com/cookiecutter/cookiecutter-django/pull/5378)) + +## 2024.09.15 + + +### Updated + +- Update ruff to 0.6.5 ([#5373](https://github.com/cookiecutter/cookiecutter-django/pull/5373)) + +## 2024.09.11 + + +### Updated + +- Update djangorestframework-stubs to 3.15.1 ([#5372](https://github.com/cookiecutter/cookiecutter-django/pull/5372)) + +- Update django-anymail to 12.0 ([#5366](https://github.com/cookiecutter/cookiecutter-django/pull/5366)) + +## 2024.09.10 + + +### Changed + +- Add nginx container to dependabot automation ([#5345](https://github.com/cookiecutter/cookiecutter-django/pull/5345)) + +### Updated + +- Update pytest to 8.3.3 ([#5371](https://github.com/cookiecutter/cookiecutter-django/pull/5371)) + +- Bump postcss-preset-env from 9.6.0 to 10.0.3 ([#5368](https://github.com/cookiecutter/cookiecutter-django/pull/5368)) + +- Bump concurrently from 8.2.2 to 9.0.0 ([#5363](https://github.com/cookiecutter/cookiecutter-django/pull/5363)) + +- Bump python to 3.12.6 in local Docker ([#5370](https://github.com/cookiecutter/cookiecutter-django/pull/5370)) + +- Bump python to 3.12.6 in production Docker ([#5367](https://github.com/cookiecutter/cookiecutter-django/pull/5367)) + +- Bump python to 3.12.6 in docs Docker image ([#5369](https://github.com/cookiecutter/cookiecutter-django/pull/5369)) + +## 2024.09.09 + + +### Updated + +- Update sentry-sdk to 2.14.0 ([#5365](https://github.com/cookiecutter/cookiecutter-django/pull/5365)) + +## 2024.09.08 + + +### Changed + +- Remove "storages" from `INSTALLED_APPS` ([#5361](https://github.com/cookiecutter/cookiecutter-django/pull/5361)) + +## 2024.09.07 + + +### Changed + +- Fix Requires Python to allow minor versions ([#5360](https://github.com/cookiecutter/cookiecutter-django/pull/5360)) + +## 2024.09.06 + + +### Updated + +- Update ruff to 0.6.4 ([#5354](https://github.com/cookiecutter/cookiecutter-django/pull/5354)) + +- Update django-allauth to 64.2.1 ([#5352](https://github.com/cookiecutter/cookiecutter-django/pull/5352)) + +- Auto-update pre-commit hooks ([#5353](https://github.com/cookiecutter/cookiecutter-django/pull/5353)) + +## 2024.09.05 + + +### Updated + +- Update django-upgrade to 1.21.0 ([#5348](https://github.com/cookiecutter/cookiecutter-django/pull/5348)) + +## 2024.09.04 + + +### Updated + +- Update django-model-utils to 5.0.0 ([#5343](https://github.com/cookiecutter/cookiecutter-django/pull/5343)) + ## 2024.09.03 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d2025e377..e6bf040615 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,33 +24,20 @@ We'll also run the tests on GitHub actions when you send your pull request, but ### Installation -First, make sure that your version of Python is 3.12: - -```bash -$ python --version -Python 3.12.2 -``` - -Any version that starts with 3.12 will do. If you need to install it, you can get it from [python.org](https://www.python.org/downloads/). - -Then install `tox`, if not already installed: - -```bash -$ python -m pip install tox -``` +We use uv to manage our environment and manage our Python installation. You can install it following the instructions at https://docs.astral.sh/uv/getting-started/installation/ ### Run the template's test suite To run the tests of the template using the current Python version: ```bash -$ tox -e py +$ uv run tox run -e py ``` This uses `pytest `under the hood, and you can pass options to it after a `--`. So to run a particular test: ```bash -$ tox -e py -- -k test_default_configuration +$ uv run tox run -e py -- -k test_default_configuration ``` For further information, please consult the [pytest usage docs](https://pytest.org/en/latest/how-to/usage.html#specifying-which-tests-to-run). diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 5010bf32f5..06c9c2db9f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -208,6 +208,13 @@ Listed in alphabetical order. + + Alex Kanavos + + alexkanavos + + + Alex Tsai @@ -537,6 +544,13 @@ Listed in alphabetical order. + + Christian Jensen + + jensenbox + + cjensen + Christopher Clarke @@ -873,6 +887,13 @@ Listed in alphabetical order. + + Francisco Navarro Morales + + spothound + + + Freddy @@ -1412,6 +1433,13 @@ Listed in alphabetical order. + + LJFP + + ljfp + + + Luis Nell @@ -1468,6 +1496,20 @@ Listed in alphabetical order. + + Mariot Tsitoara + + mariot + + + + + Marlon Castillo + + mcastle + + + Martin Blech @@ -1923,6 +1965,13 @@ Listed in alphabetical order. + + rxm7706 + + rxm7706 + + + Ryan Fitch diff --git a/Makefile b/Makefile index 5ccd456b55..cc4e70152e 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,7 @@ help: # Variables # ----------------------------------------------------------------------------- -python_version=3.9.11 -venv=cookiecutter-django_env +venv=.venv BAKE_OPTIONS=--no-input @@ -31,14 +30,24 @@ BAKE_OPTIONS=--no-input # ----------------------------------------------------------------------------- env: ## Install virtualenv for development (uses `pyenv`) - pyenv virtualenv ${python_version} ${venv} && pyenv local ${venv} - python3 -m pip install -U pip -r requirements.txt + python3 -m venv ${venv} + @echo "Virtual environment created. Activate with: source ${venv}/bin/activate" + @echo "Install dependencies with: pip install ." env_remove: ## Remove virtual environment - pyenv uninstall -f ${venv} + deactivate + rm -rf ${venv} env_from_scratch: env_remove env ## Create environment from scratch +# ----------------------------------------------------------------------------- +# Pip +# ----------------------------------------------------------------------------- + +pip_install: ## Install requirements + python3 -m pip install -U pip + python3 -m pip install . + # ----------------------------------------------------------------------------- # Cookiecutter # ----------------------------------------------------------------------------- diff --git a/README.md b/README.md index 879b2b2cf7..3b42a5e1bf 100644 --- a/README.md +++ b/README.md @@ -190,8 +190,8 @@ Now take a look at your repo. Don't forget to carefully look at the generated RE For local development, see the following: -- [Developing locally](http://cookiecutter-django.readthedocs.io/en/latest/developing-locally.html) -- [Developing locally using docker](http://cookiecutter-django.readthedocs.io/en/latest/developing-locally-docker.html) +- [Developing locally](https://cookiecutter-django.readthedocs.io/en/latest/2-local-development/developing-locally.html) +- [Developing locally using docker](https://cookiecutter-django.readthedocs.io/en/latest/2-local-development/developing-locally-docker.html) ## Community diff --git a/docs/project-generation-options.rst b/docs/1-getting-started/project-generation-options.rst similarity index 100% rename from docs/project-generation-options.rst rename to docs/1-getting-started/project-generation-options.rst diff --git a/docs/settings.rst b/docs/1-getting-started/settings.rst similarity index 98% rename from docs/settings.rst rename to docs/1-getting-started/settings.rst index 2be79cbf58..dfdccc7cc9 100644 --- a/docs/settings.rst +++ b/docs/1-getting-started/settings.rst @@ -39,7 +39,6 @@ The following table lists settings and their defaults for third-party applicatio ======================================= =========================== ============================================== ====================================================================== Environment Variable Django Setting Development Default Production Default ======================================= =========================== ============================================== ====================================================================== -CELERY_BROKER_URL CELERY_BROKER_URL auto w/ Docker; raises error w/o raises error DJANGO_AWS_ACCESS_KEY_ID AWS_ACCESS_KEY_ID n/a raises error DJANGO_AWS_SECRET_ACCESS_KEY AWS_SECRET_ACCESS_KEY n/a raises error DJANGO_AWS_STORAGE_BUCKET_NAME AWS_STORAGE_BUCKET_NAME n/a raises error diff --git a/docs/developing-locally-docker.rst b/docs/2-local-development/developing-locally-docker.rst similarity index 91% rename from docs/developing-locally-docker.rst rename to docs/2-local-development/developing-locally-docker.rst index cb2cdc0e57..05e188e527 100644 --- a/docs/developing-locally-docker.rst +++ b/docs/2-local-development/developing-locally-docker.rst @@ -257,7 +257,7 @@ Here is a link to an article on `how to add HTTPS using Nginx`_ to your local do Webpack ~~~~~~~ -If you are using Webpack, first install ``mkcert``_. It is a simple by design tool that hides all the arcane knowledge required to generate valid TLS certificates. It works for any hostname or IP, including localhost. It supports macOS, Linux, and Windows, and Firefox, Chrome and Java. It even works on mobile devices with a couple manual steps. See https://blog.filippo.io/mkcert-valid-https-certificates-for-localhost/ +If you are using Webpack, first install `mkcert`_. It is a simple by design tool that hides all the arcane knowledge required to generate valid TLS certificates. It works for any hostname or IP, including localhost. It supports macOS, Linux, and Windows, and Firefox, Chrome and Java. It even works on mobile devices with a couple manual steps. See https://blog.filippo.io/mkcert-valid-https-certificates-for-localhost/ .. _`mkcert`: https://github.com/FiloSottile/mkcert/blob/master/README.md#supported-root-stores @@ -267,36 +267,36 @@ Assuming that you registered your local hostname as ``my-dev-env.local``, the ce 1. Add the ``nginx-proxy`` service to the ``docker-compose.local.yml``. :: - nginx-proxy: - image: jwilder/nginx-proxy:alpine - container_name: nginx-proxy - ports: - - "80:80" - - "443:443" - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs - restart: always - depends_on: - - node - environment: - - VIRTUAL_HOST=my-dev-env.local - - VIRTUAL_PORT=3000 + nginx-proxy: + image: jwilder/nginx-proxy:alpine + container_name: nginx-proxy + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./certs:/etc/nginx/certs + restart: always + depends_on: + - node + environment: + - VIRTUAL_HOST=my-dev-env.local + - VIRTUAL_PORT=3000 2. Add the local secure domain to the ``config/settings/local.py``. You should allow the new hostname :: - ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1", "my-dev-env.local"] + ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1", "my-dev-env.local"] 3. Add the following configuration to the ``devServer`` section of ``webpack/dev.config.js`` :: - client: { - webSocketURL: 'auto://0.0.0.0:0/ws', // note the `:0` after `0.0.0.0` - }, + client: { + webSocketURL: 'auto://0.0.0.0:0/ws', // note the `:0` after `0.0.0.0` + }, Rebuild your ``docker`` application. :: - $ docker compose -f docker-compose.local.yml up -d --build + $ docker compose -f docker-compose.local.yml up -d --build Go to your browser and type in your URL bar ``https://my-dev-env.local``. diff --git a/docs/developing-locally.rst b/docs/2-local-development/developing-locally.rst similarity index 97% rename from docs/developing-locally.rst rename to docs/2-local-development/developing-locally.rst index 16247d082e..b8484bfe0f 100644 --- a/docs/developing-locally.rst +++ b/docs/2-local-development/developing-locally.rst @@ -24,8 +24,7 @@ First things first. $ source /bin/activate -#. - .. include:: generate-project-block.rst +#. .. include:: generate-project-block.rst #. Install development requirements: :: @@ -56,8 +55,6 @@ First things first. #. Set the environment variables for your database(s): :: $ export DATABASE_URL=postgres://postgres:@127.0.0.1:5432/ - # Optional: set broker URL if using Celery - $ export CELERY_BROKER_URL=redis://localhost:6379/0 .. note:: @@ -219,7 +216,7 @@ The project comes with a simple task for manual testing purposes, inside `' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @awk '/^#/{c=substr($$0,3);next}c&&/^[[:alpha:]][[:alnum:]_-]+:/{print substr($$1,1,index($$1,":")),c}1{c=0}' $(MAKEFILE_LIST) | column -s: -t +# Clean the build output clean: -rm -rf $(BUILDDIR)/* +# Build the HTML docs html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/{{ cookiecutter.project_slug }}.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/{{ cookiecutter.project_slug }}.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/{{ cookiecutter.project_slug }}" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/{{ cookiecutter.project_slug }}" - @echo "# devhelp" + $(SPHINXBUILD) -b html $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." +# Build and serve docs with live reload +livehtml: + sphinx-autobuild -b html --port 9000 --watch . -c . $(SOURCEDIR) $(BUILDDIR)/html +# Check all external links for integrity linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + $(SPHINXBUILD) -b linkcheck $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/conf.py b/docs/conf.py index 22e73e5db4..82098a168e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,7 +29,10 @@ templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = [".rst", ".md"] +source_suffix = { + ".rst": "restructuredtext", + ".md": "markdown", +} # The encoding of source files. # source_encoding = 'utf-8-sig' diff --git a/docs/contributing.md b/docs/contributing.md deleted file mode 100644 index 66c1f98d36..0000000000 --- a/docs/contributing.md +++ /dev/null @@ -1,3 +0,0 @@ -```{include} ../CONTRIBUTING.md - -``` diff --git a/docs/mailgun.rst b/docs/includes/mailgun.rst similarity index 100% rename from docs/mailgun.rst rename to docs/includes/mailgun.rst diff --git a/docs/index.rst b/docs/index.rst index 70daa1852b..f5fa77f5b7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,28 +7,51 @@ Powered by Cookiecutter_, Cookiecutter Django is a project template for jumpstar .. _cookiecutter: https://github.com/cookiecutter/cookiecutter -Contents --------- +.. toctree:: + :maxdepth: 2 + :caption: Getting Started + + 1-getting-started/project-generation-options + 1-getting-started/settings + +.. toctree:: + :maxdepth: 2 + :caption: Local Development + + 2-local-development/developing-locally + 2-local-development/developing-locally-docker + +.. toctree:: + :maxdepth: 2 + :caption: Deployment + + 3-deployment/deployment-on-pythonanywhere + 3-deployment/deployment-on-heroku + 3-deployment/deployment-with-docker + +.. toctree:: + :maxdepth: 2 + :caption: Guides + + 4-guides/docker-postgres-backups + 4-guides/linters + 4-guides/testing + 4-guides/document + 4-guides/websocket + +.. toctree:: + :maxdepth: 2 + :caption: Help + + 5-help/faq + 5-help/troubleshooting .. toctree:: :maxdepth: 2 + :caption: About - project-generation-options - developing-locally - developing-locally-docker - settings - linters - testing - document - deployment-on-pythonanywhere - deployment-on-heroku - deployment-with-docker - docker-postgres-backups - websocket - faq - troubleshooting - contributing - maintainer-guide + 6-about/contributing + 6-about/maintainer-guide Indices and tables ------------------ diff --git a/docs/make.bat b/docs/make.bat index ec43148b38..a8ed429ec4 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -5,36 +5,20 @@ REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) +set SOURCEDIR=. set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of + echo. clean to clean the build directory echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items + echo. livehtml to build and serve docs with live reload echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled goto end ) @@ -45,132 +29,22 @@ if "%1" == "clean" ( ) if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %SOURCEDIR% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\{{ cookiecutter.project_slug }}.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\{{ cookiecutter.project_slug }}.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes +if "%1" == "livehtml" ( + sphinx-autobuild -b html --port 9000 --watch . -c . %SOURCEDIR% %BUILDDIR%/html + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %SOURCEDIR% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ @@ -178,13 +52,4 @@ or in %BUILDDIR%/linkcheck/output.txt. goto end ) -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - :end diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index f3b1537217..0000000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -sphinx==7.4.7 -sphinx-rtd-theme==2.0.0 -myst-parser==4.0.0 diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 9739079fb7..1e2796eadf 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -91,6 +91,10 @@ def remove_docker_files(): os.remove(os.path.join(".idea", "runConfigurations", file_name)) +def remove_nginx_docker_files(): + shutil.rmtree(os.path.join("compose", "production", "nginx")) + + def remove_utility_files(): shutil.rmtree("utility") @@ -98,10 +102,7 @@ def remove_utility_files(): def remove_heroku_files(): file_names = ["Procfile", "runtime.txt", "requirements.txt"] for file_name in file_names: - if ( - file_name == "requirements.txt" - and "{{ cookiecutter.ci_tool }}".lower() == "travis" - ): + if file_name == "requirements.txt" and "{{ cookiecutter.ci_tool }}".lower() == "travis": # don't remove the file if we are using travisci but not using heroku continue os.remove(file_name) @@ -201,9 +202,7 @@ def handle_js_runner(choice, use_docker, use_async): ] if not use_docker: dev_django_cmd = ( - "uvicorn config.asgi:application --reload" - if use_async - else "python manage.py runserver_plus" + "uvicorn config.asgi:application --reload" if use_async else "python manage.py runserver_plus" ) scripts.update( { @@ -240,9 +239,7 @@ def remove_celery_files(): file_names = [ os.path.join("config", "celery_app.py"), os.path.join("{{ cookiecutter.project_slug }}", "users", "tasks.py"), - os.path.join( - "{{ cookiecutter.project_slug }}", "users", "tests", "test_tasks.py" - ), + os.path.join("{{ cookiecutter.project_slug }}", "users", "tests", "test_tasks.py"), ] for file_name in file_names: os.remove(file_name) @@ -273,9 +270,7 @@ def remove_dotdrone_file(): os.remove(".drone.yml") -def generate_random_string( - length, using_digits=False, using_ascii_letters=False, using_punctuation=False -): +def generate_random_string(length, using_digits=False, using_ascii_letters=False, using_punctuation=False): """ Example: opting out for 50 symbol-long, [a-z][A-Z][0-9] string @@ -369,9 +364,7 @@ def set_postgres_password(file_path, value=None): def set_celery_flower_user(file_path, value): - celery_flower_user = set_flag( - file_path, "!!!SET CELERY_FLOWER_USER!!!", value=value - ) + celery_flower_user = set_flag(file_path, "!!!SET CELERY_FLOWER_USER!!!", value=value) return celery_flower_user @@ -403,22 +396,14 @@ def set_flags_in_envs(postgres_user, celery_flower_user, debug=False): set_django_admin_url(production_django_envs_path) set_postgres_user(local_postgres_envs_path, value=postgres_user) - set_postgres_password( - local_postgres_envs_path, value=DEBUG_VALUE if debug else None - ) + set_postgres_password(local_postgres_envs_path, value=DEBUG_VALUE if debug else None) set_postgres_user(production_postgres_envs_path, value=postgres_user) - set_postgres_password( - production_postgres_envs_path, value=DEBUG_VALUE if debug else None - ) + set_postgres_password(production_postgres_envs_path, value=DEBUG_VALUE if debug else None) set_celery_flower_user(local_django_envs_path, value=celery_flower_user) - set_celery_flower_password( - local_django_envs_path, value=DEBUG_VALUE if debug else None - ) + set_celery_flower_password(local_django_envs_path, value=DEBUG_VALUE if debug else None) set_celery_flower_user(production_django_envs_path, value=celery_flower_user) - set_celery_flower_password( - production_django_envs_path, value=DEBUG_VALUE if debug else None - ) + set_celery_flower_password(production_django_envs_path, value=DEBUG_VALUE if debug else None) def set_flags_in_settings_files(): @@ -448,21 +433,9 @@ def remove_aws_dockerfile(): def remove_drf_starter_files(): os.remove(os.path.join("config", "api_router.py")) shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "users", "api")) - os.remove( - os.path.join( - "{{cookiecutter.project_slug}}", "users", "tests", "test_drf_urls.py" - ) - ) - os.remove( - os.path.join( - "{{cookiecutter.project_slug}}", "users", "tests", "test_drf_views.py" - ) - ) - os.remove( - os.path.join( - "{{cookiecutter.project_slug}}", "users", "tests", "test_swagger.py" - ) - ) + os.remove(os.path.join("{{cookiecutter.project_slug}}", "users", "tests", "test_drf_urls.py")) + os.remove(os.path.join("{{cookiecutter.project_slug}}", "users", "tests", "test_drf_views.py")) + os.remove(os.path.join("{{cookiecutter.project_slug}}", "users", "tests", "test_swagger.py")) # ----------------------------------------------------------------------------- @@ -483,9 +456,7 @@ def remove_storages_module(): def rename_nginx_conf(): """FORKED: We like to have the nginx conf file named `default.conf`.""" - old_name = os.path.join( - "compose", "production", "nginx", "{{cookiecutter.project_slug}}.conf" - ) + old_name = os.path.join("compose", "production", "nginx", "{{cookiecutter.project_slug}}.conf") new_name = os.path.join("compose", "production", "nginx", "default.conf") if os.path.exists(old_name): os.replace(old_name, new_name) @@ -532,22 +503,18 @@ def main(): if "{{ cookiecutter.use_docker }}".lower() == "y": remove_utility_files() + if "{{ cookiecutter.cloud_provider }}".lower() != "none": + remove_nginx_docker_files() else: remove_docker_files() - if ( - "{{ cookiecutter.use_docker }}".lower() == "y" - and "{{ cookiecutter.cloud_provider}}" != "AWS" - ): + if "{{ cookiecutter.use_docker }}".lower() == "y" and "{{ cookiecutter.cloud_provider}}" != "AWS": remove_aws_dockerfile() if "{{ cookiecutter.use_heroku }}".lower() == "n": remove_heroku_files() - if ( - "{{ cookiecutter.use_docker }}".lower() == "n" - and "{{ cookiecutter.use_heroku }}".lower() == "n" - ): + if "{{ cookiecutter.use_docker }}".lower() == "n" and "{{ cookiecutter.use_heroku }}".lower() == "n": if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y": print( INFO + ".env(s) are only utilized when Docker Compose and/or " @@ -576,10 +543,7 @@ def main(): use_async=("{{ cookiecutter.use_async }}".lower() == "y"), ) - if ( - "{{ cookiecutter.cloud_provider }}" == "None" - and "{{ cookiecutter.use_docker }}".lower() == "n" - ): + if "{{ cookiecutter.cloud_provider }}" == "None" and "{{ cookiecutter.use_docker }}".lower() == "n": print( WARNING + "You chose to not use any cloud providers nor Docker, " "media files won't be served in production." + TERMINATOR diff --git a/pyproject.toml b/pyproject.toml index 6e68762f71..1c2ef36aaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,35 +1,97 @@ -# ==== pytest ==== -[tool.pytest.ini_options] -addopts = "-v --tb=short" -norecursedirs = [ - ".tox", - ".git", - "*/migrations/*", - "*/static/*", - "docs", - "venv", - "*/{{cookiecutter.project_slug}}/*", +[project] +name = "cookiecutter-django" +version = "2024.10.30" +description = "A Cookiecutter template for creating production-ready Django projects quickly." +readme = "README.md" +keywords = [ + "cookiecutter", + "django", + "project template", + "scaffolding", + "skeleton", +] +license = { text = "BSD" } +authors = [ + { name = "Daniel Roy Greenfeld", email = "pydanny@gmail.com" }, ] +requires-python = ">=3.12,<=3.13" +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Framework :: Django :: 5.0", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Software Development", +] +dependencies = [ + "binaryornot==0.4.4", + "cookiecutter==2.6", + "django-upgrade==1.21", + "djlint==1.35.2", + "gitpython==3.1.43", + "jinja2==3.1.4", + "pre-commit==3.8", + "pygithub==2.4", + "pytest==8.3.3", + "pytest-cookies==0.7", + "pytest-instafail==0.5", + "pytest-xdist==3.6.1", + "pyyaml==6.0.2", + "requests==2.32.3", + "ruff==0.7", + "sh==2.0.7; sys_platform!='win23'", + "tox==4.21.2", + "tox-uv>=1.11.2", +] +urls = { Repository = "https://github.com/cookiecutter/cookiecutter-django" } +[dependency-groups] +docs = [ + "myst-parser>=4", + "sphinx>=8.0.2", + "sphinx-autobuild>=2024.10.3", + "sphinx-rtd-theme>=3", +] -# ==== black ==== [tool.black] line-length = 119 -target-version = ['py312'] - +target-version = [ + 'py312', +] # ==== isort ==== + [tool.isort] profile = "black" line_length = 119 known_first_party = [ - "tests", - "scripts", - "hooks", + "tests", + "scripts", + "hooks", ] +# ==== pytest ==== + +[tool.pytest.ini_options] +addopts = "-v --tb=short" +norecursedirs = [ + ".tox", + ".git", + "*/migrations/*", + "*/static/*", + "docs", + "venv", + "*/{{cookiecutter.project_slug}}/*", +] # ==== djLint ==== + [tool.djlint] blank_line_after_tag = "load,extends" close_void_tags = true diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 75fab0543a..0000000000 --- a/requirements.txt +++ /dev/null @@ -1,27 +0,0 @@ -cookiecutter==2.6.0 -sh==2.0.7; sys_platform != "win32" -binaryornot==0.4.4 -watchdog[watchmedo]==4.0.0 - -# Code quality -# ------------------------------------------------------------------------------ -ruff==0.6.3 -django-upgrade==1.20.0 -djlint==1.35.2 -pre-commit==3.8.0 - -# Testing -# ------------------------------------------------------------------------------ -tox==4.18.0 -pytest==8.3.2 -pytest-xdist==3.6.1 -pytest-cookies==0.7.0 -pytest-instafail==0.5.0 -pyyaml==6.0.2 - -# Scripting -# ------------------------------------------------------------------------------ -PyGithub==2.4.0 -gitpython==3.1.43 -jinja2==3.1.4 -requests==2.32.3 diff --git a/scripts/update_changelog.py b/scripts/update_changelog.py index 5f3ad5ec36..9ef942c495 100644 --- a/scripts/update_changelog.py +++ b/scripts/update_changelog.py @@ -1,6 +1,7 @@ import datetime as dt import os import re +import subprocess from collections.abc import Iterable from pathlib import Path @@ -47,10 +48,13 @@ def main() -> None: print(f"Wrote {changelog_path}") # Update version - setup_py_path = ROOT / "setup.py" + setup_py_path = ROOT / "pyproject.toml" update_version(setup_py_path, release) print(f"Updated version in {setup_py_path}") + # Run uv lock + subprocess.run(["uv", "lock"], cwd=ROOT) + # Commit changes, create tag and push update_git_repo([changelog_path, setup_py_path], release) @@ -124,7 +128,7 @@ def write_changelog(file_path: Path, release: str, content: str) -> None: def update_version(file_path: Path, release: str) -> None: - """Update template version in setup.py.""" + """Update template version in pyproject.toml.""" old_content = file_path.read_text() updated_content = re.sub( r'\nversion = "\d+\.\d+\.\d+"\n', diff --git a/setup.py b/setup.py deleted file mode 100644 index 7bd694e587..0000000000 --- a/setup.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -try: - from setuptools import setup -except ImportError: - from distutils.core import setup - -# We use calendar versioning -version = "2024.09.03" - -with open("README.md") as readme_file: - long_description = readme_file.read() - -setup( - name="cookiecutter-django", - version=version, - description=("A Cookiecutter template for creating production-ready " "Django projects quickly."), - long_description=long_description, - author="Daniel Roy Greenfeld", - author_email="pydanny@gmail.com", - url="https://github.com/cookiecutter/cookiecutter-django", - packages=[], - license="BSD", - zip_safe=False, - classifiers=[ - "Development Status :: 4 - Beta", - "Environment :: Console", - "Framework :: Django :: 5.0", - "Intended Audience :: Developers", - "Natural Language :: English", - "License :: OSI Approved :: BSD License", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: Implementation :: CPython", - "Topic :: Software Development", - ], - keywords=( - "cookiecutter, Python, projects, project templates, django, " - "skeleton, scaffolding, project directory, setup.py" - ), -) diff --git a/tests/test_bare.sh b/tests/test_bare.sh index 5dc175ebda..f38c9357e7 100755 --- a/tests/test_bare.sh +++ b/tests/test_bare.sh @@ -11,7 +11,7 @@ mkdir -p .cache/bare cd .cache/bare # create the project using the default settings in cookiecutter.json -cookiecutter ../../ --no-input --overwrite-if-exists use_docker=n "$@" +uv run cookiecutter ../../ --no-input --overwrite-if-exists use_docker=n "$@" cd my_awesome_project # Install OS deps diff --git a/tests/test_docker.sh b/tests/test_docker.sh index 9811b4fdc3..326e583286 100755 --- a/tests/test_docker.sh +++ b/tests/test_docker.sh @@ -11,7 +11,7 @@ mkdir -p .cache/docker cd .cache/docker # create the project using the default settings in cookiecutter.json -cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y "$@" +uv run cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y "$@" cd my_awesome_project # make sure all images build @@ -33,7 +33,6 @@ docker compose -f docker-compose.local.yml run django python manage.py makemessa docker compose -f docker-compose.local.yml run \ -e DJANGO_SECRET_KEY="$(openssl rand -base64 64)" \ -e REDIS_URL=redis://redis:6379/0 \ - -e CELERY_BROKER_URL=redis://redis:6379/0 \ -e DJANGO_AWS_ACCESS_KEY_ID=x \ -e DJANGO_AWS_SECRET_ACCESS_KEY=x \ -e DJANGO_AWS_STORAGE_BUCKET_NAME=x \ diff --git a/tox.ini b/tox.ini index 3b7a950881..70cde339f4 100644 --- a/tox.ini +++ b/tox.ini @@ -3,10 +3,9 @@ skipsdist = true envlist = py312,black-template [testenv] -deps = -rrequirements.txt passenv = AUTOFIXABLE_STYLES commands = pytest -n auto {posargs:./tests} [testenv:black-template] deps = black -commands = black --check hooks tests setup.py docs scripts +commands = black --check hooks tests docs scripts diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000000..c6944c41d5 --- /dev/null +++ b/uv.lock @@ -0,0 +1,1256 @@ +version = 1 +requires-python = "==3.12.*" + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, +] + +[[package]] +name = "anyio" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/49/f3f17ec11c4a91fe79275c426658e509b07547f874b14c1a526d86a83fc8/anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb", size = 170983 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/ef/7a4f225581a0d7886ea28359179cb861d7fbcdefad29663fc1167b86f69f/anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a", size = 89631 }, +] + +[[package]] +name = "arrow" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "types-python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85", size = 131960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size = 66419 }, +] + +[[package]] +name = "babel" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, +] + +[[package]] +name = "binaryornot" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "chardet" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/fe/7ebfec74d49f97fc55cd38240c7a7d08134002b1e14be8c3897c0dd5e49b/binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061", size = 371054 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/7e/f7b6f453e6481d1e233540262ccbfcf89adcd43606f44a028d7f5fae5eb2/binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4", size = 9006 }, +] + +[[package]] +name = "cachetools" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/38/a0f315319737ecf45b4319a8cd1f3a908e29d9277b46942263292115eee7/cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a", size = 27661 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/07/14f8ad37f2d12a5ce41206c21820d8cb6561b728e51fad4530dff0552a67/cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292", size = 9524 }, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "cffi" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/bf/82c351342972702867359cfeba5693927efe0a8dd568165490144f554b18/cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76", size = 516073 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/1f/7862231350cc959a3138889d2c8d33da7042b22e923457dfd4cd487d772a/cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc", size = 182826 }, + { url = "https://files.pythonhosted.org/packages/8b/8c/26119bf8b79e05a1c39812064e1ee7981e1f8a5372205ba5698ea4dd958d/cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59", size = 178494 }, + { url = "https://files.pythonhosted.org/packages/61/94/4882c47d3ad396d91f0eda6ef16d45be3d752a332663b7361933039ed66a/cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb", size = 454459 }, + { url = "https://files.pythonhosted.org/packages/0f/7c/a6beb119ad515058c5ee1829742d96b25b2b9204ff920746f6e13bf574eb/cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195", size = 478502 }, + { url = "https://files.pythonhosted.org/packages/61/8a/2575cd01a90e1eca96a30aec4b1ac101a6fae06c49d490ac2704fa9bc8ba/cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e", size = 485381 }, + { url = "https://files.pythonhosted.org/packages/cd/66/85899f5a9f152db49646e0c77427173e1b77a1046de0191ab3b0b9a5e6e3/cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828", size = 470907 }, + { url = "https://files.pythonhosted.org/packages/00/13/150924609bf377140abe6e934ce0a57f3fc48f1fd956ec1f578ce97a4624/cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150", size = 479074 }, + { url = "https://files.pythonhosted.org/packages/17/fd/7d73d7110155c036303b0a6462c56250e9bc2f4119d7591d27417329b4d1/cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a", size = 484225 }, + { url = "https://files.pythonhosted.org/packages/fc/83/8353e5c9b01bb46332dac3dfb18e6c597a04ceb085c19c814c2f78a8c0d0/cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885", size = 488388 }, + { url = "https://files.pythonhosted.org/packages/73/0c/f9d5ca9a095b1fc88ef77d1f8b85d11151c374144e4606da33874e17b65b/cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492", size = 172096 }, + { url = "https://files.pythonhosted.org/packages/72/21/8c5d285fe20a6e31d29325f1287bb0e55f7d93630a5a44cafdafb5922495/cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2", size = 181478 }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + +[[package]] +name = "chardet" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, + { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, + { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, + { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, + { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, + { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, + { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, + { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, + { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, + { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, + { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, + { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, + { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, + { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, + { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, + { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "cookiecutter" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "arrow" }, + { name = "binaryornot" }, + { name = "click" }, + { name = "jinja2" }, + { name = "python-slugify" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/17/9f2cd228eb949a91915acd38d3eecdc9d8893dde353b603f0db7e9f6be55/cookiecutter-2.6.0.tar.gz", hash = "sha256:db21f8169ea4f4fdc2408d48ca44859349de2647fbe494a9d6c3edfc0542c21c", size = 158767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/d9/0137658a353168ffa9d0fc14b812d3834772040858ddd1cb6eeaf09f7a44/cookiecutter-2.6.0-py3-none-any.whl", hash = "sha256:a54a8e37995e4ed963b3e82831072d1ad4b005af736bb17b99c2cbd9d41b6e2d", size = 39177 }, +] + +[[package]] +name = "cookiecutter-django" +version = "2024.10.24" +source = { virtual = "." } +dependencies = [ + { name = "binaryornot" }, + { name = "cookiecutter" }, + { name = "django-upgrade" }, + { name = "djlint" }, + { name = "gitpython" }, + { name = "jinja2" }, + { name = "pre-commit" }, + { name = "pygithub" }, + { name = "pytest" }, + { name = "pytest-cookies" }, + { name = "pytest-instafail" }, + { name = "pytest-xdist" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "ruff" }, + { name = "sh", marker = "sys_platform != 'win23'" }, + { name = "tox" }, + { name = "tox-uv" }, +] + +[package.dependency-groups] +docs = [ + { name = "myst-parser" }, + { name = "sphinx" }, + { name = "sphinx-autobuild" }, + { name = "sphinx-rtd-theme" }, +] + +[package.metadata] +requires-dist = [ + { name = "binaryornot", specifier = "==0.4.4" }, + { name = "cookiecutter", specifier = "==2.6" }, + { name = "django-upgrade", specifier = "==1.21" }, + { name = "djlint", specifier = "==1.35.2" }, + { name = "gitpython", specifier = "==3.1.43" }, + { name = "jinja2", specifier = "==3.1.4" }, + { name = "pre-commit", specifier = "==3.8" }, + { name = "pygithub", specifier = "==2.4" }, + { name = "pytest", specifier = "==8.3.3" }, + { name = "pytest-cookies", specifier = "==0.7" }, + { name = "pytest-instafail", specifier = "==0.5" }, + { name = "pytest-xdist", specifier = "==3.6.1" }, + { name = "pyyaml", specifier = "==6.0.2" }, + { name = "requests", specifier = "==2.32.3" }, + { name = "ruff", specifier = "==0.7" }, + { name = "sh", marker = "sys_platform != 'win23'", specifier = "==2.0.7" }, + { name = "tox", specifier = "==4.21.2" }, + { name = "tox-uv", specifier = ">=1.11.2" }, +] + +[package.metadata.dependency-groups] +docs = [ + { name = "myst-parser", specifier = ">=4" }, + { name = "sphinx", specifier = ">=8.0.2" }, + { name = "sphinx-autobuild", specifier = ">=2024.10.3" }, + { name = "sphinx-rtd-theme", specifier = ">=3" }, +] + +[[package]] +name = "cryptography" +version = "43.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/ec/9fb9dcf4f91f0e5e76de597256c43eedefd8423aa59be95c70c4c3db426a/cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e", size = 686873 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/46/dcd2eb6840b9452e7fbc52720f3dc54a85eb41e68414733379e8f98e3275/cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74", size = 6239718 }, + { url = "https://files.pythonhosted.org/packages/e8/23/b0713319edff1d8633775b354f8b34a476e4dd5f4cd4b91e488baec3361a/cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895", size = 3808466 }, + { url = "https://files.pythonhosted.org/packages/77/9d/0b98c73cebfd41e4fb0439fe9ce08022e8d059f51caa7afc8934fc1edcd9/cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22", size = 3998060 }, + { url = "https://files.pythonhosted.org/packages/ae/71/e073795d0d1624847f323481f7d84855f699172a632aa37646464b0e1712/cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47", size = 3792596 }, + { url = "https://files.pythonhosted.org/packages/83/25/439a8ddd8058e7f898b7d27c36f94b66c8c8a2d60e1855d725845f4be0bc/cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf", size = 4008355 }, + { url = "https://files.pythonhosted.org/packages/c7/a2/1607f1295eb2c30fcf2c07d7fd0c3772d21dcdb827de2b2730b02df0af51/cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55", size = 3899133 }, + { url = "https://files.pythonhosted.org/packages/5e/64/f41f42ddc9c583737c9df0093affb92c61de7d5b0d299bf644524afe31c1/cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431", size = 4096946 }, + { url = "https://files.pythonhosted.org/packages/cd/cd/d165adcf3e707d6a049d44ade6ca89973549bed0ab3686fa49efdeefea53/cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc", size = 2616826 }, + { url = "https://files.pythonhosted.org/packages/f9/b7/38924229e84c41b0e88d7a5eed8a29d05a44364f85fbb9ddb3984b746fd2/cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778", size = 3078700 }, + { url = "https://files.pythonhosted.org/packages/66/d7/397515233e6a861f921bd0365b162b38e0cc513fcf4f1bdd9cc7bc5a3384/cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66", size = 6242814 }, + { url = "https://files.pythonhosted.org/packages/58/aa/99b2c00a4f54c60d210d6d1759c720ecf28305aa32d6fb1bb1853f415be6/cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5", size = 3809467 }, + { url = "https://files.pythonhosted.org/packages/76/eb/ab783b47b3b9b55371b4361c7ec695144bde1a3343ff2b7a8c1d8fe617bb/cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e", size = 3998617 }, + { url = "https://files.pythonhosted.org/packages/a3/62/62770f34290ebb1b6542bd3f13b3b102875b90aed4804e296f8d2a5ac6d7/cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5", size = 3794003 }, + { url = "https://files.pythonhosted.org/packages/0f/6c/b42660b3075ff543065b2c1c5a3d9bedaadcff8ebce2ee981be2babc2934/cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f", size = 4008774 }, + { url = "https://files.pythonhosted.org/packages/f7/74/028cea86db9315ba3f991e307adabf9f0aa15067011137c38b2fb2aa16eb/cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0", size = 3900098 }, + { url = "https://files.pythonhosted.org/packages/bd/f6/e4387edb55563e2546028ba4c634522fe727693d3cdd9ec0ecacedc75411/cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b", size = 4096867 }, + { url = "https://files.pythonhosted.org/packages/ce/61/55560405e75432bdd9f6cf72fa516cab623b83a3f6d230791bc8fc4afeee/cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf", size = 2616481 }, + { url = "https://files.pythonhosted.org/packages/e6/3d/696e7a0f04555c58a2813d47aaa78cb5ba863c1f453c74a4f45ae772b054/cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709", size = 3081462 }, +] + +[[package]] +name = "cssbeautifier" +version = "1.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "editorconfig" }, + { name = "jsbeautifier" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e5/66/9bfd2d69fb4479d38439076132a620972939f7949015563dce5e61d29a8b/cssbeautifier-1.15.1.tar.gz", hash = "sha256:9f7064362aedd559c55eeecf6b6bed65e05f33488dcbe39044f0403c26e1c006", size = 25673 } + +[[package]] +name = "deprecated" +version = "1.2.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/14/1e41f504a246fc224d2ac264c227975427a85caf37c3979979edb9b1b232/Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3", size = 2974416 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/8d/778b7d51b981a96554f29136cd59ca7880bf58094338085bcf2a979a0e6a/Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", size = 9561 }, +] + +[[package]] +name = "distlib" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/91/e2df406fb4efacdf46871c25cde65d3c6ee5e173b7e5a4547a47bae91920/distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64", size = 609931 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/41/9307e4f5f9976bc8b7fea0b66367734e8faf3ec84bc0d412d8cfabbb66cd/distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", size = 468850 }, +] + +[[package]] +name = "django-upgrade" +version = "1.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tokenize-rt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/30/5215e526e054831cbbc454ce81fb5e825224019824a185e3c011cf199b90/django_upgrade-1.21.0.tar.gz", hash = "sha256:e65021029e7d18b407bd128a8ccb31e5e06685068b37b6b1eaf2f77aa3d3df98", size = 66708 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/76/7a7eeb88657abf8a0f97fbab4f6600be2ec89577e3695c395b47e6860ec5/django_upgrade-1.21.0-py3-none-any.whl", hash = "sha256:a0a7b11d5108fb2d3038cea2382c1332c9be4ff5059a38357fbd28116ebf3803", size = 67796 }, +] + +[[package]] +name = "djlint" +version = "1.35.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama" }, + { name = "cssbeautifier" }, + { name = "html-tag-names" }, + { name = "html-void-elements" }, + { name = "jsbeautifier" }, + { name = "json5" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/ee/ac8ee551ec04d5a214e62d008f40bf309574f103416482e03fb61fbad61c/djlint-1.35.2.tar.gz", hash = "sha256:318de9d4b9b0061a111f8f5164ecbacd8215f449dd4bd5a76d2a691c815ee103", size = 44684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/31/297cad1a493f2910d366418acd0f33da908dfb8b16b8d6c6bafddac4fa7d/djlint-1.35.2-py3-none-any.whl", hash = "sha256:4ba995bad378f2afa77c8ea56ba1c14429d9ff26a18e8ae23bc71eedb9152243", size = 50641 }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, +] + +[[package]] +name = "editorconfig" +version = "0.12.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/85/7b5c2fac7fdc37d959fab714b13b9acb75884490dcc0e8b1dc5e64105084/EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80", size = 13278 } + +[[package]] +name = "execnet" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612 }, +] + +[[package]] +name = "filelock" +version = "3.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, +] + +[[package]] +name = "gitdb" +version = "4.0.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/0d/bbb5b5ee188dec84647a4664f3e11b06ade2bde568dbd489d9d64adef8ed/gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b", size = 394469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/5b/8f0c4a5bb9fd491c277c21eff7ccae71b47d43c4446c9d0c6cff2fe8c2c4/gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4", size = 62721 }, +] + +[[package]] +name = "gitpython" +version = "3.1.43" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/a1/106fd9fa2dd989b6fb36e5893961f82992cf676381707253e0bf93eb1662/GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c", size = 214149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/bd/cc3a402a6439c15c3d4294333e13042b915bbeab54edc457c723931fed3f/GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff", size = 207337 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "html-tag-names" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/7c/8c0dc3c5650036127fb4629d31cadf6cbdd57e21a77f9793fa8b2c8a3241/html-tag-names-0.1.2.tar.gz", hash = "sha256:04924aca48770f36b5a41c27e4d917062507be05118acb0ba869c97389084297", size = 15173 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/e4/242318dcaa06d8525ff72332fc15cea2cffb84c06cfc73c7da6c6b45801a/html_tag_names-0.1.2-py3-none-any.whl", hash = "sha256:eeb69ef21078486b615241f0393a72b41352c5219ee648e7c61f5632d26f0420", size = 15218 }, +] + +[[package]] +name = "html-void-elements" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/5c/5f17d77256bf78ca98647517fadee50575e75d812daa01352c31d89d5bf2/html-void-elements-0.1.0.tar.gz", hash = "sha256:931b88f84cd606fee0b582c28fcd00e41d7149421fb673e1e1abd2f0c4f231f0", size = 14766 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/0a/373f28a1cf37f8c9aa23c82cbcac7197ddea95c88b5a3eaa564cdb8de375/html_void_elements-0.1.0-py3-none-any.whl", hash = "sha256:784cf39db03cdeb017320d9301009f8f3480f9d7b254d0974272e80e0cb5e0d2", size = 14889 }, +] + +[[package]] +name = "identify" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/f4/8e8f7db397a7ce20fbdeac5f25adaf567fc362472432938d25556008e03a/identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf", size = 99116 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/6c/a4f39abe7f19600b74528d0c717b52fff0b300bb0161081510d39c53cb00/identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0", size = 98962 }, +] + +[[package]] +name = "idna" +version = "3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/e349c5e6d4543326c6883ee9491e3921e0d07b55fdf3cce184b40d63e72a/idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603", size = 189467 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/7e/d71db821f177828df9dea8c42ac46473366f191be53080e552e628aad991/idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", size = 66894 }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, +] + +[[package]] +name = "jsbeautifier" +version = "1.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "editorconfig" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/3e/dd37e1a7223247e3ef94714abf572415b89c4e121c4af48e9e4c392e2ca0/jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24", size = 75606 } + +[[package]] +name = "json5" +version = "0.9.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/59/51b032d53212a51f17ebbcc01bd4217faab6d6c09ed0d856a987a5f42bbc/json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae", size = 40332 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/3c/4f8791ee53ab9eeb0b022205aa79387119a74cc9429582ce04098e6fc540/json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f", size = 30109 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215 }, + { url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069 }, + { url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452 }, + { url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462 }, + { url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869 }, + { url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906 }, + { url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296 }, + { url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 }, + { url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 }, + { url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "myst-parser" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "packaging" +version = "24.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pre-commit" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643 }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + +[[package]] +name = "pygithub" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "pynacl" }, + { name = "requests" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/a0/1e8b8ca88df9857836f5bf8e3ee15dfb810d19814ef700b12f99ce11f691/pygithub-2.4.0.tar.gz", hash = "sha256:6601e22627e87bac192f1e2e39c6e6f69a43152cfb8f307cee575879320b3051", size = 3476673 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/f3/e185613c411757c0c18b904ea2db173f2872397eddf444a3fe8cdde47077/PyGithub-2.4.0-py3-none-any.whl", hash = "sha256:81935aa4bdc939fba98fee1cb47422c09157c56a27966476ff92775602b9ee24", size = 362599 }, +] + +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + +[[package]] +name = "pyjwt" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344 }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pynacl" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/22/27582568be639dfe22ddb3902225f91f2f17ceff88ce80e4db396c8986da/PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", size = 3392854 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/75/0b8ede18506041c0bf23ac4d8e2971b4161cd6ce630b177d0a08eb0d8857/PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", size = 349920 }, + { url = "https://files.pythonhosted.org/packages/59/bb/fddf10acd09637327a97ef89d2a9d621328850a72f1fdc8c08bdf72e385f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", size = 601722 }, + { url = "https://files.pythonhosted.org/packages/5d/70/87a065c37cca41a75f2ce113a5a2c2aa7533be648b184ade58971b5f7ccc/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", size = 680087 }, + { url = "https://files.pythonhosted.org/packages/ee/87/f1bb6a595f14a327e8285b9eb54d41fef76c585a0edef0a45f6fc95de125/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", size = 856678 }, + { url = "https://files.pythonhosted.org/packages/66/28/ca86676b69bf9f90e710571b67450508484388bfce09acf8a46f0b8c785f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", size = 1133660 }, + { url = "https://files.pythonhosted.org/packages/3d/85/c262db650e86812585e2bc59e497a8f59948a005325a11bbbc9ecd3fe26b/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", size = 663824 }, + { url = "https://files.pythonhosted.org/packages/fd/1a/cc308a884bd299b651f1633acb978e8596c71c33ca85e9dc9fa33a5399b9/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", size = 1117912 }, + { url = "https://files.pythonhosted.org/packages/25/2d/b7df6ddb0c2a33afdb358f8af6ea3b8c4d1196ca45497dd37a56f0c122be/PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543", size = 204624 }, + { url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", size = 212141 }, +] + +[[package]] +name = "pyproject-api" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/19/441e0624a8afedd15bbcce96df1b80479dd0ff0d965f5ce8fde4f2f6ffad/pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496", size = 22340 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/f4/3c4ddfcc0c19c217c6de513842d286de8021af2f2ab79bbb86c00342d778/pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228", size = 13100 }, +] + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "pytest-cookies" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cookiecutter" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/2e/11a3e1abb4bbf10e0af3f194ba4c55600de3fe52417ef3594c18d28ecdbe/pytest-cookies-0.7.0.tar.gz", hash = "sha256:1aaa6b4def8238d0d1709d3d773b423351bfb671c1e3438664d824e0859d6308", size = 8840 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/f7/438af2f3a6c58f81d22c126707ee5d079f653a76961f4fb7d995e526a9c4/pytest_cookies-0.7.0-py3-none-any.whl", hash = "sha256:52770f090d77b16428f6a24a208e6be76addb2e33458035714087b4de49389ea", size = 6386 }, +] + +[[package]] +name = "pytest-instafail" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/bd/e0ba6c3cd20b9aa445f0af229f3a9582cce589f083537978a23e6f14e310/pytest-instafail-0.5.0.tar.gz", hash = "sha256:33a606f7e0c8e646dc3bfee0d5e3a4b7b78ef7c36168cfa1f3d93af7ca706c9e", size = 5849 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/c0/c32dc39fc172e684fdb3d30169843efb65c067be1e12689af4345731126e/pytest_instafail-0.5.0-py3-none-any.whl", hash = "sha256:6855414487e9e4bb76a118ce952c3c27d3866af15487506c4ded92eb72387819", size = 4176 }, +] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-slugify" +version = "8.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "text-unidecode" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, +] + +[[package]] +name = "regex" +version = "2024.7.24" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/51/64256d0dc72816a4fe3779449627c69ec8fee5a5625fd60ba048f53b3478/regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506", size = 393485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/26/f505782f386ac0399a9237571833f187414882ab6902e2e71a1ecb506835/regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86", size = 471748 }, + { url = "https://files.pythonhosted.org/packages/bb/1d/ea9a21beeb433dbfca31ab82867d69cb67ff8674af9fab6ebd55fa9d3387/regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad", size = 282841 }, + { url = "https://files.pythonhosted.org/packages/9b/f2/c6182095baf0a10169c34e87133a8e73b2e816a80035669b1278e927685e/regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9", size = 279114 }, + { url = "https://files.pythonhosted.org/packages/72/58/b5161bf890b6ca575a25685f19a4a3e3b6f4a072238814f8658123177d84/regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289", size = 789749 }, + { url = "https://files.pythonhosted.org/packages/09/fb/5381b19b62f3a3494266be462f6a015a869cf4bfd8e14d6e7db67e2c8069/regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9", size = 831666 }, + { url = "https://files.pythonhosted.org/packages/3d/6d/2a21c85f970f9be79357d12cf4b97f4fc6bf3bf6b843c39dabbc4e5f1181/regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c", size = 817544 }, + { url = "https://files.pythonhosted.org/packages/f9/ae/5f23e64f6cf170614237c654f3501a912dfb8549143d4b91d1cd13dba319/regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440", size = 790854 }, + { url = "https://files.pythonhosted.org/packages/29/0a/d04baad1bbc49cdfb4aef90c4fc875a60aaf96d35a1616f1dfe8149716bc/regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610", size = 779242 }, + { url = "https://files.pythonhosted.org/packages/3a/27/b242a962f650c3213da4596d70e24c7c1c46e3aa0f79f2a81164291085f8/regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5", size = 776932 }, + { url = "https://files.pythonhosted.org/packages/9c/ae/de659bdfff80ad2c0b577a43dd89dbc43870a4fc4bbf604e452196758e83/regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799", size = 784521 }, + { url = "https://files.pythonhosted.org/packages/d4/ac/eb6a796da0bdefbf09644a7868309423b18d344cf49963a9d36c13502d46/regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05", size = 854548 }, + { url = "https://files.pythonhosted.org/packages/56/77/fde8d825dec69e70256e0925af6c81eea9acf0a634d3d80f619d8dcd6888/regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94", size = 853345 }, + { url = "https://files.pythonhosted.org/packages/ff/04/2b79ad0bb9bc05ab4386caa2c19aa047a66afcbdfc2640618ffc729841e4/regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38", size = 781414 }, + { url = "https://files.pythonhosted.org/packages/bf/71/d0af58199283ada7d25b20e416f5b155f50aad99b0e791c0966ff5a1cd00/regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc", size = 258125 }, + { url = "https://files.pythonhosted.org/packages/95/b3/10e875c45c60b010b66fc109b899c6fc4f05d485fe1d54abff98ce791124/regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908", size = 269162 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rich" +version = "13.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/60/5959113cae0ce512cf246a6871c623117330105a0d5f59b4e26138f2c9cc/rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4", size = 222072 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/d9/c2a126eeae791e90ea099d05cb0515feea3688474b978343f3cdcfe04523/rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc", size = 241597 }, +] + +[[package]] +name = "ruff" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/c7/f3367d1da5d568192968c5c9e7f3d51fb317b9ac04828493b23d8fce8ce6/ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b", size = 3146645 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/59/a0275a0913f3539498d116046dd679cd657fe3b7caf5afe1733319414932/ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628", size = 10434007 }, + { url = "https://files.pythonhosted.org/packages/cd/94/da0ba5f956d04c90dd899209904210600009dcda039ce840d83eb4298c7d/ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737", size = 10048066 }, + { url = "https://files.pythonhosted.org/packages/57/1d/e5cc149ecc46e4f203403a79ccd170fad52d316f98b87d0f63b1945567db/ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06", size = 9711389 }, + { url = "https://files.pythonhosted.org/packages/05/67/fb7ea2c869c539725a16c5bc294e9aa34f8b1b6fe702f1d173a5da517c2b/ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be", size = 10755174 }, + { url = "https://files.pythonhosted.org/packages/5f/f0/13703bc50536a0613ea3dce991116e5f0917a1f05528c6ab738b33c08d3f/ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa", size = 10196040 }, + { url = "https://files.pythonhosted.org/packages/99/c1/77b04ab20324ab03d333522ee55fb0f1c38e3ca0d326b4905f82ce6b6c70/ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495", size = 11033684 }, + { url = "https://files.pythonhosted.org/packages/f2/97/f463334dc4efeea3551cd109163df15561c18a1c3ec13d51643740fd36ba/ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598", size = 11803700 }, + { url = "https://files.pythonhosted.org/packages/b4/f8/a31d40c4bb92933d376a53e7c5d0245d9b27841357e4820e96d38f54b480/ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e", size = 11347848 }, + { url = "https://files.pythonhosted.org/packages/83/62/0c133b35ddaf91c65c30a56718b80bdef36bfffc35684d29e3a4878e0ea3/ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914", size = 12480632 }, + { url = "https://files.pythonhosted.org/packages/46/96/464058dd1d980014fb5aa0a1254e78799efb3096fc7a4823cd66a1621276/ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9", size = 10941919 }, + { url = "https://files.pythonhosted.org/packages/a0/f7/bda37ec77986a435dde44e1f59374aebf4282a5fa9cf17735315b847141f/ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4", size = 10745519 }, + { url = "https://files.pythonhosted.org/packages/c2/33/5f77fc317027c057b61a848020a47442a1cbf12e592df0e41e21f4d0f3bd/ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9", size = 10284872 }, + { url = "https://files.pythonhosted.org/packages/ff/50/98aec292bc9537f640b8d031c55f3414bf15b6ed13b3e943fed75ac927b9/ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d", size = 10600334 }, + { url = "https://files.pythonhosted.org/packages/f2/85/12607ae3201423a179b8cfadc7cb1e57d02cd0135e45bd0445acb4cef327/ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11", size = 11017333 }, + { url = "https://files.pythonhosted.org/packages/d4/7f/3b85a56879e705d5f46ec14daf8a439fca05c3081720fe3dc3209100922d/ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec", size = 8570962 }, + { url = "https://files.pythonhosted.org/packages/39/9f/c5ee2b40d377354dabcc23cff47eb299de4b4d06d345068f8f8cc1eadac8/ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2", size = 9365544 }, + { url = "https://files.pythonhosted.org/packages/89/8b/ee1509f60148cecba644aa718f6633216784302458340311898aaf0b1bed/ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e", size = 8695763 }, +] + +[[package]] +name = "sh" +version = "2.0.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/7a/5148402146d360a6d15922814a3b065b52be3a5fe878a8834d3ce4e7d33f/sh-2.0.7.tar.gz", hash = "sha256:029d45198902bfb967391eccfd13a88d92f7cebd200411e93f99ebacc6afbb35", size = 345477 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/c2/79f9dea6fc544c0eb79ed5018a38860c52d597c4be66c2cf2029bea5b3fd/sh-2.0.7-py3-none-any.whl", hash = "sha256:2f2f79a65abd00696cf2e9ad26508cf8abb6dba5745f40255f1c0ded2876926d", size = 38103 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "smmap" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/04/b5bf6d21dc4041000ccba7eb17dd3055feb237e7ffc2c20d3fae3af62baa/smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62", size = 22291 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a5/10f97f73544edcdef54409f1d839f6049a0d79df68adbc1ceb24d1aaca42/smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da", size = 24282 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, +] + +[[package]] +name = "sphinx" +version = "8.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/a7/3cc3d6dcad70aba2e32a3ae8de5a90026a0a2fdaaa0756925e3a120249b6/sphinx-8.0.2.tar.gz", hash = "sha256:0cce1ddcc4fd3532cf1dd283bc7d886758362c5c1de6598696579ce96d8ffa5b", size = 8189041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/61/2ad169c6ff1226b46e50da0e44671592dbc6d840a52034a0193a99b28579/sphinx-8.0.2-py3-none-any.whl", hash = "sha256:56173572ae6c1b9a38911786e206a110c9749116745873feae4f9ce88e59391d", size = 3498950 }, +] + +[[package]] +name = "sphinx-autobuild" +version = "2024.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "sphinx" }, + { name = "starlette" }, + { name = "uvicorn" }, + { name = "watchfiles" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908 }, +] + +[[package]] +name = "sphinx-rtd-theme" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "sphinx" }, + { name = "sphinxcontrib-jquery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/f8/2667f9cab89827528596588dd9de6f937f52e5c6e87e6f28ecb866955551/sphinx_rtd_theme-3.0.0.tar.gz", hash = "sha256:905d67de03217fd3d76fbbdd992034ac8e77044ef8063a544dda1af74d409e08", size = 7620317 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/11/81e5bfffdbd6dd3173d5ee29b4629a03ba80d38d4a250e7a8504af22d5c2/sphinx_rtd_theme-3.0.0-py2.py3-none-any.whl", hash = "sha256:1ffe1539957775bfa0a7331370de7dc145b6eac705de23365dc55c5d94bb08e7", size = 7655495 }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, +] + +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104 }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, +] + +[[package]] +name = "starlette" +version = "0.39.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/0a/62fbd5697f6174041f9b4e2e377b6f383f9189b77dbb7d73d24624caca1d/starlette-0.39.2.tar.gz", hash = "sha256:caaa3b87ef8518ef913dac4f073dea44e85f73343ad2bdc17941931835b2a26a", size = 2573080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/f0/04547f776c8845be46df4bdd1f11159c088bd39e916f35d7da1b9f6eb3ef/starlette-0.39.2-py3-none-any.whl", hash = "sha256:134dd6deb655a9775991d352312d53f1879775e5cc8a481f966e83416a2c3f71", size = 73219 }, +] + +[[package]] +name = "text-unidecode" +version = "1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154 }, +] + +[[package]] +name = "tokenize-rt" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/09/6257dabdeab5097d72c5d874f29b33cd667ec411af6667922d84f85b79b5/tokenize_rt-6.0.0.tar.gz", hash = "sha256:b9711bdfc51210211137499b5e355d3de5ec88a85d2025c520cbb921b5194367", size = 5360 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/c2/44486862562c6902778ccf88001ad5ea3f8da5c030c638cac8be72f65b40/tokenize_rt-6.0.0-py2.py3-none-any.whl", hash = "sha256:d4ff7ded2873512938b4f8cbb98c9b07118f01d30ac585a30d7a88353ca36d22", size = 5869 }, +] + +[[package]] +name = "tox" +version = "4.21.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "chardet" }, + { name = "colorama" }, + { name = "filelock" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "pluggy" }, + { name = "pyproject-api" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/db/ba5b1a4cf664f221a33c3cbb1adf40ccccbbd13f5eec6d9d7291c7a39e44/tox-4.21.2.tar.gz", hash = "sha256:49381ff102296753e378fa5ff30e42a35e695f149b4dbf8a2c49d15fdb5797b2", size = 188539 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/20/168300f3f334e255b618322dce14b86a5c423aab05f28be14d1a2d6af14a/tox-4.21.2-py3-none-any.whl", hash = "sha256:13d996adcd792e7c82994b0e116d85efd84f0c6d185254d83d156f73f86b2038", size = 165698 }, +] + +[[package]] +name = "tox-uv" +version = "1.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tox" }, + { name = "uv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/cf/7062095ae8c5e6a25d7153b9b1c4a16e52df61859160b3db66a60f055d1e/tox_uv-1.11.2.tar.gz", hash = "sha256:a7aded5c3fb69f055b523357988c1055bb573e91bfd7ecfb9b5233ebcab5d10b", size = 13628 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/50/5b9f2d9d10bfdfb1f748bbcdb6eae5a5da6d2a7adbef9a4df34aceac6eca/tox_uv-1.11.2-py3-none-any.whl", hash = "sha256:7f8f1737b3277e1cddcb5b89fcc5931d04923562c940ae60f29e140908566df2", size = 11265 }, +] + +[[package]] +name = "tqdm" +version = "4.66.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/83/6ba9844a41128c62e810fddddd72473201f3eacde02046066142a2d96cc5/tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad", size = 169504 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/5d/acf5905c36149bbaec41ccf7f2b68814647347b72075ac0b1fe3022fdc73/tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd", size = 78351 }, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20240821" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/11/aae06ddb6a90cf8ba078be6dbe47f904d2efdf451f9859248b436c945ca4/types-python-dateutil-2.9.0.20240821.tar.gz", hash = "sha256:9649d1dcb6fef1046fb18bebe9ea2aa0028b160918518c34589a46045f6ebd98", size = 9122 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/ba/2a4750156272f180f8209f87656ae92e0aeb14f9864976aa90cbd9f21eda/types_python_dateutil-2.9.0.20240821-py3-none-any.whl", hash = "sha256:f5889fcb4e63ed4aaa379b44f93c32593d50b9a94c9a60a0c854d8cc3511cd57", size = 9668 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "urllib3" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/6d/fa469ae21497ddc8bc93e5877702dca7cb8f911e337aca7452b5724f1bb6/urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168", size = 292266 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/1c/89ffc63a9605b583d5df2be791a27bc1a42b7c32bab68d3c8f2f73a98cd4/urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", size = 121444 }, +] + +[[package]] +name = "uv" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/5c/916bc1eacf2a5ca8cd33dc5d51cc9f64735ef6faccefd2f689d34b9eb23a/uv-0.4.6.tar.gz", hash = "sha256:5c142b0082844c9eca4a303e13cf1d286622f49af70e8f922b833cb3e667b2e3", size = 1859862 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/0b/eb3ca96aa881e5a88e704b5d7aec99610cc0d64702c984da24ae2a5682c2/uv-0.4.6-py3-none-linux_armv6l.whl", hash = "sha256:e0360bb3b094d106d1fa00b3a15c69ccd0d1593682c33affb1b94367a248a378", size = 11097185 }, + { url = "https://files.pythonhosted.org/packages/30/1c/3c27e0fe4ad1a5b9792a61e92283457beec7949863c1b3435ef79cfad179/uv-0.4.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:16ae9f0a9b3bb6db755d057c4ff55b0e82fbb09243a1dce02720268718c0a11c", size = 11558713 }, + { url = "https://files.pythonhosted.org/packages/5a/f8/93e8574c91ef7884154af6f80d213ee11f7710201c2fe0d73a9f9556bd2d/uv-0.4.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e6e925f661aeaf392cc102d3c2f6f05b6994519bd7a36578f836a4bbf42ee813", size = 10668873 }, + { url = "https://files.pythonhosted.org/packages/0f/f3/1b2625aa119ee66fc7a6c3c1026c66707a5f99a0c86e5d49fd237f45f0b5/uv-0.4.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:86f78fb80989b524ce7da705fb5af613339a8eadbcf9e230bb16e8a7d383c2ce", size = 11006778 }, + { url = "https://files.pythonhosted.org/packages/30/01/cbe8bc23dd1e32b77b652e31757fcdb636c194bd13acf32e2438a72daf5a/uv-0.4.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a66442018e09467222824b685b2d401053dfdbc3b5f6fb1f863ca2eb2560076a", size = 10896611 }, + { url = "https://files.pythonhosted.org/packages/5f/11/46328fc827f4bf35b715a97f067a61563f5421e0e35ba6fd16b9de82771a/uv-0.4.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4db5c2b770bb0e95a4d32dc19943109310c86103fe8b8ecf0d18dfe1a4f2a212", size = 11563597 }, + { url = "https://files.pythonhosted.org/packages/3e/b1/8774d2fab7cbec44e68aafa58745ce9ec2af26a17e1d6ad86284fcd79acf/uv-0.4.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9bf486c6331197a301bff253945b233d852ed2fdc322e93c78d382092813161d", size = 12341920 }, + { url = "https://files.pythonhosted.org/packages/81/cc/f53877269f75d4bba8e4bad131b6aa2d39d6545afff2c703be2c9608ad50/uv-0.4.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2bd9879eb8ee9de0fa532136ddfe76c1b425520fa6de52f5d023567e226a08b5", size = 12142981 }, + { url = "https://files.pythonhosted.org/packages/13/b8/373d86645849320eb61e213e657084f7cf6be2d6f2b13e413cb555876659/uv-0.4.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831be24db75938ebb01f9e6b7bcf24683b9661f91aab4108636207ba9dc6a2b9", size = 15237758 }, + { url = "https://files.pythonhosted.org/packages/29/bb/0accc6347df2f88efd092aa821eba46edebac726659a0160590989860b77/uv-0.4.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8cdadb99b842494a0a39e0184cacfb4af722fdbea3d4cb5ee3b25ea0b824292", size = 11925644 }, + { url = "https://files.pythonhosted.org/packages/a6/3d/32dfd30e22dca7ea76a1298d42cdda3c3e80be6763d9d87a14a5b9bbb38b/uv-0.4.6-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:081793fc66286ad0f9b939b5ab3239d20d486eff7b8f7c574d3fdcd44536595e", size = 11116370 }, + { url = "https://files.pythonhosted.org/packages/72/8a/18731ce4fae35a205efabd5f5f1979821fcb66f4d6ad6818e79b0b0bce20/uv-0.4.6-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:346db85ce897c6c782af33504922a84c583180906244ea224a306e2393f396f4", size = 10901947 }, + { url = "https://files.pythonhosted.org/packages/a7/08/665613bc0cb41f85afc1059b6b25815d5904020822d3a958986aa2d2eee6/uv-0.4.6-py3-none-musllinux_1_1_i686.whl", hash = "sha256:5d507dee4767d054171dcdbc1ac1fa7d15a7579e20ff988162c749f09a95f943", size = 11389509 }, + { url = "https://files.pythonhosted.org/packages/23/47/5a30dd313ce2c33c3637ea17c65fcddbfbf005fc456c6025f18e99bf56e8/uv-0.4.6-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:397e02640cbddc4230da8d614ef9a7138a024b9ce396f2a27013cd254e97480d", size = 13150151 }, + { url = "https://files.pythonhosted.org/packages/44/0e/37f4040363d05a0cca7f35759d34bb2ca10e9d89a647b8c6c3888ed8b643/uv-0.4.6-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:93165aa25f5278a0844c91a367140e725d384633946705651ff8e70757c2e92f", size = 12048785 }, + { url = "https://files.pythonhosted.org/packages/b4/1e/0b21ab16140d252d211411b4f61e2e448830f76b5cd5b88bb8dd827e5c7d/uv-0.4.6-py3-none-win32.whl", hash = "sha256:954f91be1b8e531e9fd3d6d549ec1638c20ce4e77dc6e272c5c5d21b8c952f11", size = 11320666 }, + { url = "https://files.pythonhosted.org/packages/d8/44/0231b506b5c2ea9da3242142fc7e53696060f548bb6f1cebca89268fa238/uv-0.4.6-py3-none-win_amd64.whl", hash = "sha256:86ecd19796363f0161600120d81ce7e3d17d54cce6d25017190daa608a8ab535", size = 12520653 }, +] + +[[package]] +name = "uvicorn" +version = "0.31.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/96/ee52d900f8e41cc35eaebfda76f3619c2e45b741f3ee957d6fe32be1b2aa/uvicorn-0.31.0.tar.gz", hash = "sha256:13bc21373d103859f68fe739608e2eb054a816dea79189bc3ca08ea89a275906", size = 77140 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/12/206aca5442524d16be7702d08b453d7c274c86fd759266b1f709d4ef43ba/uvicorn-0.31.0-py3-none-any.whl", hash = "sha256:cac7be4dd4d891c363cd942160a7b02e69150dcbc7a36be04d5f4af4b17c8ced", size = 63656 }, +] + +[[package]] +name = "virtualenv" +version = "20.26.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/40/abc5a766da6b0b2457f819feab8e9203cbeae29327bd241359f866a3da9d/virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48", size = 9372482 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/90/57b8ac0c8a231545adc7698c64c5a36fa7cd8e376c691b9bde877269f2eb/virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2", size = 5999862 }, +] + +[[package]] +name = "watchfiles" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/27/2ba23c8cc85796e2d41976439b08d52f691655fdb9401362099502d1f0cf/watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1", size = 37870 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/82/92a7bb6dc82d183e304a5f84ae5437b59ee72d48cee805a9adda2488b237/watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a", size = 374137 }, + { url = "https://files.pythonhosted.org/packages/87/91/49e9a497ddaf4da5e3802d51ed67ff33024597c28f652b8ab1e7c0f5718b/watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370", size = 367733 }, + { url = "https://files.pythonhosted.org/packages/0d/d8/90eb950ab4998effea2df4cf3a705dc594f6bc501c5a353073aa990be965/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6", size = 437322 }, + { url = "https://files.pythonhosted.org/packages/6c/a2/300b22e7bc2a222dd91fce121cefa7b49aa0d26a627b2777e7bdfcf1110b/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b", size = 433409 }, + { url = "https://files.pythonhosted.org/packages/99/44/27d7708a43538ed6c26708bcccdde757da8b7efb93f4871d4cc39cffa1cc/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e", size = 452142 }, + { url = "https://files.pythonhosted.org/packages/b0/ec/c4e04f755be003129a2c5f3520d2c47026f00da5ecb9ef1e4f9449637571/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea", size = 469414 }, + { url = "https://files.pythonhosted.org/packages/c5/4e/cdd7de3e7ac6432b0abf282ec4c1a1a2ec62dfe423cf269b86861667752d/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f", size = 472962 }, + { url = "https://files.pythonhosted.org/packages/27/69/e1da9d34da7fc59db358424f5d89a56aaafe09f6961b64e36457a80a7194/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234", size = 425705 }, + { url = "https://files.pythonhosted.org/packages/e8/c1/24d0f7357be89be4a43e0a656259676ea3d7a074901f47022f32e2957798/watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef", size = 612851 }, + { url = "https://files.pythonhosted.org/packages/c7/af/175ba9b268dec56f821639c9893b506c69fd999fe6a2e2c51de420eb2f01/watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968", size = 594868 }, + { url = "https://files.pythonhosted.org/packages/44/81/1f701323a9f70805bc81c74c990137123344a80ea23ab9504a99492907f8/watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444", size = 264109 }, + { url = "https://files.pythonhosted.org/packages/b4/0b/32cde5bc2ebd9f351be326837c61bdeb05ad652b793f25c91cac0b48a60b/watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896", size = 277055 }, + { url = "https://files.pythonhosted.org/packages/4b/81/daade76ce33d21dbec7a15afd7479de8db786e5f7b7d249263b4ea174e08/watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418", size = 266169 }, +] + +[[package]] +name = "websockets" +version = "13.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/73/9223dbc7be3dcaf2a7bbf756c351ec8da04b1fa573edaf545b95f6b0c7fd/websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878", size = 158549 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/46/c426282f543b3c0296cf964aa5a7bb17e984f58dde23460c3d39b3148fcf/websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc", size = 157821 }, + { url = "https://files.pythonhosted.org/packages/aa/85/22529867010baac258da7c45848f9415e6cf37fef00a43856627806ffd04/websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49", size = 155480 }, + { url = "https://files.pythonhosted.org/packages/29/2c/bdb339bfbde0119a6e84af43ebf6275278698a2241c2719afc0d8b0bdbf2/websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd", size = 155715 }, + { url = "https://files.pythonhosted.org/packages/9f/d0/8612029ea04c5c22bf7af2fd3d63876c4eaeef9b97e86c11972a43aa0e6c/websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0", size = 165647 }, + { url = "https://files.pythonhosted.org/packages/56/04/1681ed516fa19ca9083f26d3f3a302257e0911ba75009533ed60fbb7b8d1/websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6", size = 164592 }, + { url = "https://files.pythonhosted.org/packages/38/6f/a96417a49c0ed132bb6087e8e39a37db851c70974f5c724a4b2a70066996/websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9", size = 165012 }, + { url = "https://files.pythonhosted.org/packages/40/8b/fccf294919a1b37d190e86042e1a907b8f66cff2b61e9befdbce03783e25/websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68", size = 165311 }, + { url = "https://files.pythonhosted.org/packages/c1/61/f8615cf7ce5fe538476ab6b4defff52beb7262ff8a73d5ef386322d9761d/websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14", size = 164692 }, + { url = "https://files.pythonhosted.org/packages/5c/f1/a29dd6046d3a722d26f182b783a7997d25298873a14028c4760347974ea3/websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf", size = 164686 }, + { url = "https://files.pythonhosted.org/packages/0f/99/ab1cdb282f7e595391226f03f9b498f52109d25a2ba03832e21614967dfa/websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c", size = 158712 }, + { url = "https://files.pythonhosted.org/packages/46/93/e19160db48b5581feac8468330aa11b7292880a94a37d7030478596cc14e/websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3", size = 159145 }, + { url = "https://files.pythonhosted.org/packages/56/27/96a5cd2626d11c8280656c6c71d8ab50fe006490ef9971ccd154e0c42cd2/websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f", size = 152134 }, +] + +[[package]] +name = "wrapt" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/4c/063a912e20bcef7124e0df97282a8af3ff3e4b603ce84c481d6d7346be0a/wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", size = 53972 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/17/224132494c1e23521868cdd57cd1e903f3b6a7ba6996b7b8f077ff8ac7fe/wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", size = 37614 }, + { url = "https://files.pythonhosted.org/packages/6a/d7/cfcd73e8f4858079ac59d9db1ec5a1349bc486ae8e9ba55698cc1f4a1dff/wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", size = 38316 }, + { url = "https://files.pythonhosted.org/packages/7e/79/5ff0a5c54bda5aec75b36453d06be4f83d5cd4932cc84b7cb2b52cee23e2/wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", size = 86322 }, + { url = "https://files.pythonhosted.org/packages/c4/81/e799bf5d419f422d8712108837c1d9bf6ebe3cb2a81ad94413449543a923/wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", size = 79055 }, + { url = "https://files.pythonhosted.org/packages/62/62/30ca2405de6a20448ee557ab2cd61ab9c5900be7cbd18a2639db595f0b98/wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", size = 87291 }, + { url = "https://files.pythonhosted.org/packages/49/4e/5d2f6d7b57fc9956bf06e944eb00463551f7d52fc73ca35cfc4c2cdb7aed/wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", size = 90374 }, + { url = "https://files.pythonhosted.org/packages/a6/9b/c2c21b44ff5b9bf14a83252a8b973fb84923764ff63db3e6dfc3895cf2e0/wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", size = 83896 }, + { url = "https://files.pythonhosted.org/packages/14/26/93a9fa02c6f257df54d7570dfe8011995138118d11939a4ecd82cb849613/wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", size = 91738 }, + { url = "https://files.pythonhosted.org/packages/a2/5b/4660897233eb2c8c4de3dc7cefed114c61bacb3c28327e64150dc44ee2f6/wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", size = 35568 }, + { url = "https://files.pythonhosted.org/packages/5c/cc/8297f9658506b224aa4bd71906447dea6bb0ba629861a758c28f67428b91/wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", size = 37653 }, + { url = "https://files.pythonhosted.org/packages/ff/21/abdedb4cdf6ff41ebf01a74087740a709e2edb146490e4d9beea054b0b7a/wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", size = 23362 }, +] diff --git a/{{cookiecutter.project_slug}}/.drone.yml b/{{cookiecutter.project_slug}}/.drone.yml index d6c13e62bf..20d6fb1bbd 100644 --- a/{{cookiecutter.project_slug}}/.drone.yml +++ b/{{cookiecutter.project_slug}}/.drone.yml @@ -7,7 +7,7 @@ environment: POSTGRES_DB: 'test_{{ cookiecutter.project_slug }}' POSTGRES_HOST_AUTH_METHOD: trust {%- if cookiecutter.use_celery == 'y' %} - CELERY_BROKER_URL: 'redis://redis:6379/0' + REDIS_URL: 'redis://redis:6379/0' {%- endif %} steps: diff --git a/{{cookiecutter.project_slug}}/.github/dependabot.yml b/{{cookiecutter.project_slug}}/.github/dependabot.yml index be52c68d55..4b89735588 100644 --- a/{{cookiecutter.project_slug}}/.github/dependabot.yml +++ b/{{cookiecutter.project_slug}}/.github/dependabot.yml @@ -1,5 +1,5 @@ # Config for Dependabot updates. See Documentation here: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: @@ -83,6 +83,16 @@ updates: schedule: interval: 'daily' +{%- if cookiecutter.cloud_provider == 'None' %} + + - package-ecosystem: 'docker' + # Look for a `Dockerfile` in the `compose/production/nginx` directory + directory: 'compose/production/nginx/' + # Every weekday + schedule: + interval: 'daily' +{%- endif %} + {%- endif %} # Enable version updates for Python/Pip - Production diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml index 0d8ed0cada..6bb555063f 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: env: {%- if cookiecutter.use_celery == 'y' %} - CELERY_BROKER_URL: 'redis://localhost:6379/0' + REDIS_URL: 'redis://localhost:6379/0' {%- endif %} # postgres://user:password@host:port/database DATABASE_URL: 'postgres://postgres:postgres@localhost:5432/postgres' diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index 71216bc7a2..9c7cd53672 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -8,7 +8,7 @@ variables: POSTGRES_DB: 'test_{{ cookiecutter.project_slug }}' POSTGRES_HOST_AUTH_METHOD: trust {%- if cookiecutter.use_celery == 'y' %} - CELERY_BROKER_URL: 'redis://redis:6379/0' + REDIS_URL: 'redis://redis:6379/0' {%- endif %} precommit: diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml index a431cf405a..3ede4246a6 100644 --- a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -1,12 +1,12 @@ exclude: '^docs/|/migrations/|devcontainer.json' -default_stages: [commit] +default_stages: [pre-commit] default_language_version: python: python3.12 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -28,14 +28,14 @@ repos: exclude: '{{cookiecutter.project_slug}}/templates/|node_modules/|.*\.min\.(js|css)$' - repo: https://github.com/adamchainz/django-upgrade - rev: '1.20.0' + rev: '1.22.1' hooks: - id: django-upgrade args: ['--target-version', '5.0'] # Run the Ruff linter. - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.3 + rev: v0.7.1 hooks: # Linter - id: ruff @@ -44,7 +44,7 @@ repos: - id: ruff-format - repo: https://github.com/Riverside-Healthcare/djLint - rev: v1.35.2 + rev: v1.35.4 hooks: - id: djlint-reformat-django - id: djlint-django diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md index 7b174850d3..bae194a787 100644 --- a/{{cookiecutter.project_slug}}/README.md +++ b/{{cookiecutter.project_slug}}/README.md @@ -14,7 +14,7 @@ License: {{cookiecutter.open_source_license}} ## Settings -Moved to [settings](http://cookiecutter-django.readthedocs.io/en/latest/settings.html). +Moved to [settings](https://cookiecutter-django.readthedocs.io/en/latest/1-getting-started/settings.html). ## Basic Commands @@ -48,7 +48,7 @@ To run the tests, check your test coverage, and generate an HTML coverage report ### Live reloading and Sass CSS compilation -Moved to [Live reloading and SASS compilation](https://cookiecutter-django.readthedocs.io/en/latest/developing-locally.html#sass-compilation-live-reloading). +Moved to [Live reloading and SASS compilation](https://cookiecutter-django.readthedocs.io/en/latest/2-local-development/developing-locally.html#using-webpack-or-gulp). {%- if cookiecutter.use_celery == "y" %} @@ -89,7 +89,7 @@ celery -A config.celery_app worker -B -l info In development, it is often nice to be able to see emails that are being sent from your application. For that reason local SMTP server [Mailpit](https://github.com/axllent/mailpit) with a web interface is available as docker container. Container mailpit will start automatically when you will run all docker containers. -Please check [cookiecutter-django Docker documentation](http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html) for more details how to start all containers. +Please check [cookiecutter-django Docker documentation](https://cookiecutter-django.readthedocs.io/en/latest/2-local-development/developing-locally-docker.html) for more details how to start all containers. With Mailpit running, to view messages that are sent by your application, open your browser and go to `http://127.0.0.1:8025` {%- else %} @@ -132,14 +132,14 @@ The following details how to deploy this application. ### Heroku -See detailed [cookiecutter-django Heroku documentation](http://cookiecutter-django.readthedocs.io/en/latest/deployment-on-heroku.html). +See detailed [cookiecutter-django Heroku documentation](https://cookiecutter-django.readthedocs.io/en/latest/3-deployment/deployment-on-heroku.html). {%- endif %} {%- if cookiecutter.use_docker.lower() == "y" %} ### Docker -See detailed [cookiecutter-django Docker documentation](http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html). +See detailed [cookiecutter-django Docker documentation](https://cookiecutter-django.readthedocs.io/en/latest/3-deployment/deployment-with-docker.html). {%- endif %} {%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %} diff --git a/{{cookiecutter.project_slug}}/README_FORK.md b/{{cookiecutter.project_slug}}/README_FORK.md index aac410f93e..9262849004 100644 --- a/{{cookiecutter.project_slug}}/README_FORK.md +++ b/{{cookiecutter.project_slug}}/README_FORK.md @@ -35,29 +35,14 @@ $ open open_coverage Out of the box, we have all this running already. **Now get to the actual work!** -### Browser sync - -- `http://localhost:3000/` - app running under BrowserSync -- `http://localhost:3001` - BrowserSync GUI - -### Runserver - -- `http://localhost:8000/` - Django -- `http://localhost:8000/admin/` - Django Admin - -### Docs - -- `http://localhost:8989/` - MkDocs for the project (recommend you move this to a dedicated repo) -- `http://localhost:8000/api/v1/swagger/` - Swagger docs -- `http://localhost:8000/api/v1/redoc/` - Redoc docs - -### Email - -- `http://localhost:8025/` - Mailpit (receives all emails sent from Django, no matter the to address) - -### Celery / Flower - -- `http://localhost:5555/` - Flower GUI +Simply visit the following: + +- [Public Website](http://localhost:8000/) +- [Django Admin](http://localhost:8000/) +- [MailPit](http://localhost:8025/) +- [Flower](http://localhost:5555/) +- [BrowserSync](http://localhost:3000/) +- [BrowserSync UI](http://localhost:3001/) ## Production Deploy This is a simple deployment process to a **single** server. diff --git a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile index ddec2d4e70..d892c9dca1 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile @@ -1,5 +1,5 @@ # define an alias for the specific python version used in this file. -FROM docker.io/python:3.12.5-slim-bookworm AS python +FROM docker.io/python:3.12.7-slim-bookworm AS python # Python build stage FROM python AS python-build-stage @@ -48,7 +48,8 @@ RUN groupadd --gid 1000 dev-user \ # Install required system dependencies RUN apt-get update && apt-get install --no-install-recommends -y \ # psycopg dependencies - libpq-dev \ + libpq-dev \ + wait-for-it \ # Translations dependencies gettext \ # cleaning up unused files diff --git a/{{cookiecutter.project_slug}}/compose/local/django/celery/flower/start b/{{cookiecutter.project_slug}}/compose/local/django/celery/flower/start index cebb62203a..f8377adaef 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/celery/flower/start +++ b/{{cookiecutter.project_slug}}/compose/local/django/celery/flower/start @@ -13,4 +13,4 @@ echo 'Starting flower' exec watchfiles --filter python celery.__main__.main \ --args \ - "-A config.celery_app -b \"${CELERY_BROKER_URL}\" flower --basic_auth=\"${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}\"" + "-A config.celery_app -b \"${REDIS_URL}\" flower --basic_auth=\"${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}\"" diff --git a/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile index 49e25cba99..40caf85117 100644 --- a/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile @@ -1,5 +1,5 @@ # define an alias for the specific python version used in this file. -FROM docker.io/python:3.12.5-slim-bookworm AS python +FROM docker.io/python:3.12.7-slim-bookworm AS python # Python build stage diff --git a/{{cookiecutter.project_slug}}/compose/production/aws/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/aws/Dockerfile index 80c9a5cc98..5a6be94dab 100644 --- a/{{cookiecutter.project_slug}}/compose/production/aws/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/aws/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/amazon/aws-cli:2.17.0 +FROM docker.io/amazon/aws-cli:2.19.0 # Clear entrypoint from the base image, otherwise it's always calling the aws CLI ENTRYPOINT [] diff --git a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile index 0545a53b22..d8eee7a849 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile @@ -25,7 +25,7 @@ RUN npm run build {%- endif %} # define an alias for the specific python version used in this file. -FROM docker.io/python:3.12.5-slim-bookworm AS python +FROM docker.io/python:3.12.7-slim-bookworm AS python # Python build stage FROM python AS python-build-stage @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ # psycopg dependencies libpq-dev + # Requirements are installed here to ensure they will be cached. COPY ./requirements . @@ -69,6 +70,8 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ libpq-dev \ # Translations dependencies gettext \ + # entrypoint + wait-for-it \ # cleaning up unused files && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ && rm -rf /var/lib/apt/lists/* @@ -126,9 +129,6 @@ RUN chown -R django:django ${APP_HOME} USER django RUN DATABASE_URL="" \ - {%- if cookiecutter.use_celery == "y" %} - CELERY_BROKER_URL="" \ - {%- endif %} DJANGO_SETTINGS_MODULE="config.settings.test" \ python manage.py compilemessages diff --git a/{{cookiecutter.project_slug}}/compose/production/django/celery/flower/start b/{{cookiecutter.project_slug}}/compose/production/django/celery/flower/start index f903a05a26..c0b1cbe5d4 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/celery/flower/start +++ b/{{cookiecutter.project_slug}}/compose/production/django/celery/flower/start @@ -14,6 +14,6 @@ echo 'Starting flower' exec celery \ -A config.celery_app \ - -b "${CELERY_BROKER_URL}" \ + -b "${REDIS_URL}" \ flower \ --basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}" diff --git a/{{cookiecutter.project_slug}}/compose/production/django/entrypoint b/{{cookiecutter.project_slug}}/compose/production/django/entrypoint index dd07f2d2a6..fe9a0133f8 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/entrypoint +++ b/{{cookiecutter.project_slug}}/compose/production/django/entrypoint @@ -4,45 +4,13 @@ set -o errexit set -o pipefail set -o nounset - -{% if cookiecutter.use_celery == 'y' %} -# N.B. If only .env files supported variable expansion... -export CELERY_BROKER_URL="${REDIS_URL}" -{% endif %} - if [ -z "${POSTGRES_USER}" ]; then base_postgres_image_default_user='postgres' export POSTGRES_USER="${base_postgres_image_default_user}" fi export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" -python << END -import sys -import time - -import psycopg - -suggest_unrecoverable_after = 30 -start = time.time() - -while True: - try: - psycopg.connect( - dbname="${POSTGRES_DB}", - user="${POSTGRES_USER}", - password="${POSTGRES_PASSWORD}", - host="${POSTGRES_HOST}", - port="${POSTGRES_PORT}", - ) - break - except psycopg.OperationalError as error: - sys.stderr.write("Waiting for PostgreSQL to become available...\n") - - if time.time() - start > suggest_unrecoverable_after: - sys.stderr.write(" This is taking longer than expected. The following exception may be indicative of an unrecoverable error: '{}'\n".format(error)) - - time.sleep(1) -END +wait-for-it "${POSTGRES_HOST}:${POSTGRES_PORT}" -t 30 >&2 echo 'PostgreSQL is available' diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile index bdc915db13..e5bc937a7a 100644 --- a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/traefik:3.1.2 +FROM docker.io/traefik:3.2.0 RUN mkdir -p /etc/traefik/acme \ && touch /etc/traefik/acme/acme.json \ && chmod 600 /etc/traefik/acme/acme.json diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py index fdf636eff8..e7cc4c8db5 100644 --- a/{{cookiecutter.project_slug}}/config/settings/base.py +++ b/{{cookiecutter.project_slug}}/config/settings/base.py @@ -346,6 +346,8 @@ # }, } +REDIS_URL = env("REDIS_URL", default="redis://{% if cookiecutter.use_docker == 'y' %}redis{%else%}localhost{% endif %}:6379/0") + {% if cookiecutter.use_celery == 'y' -%} # Celery # ------------------------------------------------------------------------------ @@ -353,9 +355,9 @@ # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-timezone CELERY_TIMEZONE = TIME_ZONE # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-broker_url -CELERY_BROKER_URL = env("CELERY_BROKER_URL") +CELERY_BROKER_URL = REDIS_URL # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-result_backend -CELERY_RESULT_BACKEND = CELERY_BROKER_URL +CELERY_RESULT_BACKEND = REDIS_URL # https://docs.celeryq.dev/en/stable/userguide/configuration.html#result-extended CELERY_RESULT_EXTENDED = True # https://docs.celeryq.dev/en/stable/userguide/configuration.html#result-backend-always-retry diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index 6bf23db801..5d176278c9 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -16,6 +16,7 @@ from .base import * # noqa: F403 from .base import DATABASES from .base import INSTALLED_APPS +from .base import REDIS_URL {%- if cookiecutter.use_drf == "y" %} from .base import SPECTACULAR_SETTINGS {%- endif %} @@ -37,10 +38,10 @@ CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": env("REDIS_URL"), + "LOCATION": REDIS_URL, "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", - # Mimicing memcache behavior. + # Mimicking memcache behavior. # https://github.com/jazzband/django-redis#memcached-exceptions-behavior "IGNORE_EXCEPTIONS": True, }, @@ -78,12 +79,6 @@ default=True, ) -{% if cookiecutter.cloud_provider != 'None' -%} -# STORAGES -# ------------------------------------------------------------------------------ -# https://django-storages.readthedocs.io/en/latest/#installation -INSTALLED_APPS += ["storages"] -{%- endif -%} {% if cookiecutter.cloud_provider == 'AWS' %} # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings AWS_ACCESS_KEY_ID = env("DJANGO_AWS_ACCESS_KEY_ID") @@ -176,7 +171,7 @@ "BACKEND": "storages.backends.azure_storage.AzureStorage", "OPTIONS": { "location": "media", - "file_overwrite": False, + "overwrite_files": False, }, }, {%- if cookiecutter.use_whitenoise == 'y' %} @@ -239,6 +234,7 @@ "DJANGO_EMAIL_SUBJECT_PREFIX", default="[{{cookiecutter.project_name}}] ", ) +ACCOUNT_EMAIL_SUBJECT_PREFIX = EMAIL_SUBJECT_PREFIX # ADMIN # ------------------------------------------------------------------------------ @@ -339,7 +335,7 @@ "js": ["compressor.filters.jsmin.JSMinFilter"], } {% endif %} -{%- if cookiecutter.use_whitenoise == 'n' -%} +{%- if cookiecutter.use_whitenoise == 'n' and cookiecutter.cloud_provider in ('AWS', 'GCP') -%} # Collectfasta # ------------------------------------------------------------------------------ # https://github.com/jasongi/collectfasta#installation diff --git a/{{cookiecutter.project_slug}}/custom_commands/README.md b/{{cookiecutter.project_slug}}/custom_commands/README.md new file mode 100644 index 0000000000..8e05d27901 --- /dev/null +++ b/{{cookiecutter.project_slug}}/custom_commands/README.md @@ -0,0 +1,14 @@ +A collection of 3rd party package commands. You may or may not have these packages installed. + +## Commands (one time) + +- `create_periodic_tasks` - Creates Periodic Tasks that run at 4am daily. +- `create_robots` - Creates a typical robots.txt + +## Commands (meant to be run daily) + +- Delete Expired Sessions - Runs `clearsessions` for `sessions` app. +- Delete Expired Tokens - Runs `cleartokens` for `oauth2_provider` app. +- Delete Old Audit Logs - Runs `clean_apilogs` for `drf_api_logger` app. +- Delete Old API Logs - Runs `clean_auditlogs` for `auditlog` app. +- Delete Old Task Results - Runs `clean_taskresults` for `django_celery` app. diff --git a/{{cookiecutter.project_slug}}/custom_commands/__init__.py b/{{cookiecutter.project_slug}}/custom_commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/{{cookiecutter.project_slug}}/custom_commands/apps.py b/{{cookiecutter.project_slug}}/custom_commands/apps.py new file mode 100644 index 0000000000..551f281e12 --- /dev/null +++ b/{{cookiecutter.project_slug}}/custom_commands/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CustomCommandsConfig(AppConfig): + name = "custom_commands" + verbose_name = "Custom Commands" diff --git a/{{cookiecutter.project_slug}}/custom_commands/management/__init__.py b/{{cookiecutter.project_slug}}/custom_commands/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/{{cookiecutter.project_slug}}/custom_commands/management/commands/__init__.py b/{{cookiecutter.project_slug}}/custom_commands/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_apilogs.py b/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_apilogs.py new file mode 100644 index 0000000000..66a36e3537 --- /dev/null +++ b/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_apilogs.py @@ -0,0 +1,43 @@ +import json +from datetime import timedelta + +from django.core.management.base import BaseCommand +from django.utils import timezone +from drf_api_logger.models import APILogsModel + + +class Command(BaseCommand): + help = "Deletes APILog instances older than a specified number of days" + + def add_arguments(self, parser): + parser.add_argument( + "--days", + type=int, + default=90, + required=False, + help="Number of days to use as the cutoff for deleting log entries", + ) + + def handle(self, *args, **kwargs): + days = kwargs["days"] + + # Calculate the cutoff date + cutoff_date = timezone.now() - timedelta(days=days) + + # Find and delete LogEntry instances older than the specified number of days + old_entries = APILogsModel.objects.filter(added_on__date__lt=cutoff_date) + count = old_entries.count() + old_entries.delete() + + # Output the result + self.stdout.write( + self.style.SUCCESS( + f"Successfully deleted {count} API log entries older than {days} days", + ), + ) + return json.dumps( + { + "message": f"Successfully deleted {count} API log entries older than {days} days", # noqa: E501 + "total_deleted": count, + }, + ) diff --git a/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_auditlogs.py b/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_auditlogs.py new file mode 100644 index 0000000000..492490be61 --- /dev/null +++ b/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_auditlogs.py @@ -0,0 +1,43 @@ +import json +from datetime import timedelta + +from auditlog.models import LogEntry +from django.core.management.base import BaseCommand +from django.utils import timezone + + +class Command(BaseCommand): + help = "Deletes LogEntry instances older than a specified number of days" + + def add_arguments(self, parser): + parser.add_argument( + "--days", + type=int, + default=90, + required=False, + help="Number of days to use as the cutoff for deleting log entries", + ) + + def handle(self, *args, **kwargs): + days = kwargs["days"] + + # Calculate the cutoff date + cutoff_date = timezone.now() - timedelta(days=days) + + # Find and delete LogEntry instances older than the specified number of days + old_entries = LogEntry.objects.filter(timestamp__date__lt=cutoff_date) + count = old_entries.count() + old_entries.delete() + + # Output the result + self.stdout.write( + self.style.SUCCESS( + f"Successfully deleted {count} log entries older than {days} days" + ) + ) + return json.dumps( + { + "message": f"Successfully deleted {count} log entries older than {days} days", # noqa: E501 + "total_deleted": count, + } + ) diff --git a/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_taskresults.py b/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_taskresults.py new file mode 100644 index 0000000000..87bd590ba8 --- /dev/null +++ b/{{cookiecutter.project_slug}}/custom_commands/management/commands/clean_taskresults.py @@ -0,0 +1,43 @@ +import json +from datetime import timedelta + +from django.core.management.base import BaseCommand +from django.utils import timezone +from django_celery.results.models import TaskResult + + +class Command(BaseCommand): + help = "Deletes TaskResult instances older than a specified number of days" + + def add_arguments(self, parser): + parser.add_argument( + "--days", + type=int, + default=90, + required=False, + help="Number of days to use as the cutoff for deleting task result entries", + ) + + def handle(self, *args, **kwargs): + days = kwargs["days"] + + # Calculate the cutoff date + cutoff_date = timezone.now() - timedelta(days=days) + + # Find and delete TaskResult instances older than the specified number of days + old_entries = TaskResult.objects.filter(date_created__date__lt=cutoff_date) + count = old_entries.count() + old_entries.delete() + + # Output the result + self.stdout.write( + self.style.SUCCESS( + f"Successfully deleted {count} task results older than {days} days" + ) + ) + return json.dumps( + { + "message": f"Successfully deleted {count} task results older than {days} days", # noqa: E501 + "total_deleted": count, + } + ) diff --git a/{{cookiecutter.project_slug}}/custom_commands/management/commands/create_periodic_tasks.py b/{{cookiecutter.project_slug}}/custom_commands/management/commands/create_periodic_tasks.py new file mode 100644 index 0000000000..eb901e4430 --- /dev/null +++ b/{{cookiecutter.project_slug}}/custom_commands/management/commands/create_periodic_tasks.py @@ -0,0 +1,62 @@ +from django.conf import settings +from django.core.management.base import BaseCommand +from django_celery_beat.models import CrontabSchedule +from django_celery_beat.models import PeriodicTask + + +def create_crontab(): + """Create a crontab schedule for 4am if it does not already exist.""" + return CrontabSchedule.objects.get_or_create( + minute="0", + hour="4", + day_of_week="*", + day_of_month="*", + month_of_year="*", + timezone=settings.TIME_ZONE, + ) + + +def create_periodic_task(crontab_schedule, name, task): + """Create a periodic task if it does not already exist.""" + return PeriodicTask.objects.get_or_create( + crontab=crontab_schedule, + name=name, + task=task, + ) + + +TASKS = [ + ("Delete Expired Sessions", "custom_commands.tasks.delete_expired_sessions"), + ("Delete Expired Tokens", "custom_commands.tasks.delete_expired_tokens"), + ("Delete Old Audit Logs", "custom_commands.tasks.delete_old_audit_logs"), + ("Delete Old API Logs", "custom_commands.tasks.delete_old_api_logs"), + ("Delete Old Task Results", "custom_commands.tasks.delete_old_api_logs"), +] + + +class Command(BaseCommand): + help = "Create periodic tasks for typical things we do." + + def handle(self, *args, **kwargs): + # Create a cron schedule + crontab_schedule, created = create_crontab() + if created: + self.stdout.write( + self.style.SUCCESS("Created crontab schedule for 4am daily."), + ) + else: + self.stdout.write( + self.style.WARNING("Crontab schedule for 4am daily exists."), + ) + + # Create tasks + for name, task in TASKS: + _, created = create_periodic_task(crontab_schedule, name, task) + if created: + self.stdout.write( + self.style.SUCCESS(f'Created periodic task "{name}".') + ) + else: + self.stdout.write(self.style.WARNING(f'Periodic task "{name}" exists.')) + + self.stdout.write(self.style.SUCCESS("Successfully created periodic tasks.")) diff --git a/{{cookiecutter.project_slug}}/custom_commands/management/commands/create_robots.py b/{{cookiecutter.project_slug}}/custom_commands/management/commands/create_robots.py new file mode 100644 index 0000000000..8d4cc18dae --- /dev/null +++ b/{{cookiecutter.project_slug}}/custom_commands/management/commands/create_robots.py @@ -0,0 +1,46 @@ +from django.contrib.sites.models import Site +from django.core.management.base import BaseCommand +from robots.models import Rule +from robots.models import Url + +# Create URL patterns (pattern, allowed) +URLS = [ + ("/admin/", False), + ("/media/", False), + ("/static/", False), + ("/accounts/", False), + ("/", True), +] + + +class Command(BaseCommand): + help = "Generate default robots.txt rules" + + def handle(self, *args, **options): + # Delete existing rules + Rule.objects.all().delete() + + # Retrieve the default site + default_site = Site.objects.get_current() + + # Create or get the default rule + default_rule, created = Rule.objects.get_or_create(robot="*") + if created: + default_rule.sites.add(default_site) + + # Create URL patterns and associate them with the rule + allowed_urls = [] + disallowed_urls = [] + + for pattern, allowed in URLS: + url, _ = Url.objects.get_or_create(pattern=pattern) + if allowed: + allowed_urls.append(url) + else: + disallowed_urls.append(url) + + # Use bulk operations to add URLs to the rule + default_rule.allowed.add(*allowed_urls) + default_rule.disallowed.add(*disallowed_urls) + + self.stdout.write(self.style.SUCCESS("Default robots.txt rules generated")) diff --git a/{{cookiecutter.project_slug}}/custom_commands/tasks.py b/{{cookiecutter.project_slug}}/custom_commands/tasks.py new file mode 100644 index 0000000000..2cecf36fba --- /dev/null +++ b/{{cookiecutter.project_slug}}/custom_commands/tasks.py @@ -0,0 +1,38 @@ +import json + +from celery import shared_task +from django.core import management + + +@shared_task +def delete_expired_tokens(): + """Delete expired oAuth tokens for`oauth2_provider` app.""" + management.call_command("cleartokens", verbosity=0) + + +@shared_task +def delete_expired_sessions(): + """Delete expired sessions for `sessions` app.""" + management.call_command("clearsessions", verbosity=0) + + +@shared_task +def delete_old_audit_logs(days: int = 90): + """Delete audit logs older than X days for `auditlog` app.""" + return json.loads( + management.call_command("clean_auditlogs", verbosity=0, days=days), + ) + + +@shared_task +def delete_old_api_logs(days: int = 90): + """Delete API logs older than X days for `drf_api_logger` app.""" + return json.loads(management.call_command("clean_apilogs", verbosity=0, days=days)) + + +@shared_task +def delete_old_api_task_results(days: int = 90): + """Delete Task Results older than X days for `drf_api_logger` app.""" + return json.loads( + management.call_command("clean_taskresults", verbosity=0, days=days) + ) diff --git a/{{cookiecutter.project_slug}}/docs/Makefile b/{{cookiecutter.project_slug}}/docs/Makefile index cf080e4767..9e0e4d6c06 100644 --- a/{{cookiecutter.project_slug}}/docs/Makefile +++ b/{{cookiecutter.project_slug}}/docs/Makefile @@ -13,11 +13,11 @@ APP = /app APP = ../{{cookiecutter.project_slug}} {% endif %} -.PHONY: help livehtml apidocs Makefile +.PHONY: html livehtml apidocs Makefile -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -c . +# Put it first so that "make" without argument is like "make html". +html: + @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -c . # Build, watch and serve docs with live reload livehtml: diff --git a/{{cookiecutter.project_slug}}/docs/conf.py b/{{cookiecutter.project_slug}}/docs/conf.py index 40d59dbbb8..31a828fe5b 100644 --- a/{{cookiecutter.project_slug}}/docs/conf.py +++ b/{{cookiecutter.project_slug}}/docs/conf.py @@ -26,9 +26,6 @@ sys.path.insert(0, os.path.abspath("..")) {%- endif %} os.environ["DATABASE_URL"] = "sqlite:///readthedocs.db" -{%- if cookiecutter.use_celery == 'y' %} -os.environ["CELERY_BROKER_URL"] = os.getenv("REDIS_URL", "redis://redis:6379") -{%- endif %} os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local") django.setup() diff --git a/{{cookiecutter.project_slug}}/docs/make.bat b/{{cookiecutter.project_slug}}/docs/make.bat index 6cd1129f03..fbf6eb45f2 100644 --- a/{{cookiecutter.project_slug}}/docs/make.bat +++ b/{{cookiecutter.project_slug}}/docs/make.bat @@ -12,7 +12,7 @@ set SOURCEDIR=_source set BUILDDIR=_build set APP=..\{{cookiecutter.project_slug}} -if "%1" == "" goto help +if "%1" == "" goto html %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( @@ -39,8 +39,8 @@ GOTO :EOF sphinx-apidoc -o %SOURCEDIR%/api %APP% GOTO :EOF -:help -%SPHINXBUILD% -b help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +:html +%SPHINXBUILD% -b html %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd diff --git a/{{cookiecutter.project_slug}}/package.json b/{{cookiecutter.project_slug}}/package.json index a93b3fa5f8..94861c4eb3 100644 --- a/{{cookiecutter.project_slug}}/package.json +++ b/{{cookiecutter.project_slug}}/package.json @@ -11,7 +11,7 @@ "browser-sync": "^3.0.2", "css-loader": "^7.1.2", "gulp-concat": "^2.6.1", - "concurrently": "^8.0.1", + "concurrently": "^9.0.0", "cssnano": "^7.0.0", "gulp": "^4.0.2", "gulp-imagemin": "^7.1.0", @@ -25,7 +25,7 @@ "pixrem": "^5.0.0", "postcss": "^8.3.11", "postcss-loader": "^8.0.0", - "postcss-preset-env": "^9.0.0", + "postcss-preset-env": "^10.0.3", "sass": "^1.43.4", "sass-loader": "^16.0.1", "webpack": "^5.65.0", diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index b775f30432..c0d150b52f 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -1,5 +1,5 @@ python-slugify==8.0.4 # https://github.com/un33k/python-slugify -Pillow==10.4.0 # https://github.com/python-pillow/Pillow +Pillow==11.0.0 # https://github.com/python-pillow/Pillow {%- if cookiecutter.frontend_pipeline == 'Django Compressor' %} {%- if cookiecutter.windows == 'y' and cookiecutter.use_docker == 'n' %} rcssmin==1.1.2 --install-option="--without-c-extensions" # https://github.com/ndparker/rcssmin @@ -9,9 +9,9 @@ rcssmin==1.1.2 # https://github.com/ndparker/rcssmin {%- endif %} argon2-cffi==23.1.0 # https://github.com/hynek/argon2_cffi {%- if cookiecutter.use_whitenoise == 'y' %} -whitenoise==6.7.0 # https://github.com/evansd/whitenoise +whitenoise==6.8.2 # https://github.com/evansd/whitenoise {%- endif %} -redis==5.0.8 # https://github.com/redis/redis-py +redis==5.2.0 # https://github.com/redis/redis-py {%- if cookiecutter.use_docker == "y" or cookiecutter.windows == "n" %} hiredis==3.0.0 # https://github.com/redis/hiredis-py {%- endif %} @@ -23,7 +23,7 @@ flower==2.0.1 # https://github.com/mher/flower {%- endif %} {%- endif %} {%- if cookiecutter.use_async == 'y' %} -uvicorn[standard]==0.30.6 # https://github.com/encode/uvicorn +uvicorn[standard]==0.32.0 # https://github.com/encode/uvicorn uvicorn-worker==0.2.0 # https://github.com/Kludex/uvicorn-worker {%- endif %} @@ -32,9 +32,9 @@ uvicorn-worker==0.2.0 # https://github.com/Kludex/uvicorn-worker django==5.0.9 # pyup: < 5.1 # https://www.djangoproject.com/ django-environ==0.11.2 # https://github.com/joke2k/django-environ django-model-utils==5.0.0 # https://github.com/jazzband/django-model-utils -django-allauth[mfa]==64.2.0 # https://github.com/pennersr/django-allauth +django-allauth[mfa]==65.1.0 # https://github.com/pennersr/django-allauth django-crispy-forms==2.3 # https://github.com/django-crispy-forms/django-crispy-forms -crispy-bootstrap5==2024.2 # https://github.com/django-crispy-forms/crispy-bootstrap5 +crispy-bootstrap5==2024.10 # https://github.com/django-crispy-forms/crispy-bootstrap5 {%- if cookiecutter.frontend_pipeline == 'Django Compressor' %} django-compressor==4.5.1 # https://github.com/django-compressor/django-compressor {%- endif %} @@ -42,7 +42,7 @@ django-redis==5.4.0 # https://github.com/jazzband/django-redis {%- if cookiecutter.use_drf == 'y' %} # Django REST Framework djangorestframework==3.15.2 # https://github.com/encode/django-rest-framework -django-cors-headers==4.4.0 # https://github.com/adamchainz/django-cors-headers +django-cors-headers==4.6.0 # https://github.com/adamchainz/django-cors-headers # DRF-spectacular for api documentation drf-spectacular==0.27.2 # https://github.com/tfranzel/drf-spectacular {%- endif %} diff --git a/{{cookiecutter.project_slug}}/requirements/local.txt b/{{cookiecutter.project_slug}}/requirements/local.txt index 4f3795a62a..03e6c3e079 100644 --- a/{{cookiecutter.project_slug}}/requirements/local.txt +++ b/{{cookiecutter.project_slug}}/requirements/local.txt @@ -1,12 +1,11 @@ -r production.txt -watchdog==4.0.2 # https://github.com/gorakhargosh/watchdog -Werkzeug[watchdog]==3.0.4 # https://github.com/pallets/werkzeug +Werkzeug[watchdog]==3.0.6 # https://github.com/pallets/werkzeug ipdb==0.13.13 # https://github.com/gotcha/ipdb {%- if cookiecutter.use_docker == 'y' %} -psycopg[c]==3.2.1 # https://github.com/psycopg/psycopg +psycopg[c]==3.2.3 # https://github.com/psycopg/psycopg {%- else %} -psycopg[binary]==3.2.1 # https://github.com/psycopg/psycopg +psycopg[binary]==3.2.3 # https://github.com/psycopg/psycopg {%- endif %} {%- if cookiecutter.use_async == 'y' or cookiecutter.use_celery == 'y' %} watchfiles==0.24.0 # https://github.com/samuelcolvin/watchfiles @@ -14,25 +13,25 @@ watchfiles==0.24.0 # https://github.com/samuelcolvin/watchfiles # Testing # ------------------------------------------------------------------------------ -mypy==1.11.2 # https://github.com/python/mypy -django-stubs[compatible-mypy]==5.0.4 # https://github.com/typeddjango/django-stubs -pytest==8.3.2 # https://github.com/pytest-dev/pytest +mypy==1.13.0 # https://github.com/python/mypy +django-stubs[compatible-mypy]==5.1.1 # https://github.com/typeddjango/django-stubs +pytest==8.3.3 # https://github.com/pytest-dev/pytest pytest-sugar==1.0.0 # https://github.com/Frozenball/pytest-sugar {%- if cookiecutter.use_drf == "y" %} -djangorestframework-stubs==3.15.0 # https://github.com/typeddjango/djangorestframework-stubs +djangorestframework-stubs==3.15.1 # https://github.com/typeddjango/djangorestframework-stubs {%- endif %} # Documentation # ------------------------------------------------------------------------------ -sphinx==7.4.7 # https://github.com/sphinx-doc/sphinx -sphinx-autobuild==2024.9.3 # https://github.com/GaretJax/sphinx-autobuild +sphinx==8.1.3 # https://github.com/sphinx-doc/sphinx +sphinx-autobuild==2024.10.3 # https://github.com/GaretJax/sphinx-autobuild # Code quality # ------------------------------------------------------------------------------ -ruff==0.6.3 # https://github.com/astral-sh/ruff -coverage==7.6.1 # https://github.com/nedbat/coveragepy -djlint==1.35.2 # https://github.com/Riverside-Healthcare/djLint -pre-commit==3.8.0 # https://github.com/pre-commit/pre-commit +ruff==0.7.1 # https://github.com/astral-sh/ruff +coverage==7.6.4 # https://github.com/nedbat/coveragepy +djlint==1.35.4 # https://github.com/Riverside-Healthcare/djLint +pre-commit==4.0.1 # https://github.com/pre-commit/pre-commit # Django # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/requirements/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt index 824927ca64..b6f22db451 100644 --- a/{{cookiecutter.project_slug}}/requirements/production.txt +++ b/{{cookiecutter.project_slug}}/requirements/production.txt @@ -3,12 +3,12 @@ -r base.txt gunicorn==23.0.0 # https://github.com/benoitc/gunicorn -psycopg[c]==3.2.1 # https://github.com/psycopg/psycopg -{%- if cookiecutter.use_whitenoise == 'n' %} +psycopg[c]==3.2.3 # https://github.com/psycopg/psycopg +{%- if cookiecutter.use_whitenoise == 'n'and cookiecutter.cloud_provider in ('AWS', 'GCP') %} Collectfasta==3.2.0 # https://github.com/jasongi/collectfasta {%- endif %} {%- if cookiecutter.use_sentry == "y" %} -sentry-sdk==2.13.0 # https://github.com/getsentry/sentry-python +sentry-sdk==2.17.0 # https://github.com/getsentry/sentry-python {%- endif %} {%- if cookiecutter.use_docker == "n" and cookiecutter.windows == "y" %} hiredis==3.0.0 # https://github.com/redis/hiredis-py @@ -24,21 +24,21 @@ django-storages[google]==1.14.4 # https://github.com/jschneier/django-storages django-storages[azure]==1.14.4 # https://github.com/jschneier/django-storages {%- endif %} {%- if cookiecutter.mail_service == 'Mailgun' %} -django-anymail[mailgun]==11.1 # https://github.com/anymail/django-anymail +django-anymail[mailgun]==12.0 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Amazon SES' %} -django-anymail[amazon-ses]==11.1 # https://github.com/anymail/django-anymail +django-anymail[amazon-ses]==12.0 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Mailjet' %} -django-anymail[mailjet]==11.1 # https://github.com/anymail/django-anymail +django-anymail[mailjet]==12.0 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Mandrill' %} -django-anymail[mandrill]==11.1 # https://github.com/anymail/django-anymail +django-anymail[mandrill]==12.0 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Postmark' %} -django-anymail[postmark]==11.1 # https://github.com/anymail/django-anymail +django-anymail[postmark]==12.0 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Sendgrid' %} -django-anymail[sendgrid]==11.1 # https://github.com/anymail/django-anymail +django-anymail[sendgrid]==12.0 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Brevo' %} -django-anymail[brevo]==11.1 # https://github.com/anymail/django-anymail +django-anymail[brevo]==12.0 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'SparkPost' %} -django-anymail[sparkpost]==11.1 # https://github.com/anymail/django-anymail +django-anymail[sparkpost]==12.0 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Other SMTP' %} -django-anymail==11.1 # https://github.com/anymail/django-anymail +django-anymail==12.0 # https://github.com/anymail/django-anymail {%- endif %} diff --git a/{{cookiecutter.project_slug}}/runtime.txt b/{{cookiecutter.project_slug}}/runtime.txt index 8a7cba521c..32905d6e0f 100644 --- a/{{cookiecutter.project_slug}}/runtime.txt +++ b/{{cookiecutter.project_slug}}/runtime.txt @@ -1 +1 @@ -python-3.12.5 +python-3.12.7 diff --git a/{{cookiecutter.project_slug}}/utility/requirements-noble.apt b/{{cookiecutter.project_slug}}/utility/requirements-noble.apt new file mode 100644 index 0000000000..f24f6f3dac --- /dev/null +++ b/{{cookiecutter.project_slug}}/utility/requirements-noble.apt @@ -0,0 +1,23 @@ +##basic build dependencies of various Django apps for Debian Bookworm 12.x +#build-essential metapackage install: make, gcc, g++, +build-essential +#required to translate +gettext +python3-dev + +##shared dependencies of: +##Pillow, pylibmc +zlib1g-dev + +##Postgresql and psycopg dependencies +libpq-dev + +##Pillow dependencies +libtiff5-dev +libjpeg62-turbo-dev +libfreetype6-dev +liblcms2-dev +libwebp-dev + +##django-extensions +libgraphviz-dev