├── .github
├── CODE_OF_CONDUCT.md
├── FUNDING.yml
├── PULL_REQUEST_TEMPLATE.md
├── labels.toml
└── workflows
│ ├── ci.yml
│ ├── codeql.yml
│ ├── hacktoberfest.yml
│ ├── issue-manager.yml
│ ├── labels.yml
│ └── upgrader.yml
├── .gitignore
├── .gitpod.yml
├── .pre-commit-config.yaml
├── CHANGELOG.md
├── README.md
├── commitlint.config.mjs
├── pyproject.toml
├── renovate.json
├── src
└── flake8_django_migrations
│ ├── __init__.py
│ ├── checkers
│ ├── __init__.py
│ ├── base.py
│ ├── issue.py
│ └── remove_field.py
│ └── plugin.py
├── templates
├── .changelog-old.md
└── CHANGELOG.md.j2
├── tests
└── test_flake8_django_migrations.py
└── uv.lock
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socioeconomic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | - Demonstrating empathy and kindness toward other people
21 | - Being respectful of differing opinions, viewpoints, and experiences
22 | - Giving and gracefully accepting constructive feedback
23 | - Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | - Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | - The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | - Trolling, insulting or derogatory comments, and personal or political attacks
33 | - Public or private harassment
34 | - Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | - Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported by contacting @browniebroke. All complaints will be reviewed and
63 | investigated promptly and fairly.
64 |
65 | All community leaders are obligated to respect the privacy and security of the
66 | reporter of any incident.
67 |
68 | ## Enforcement Guidelines
69 |
70 | Community leaders will follow these Community Impact Guidelines in determining
71 | the consequences for any action they deem in violation of this Code of Conduct:
72 |
73 | ### 1. Correction
74 |
75 | **Community Impact**: Use of inappropriate language or other behavior deemed
76 | unprofessional or unwelcome in the community.
77 |
78 | **Consequence**: A private, written warning from community leaders, providing
79 | clarity around the nature of the violation and an explanation of why the
80 | behavior was inappropriate. A public apology may be requested.
81 |
82 | ### 2. Warning
83 |
84 | **Community Impact**: A violation through a single incident or series
85 | of actions.
86 |
87 | **Consequence**: A warning with consequences for continued behavior. No
88 | interaction with the people involved, including unsolicited interaction with
89 | those enforcing the Code of Conduct, for a specified period of time. This
90 | includes avoiding interactions in community spaces as well as external channels
91 | like social media. Violating these terms may lead to a temporary or
92 | permanent ban.
93 |
94 | ### 3. Temporary Ban
95 |
96 | **Community Impact**: A serious violation of community standards, including
97 | sustained inappropriate behavior.
98 |
99 | **Consequence**: A temporary ban from any sort of interaction or public
100 | communication with the community for a specified period of time. No public or
101 | private interaction with the people involved, including unsolicited interaction
102 | with those enforcing the Code of Conduct, is allowed during this period.
103 | Violating these terms may lead to a permanent ban.
104 |
105 | ### 4. Permanent Ban
106 |
107 | **Community Impact**: Demonstrating a pattern of violation of community
108 | standards, including sustained inappropriate behavior, harassment of an
109 | individual, or aggression toward or disparagement of classes of individuals.
110 |
111 | **Consequence**: A permanent ban from any sort of public interaction within
112 | the community.
113 |
114 | ## Attribution
115 |
116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
117 | version 2.0, available at
118 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
119 |
120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
121 | enforcement ladder](https://github.com/mozilla/diversity).
122 |
123 | [homepage]: https://www.contributor-covenant.org
124 |
125 | For answers to common questions about this code of conduct, see the FAQ at
126 | https://www.contributor-covenant.org/faq. Translations are available at
127 | https://www.contributor-covenant.org/translations.
128 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [browniebroke]
2 | patreon: browniebroke
3 | custom: ["https://paypal.me/browneibroke"]
4 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
9 |
10 | ### Description of change
11 |
12 |
25 |
26 | ### Pull-Request Checklist
27 |
28 |
35 |
36 | - [ ] Code is up-to-date with the `main` branch
37 | - [ ] This pull request follows the [contributing guidelines](https://github.com/browniebroke/django-admin-helpers/blob/main/CONTRIBUTING.md).
38 | - [ ] This pull request links relevant issues as `Fixes #0000`
39 | - [ ] There are new or updated unit tests validating the change
40 | - [ ] Documentation has been updated to reflect this change
41 | - [ ] The new commits follow conventions outlined in the [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/), such as "fix(api): prevent racing of requests".
42 |
43 | > - If pre-commit.ci is failing, try `pre-commit run -a` for further information.
44 | > - If CI / test is failing, try `uv run pytest` for further information.
45 |
46 |
49 |
--------------------------------------------------------------------------------
/.github/labels.toml:
--------------------------------------------------------------------------------
1 | [breaking]
2 | color = "ffcc00"
3 | name = "breaking"
4 | description = "Breaking change."
5 |
6 | [bug]
7 | color = "d73a4a"
8 | name = "bug"
9 | description = "Something isn't working"
10 |
11 | [dependencies]
12 | color = "0366d6"
13 | name = "dependencies"
14 | description = "Pull requests that update a dependency file"
15 |
16 | [github_actions]
17 | color = "000000"
18 | name = "github_actions"
19 | description = "Update of github actions"
20 |
21 | [documentation]
22 | color = "1bc4a5"
23 | name = "documentation"
24 | description = "Improvements or additions to documentation"
25 |
26 | [duplicate]
27 | color = "cfd3d7"
28 | name = "duplicate"
29 | description = "This issue or pull request already exists"
30 |
31 | [enhancement]
32 | color = "a2eeef"
33 | name = "enhancement"
34 | description = "New feature or request"
35 |
36 | ["good first issue"]
37 | color = "7057ff"
38 | name = "good first issue"
39 | description = "Good for newcomers"
40 |
41 | ["help wanted"]
42 | color = "008672"
43 | name = "help wanted"
44 | description = "Extra attention is needed"
45 |
46 | [invalid]
47 | color = "e4e669"
48 | name = "invalid"
49 | description = "This doesn't seem right"
50 |
51 | [nochangelog]
52 | color = "555555"
53 | name = "nochangelog"
54 | description = "Exclude pull requests from changelog"
55 |
56 | [question]
57 | color = "d876e3"
58 | name = "question"
59 | description = "Further information is requested"
60 |
61 | [removed]
62 | color = "e99695"
63 | name = "removed"
64 | description = "Removed piece of functionalities."
65 |
66 | [tests]
67 | color = "bfd4f2"
68 | name = "tests"
69 | description = "CI, CD and testing related changes"
70 |
71 | [wontfix]
72 | color = "ffffff"
73 | name = "wontfix"
74 | description = "This will not be worked on"
75 |
76 | [discussion]
77 | color = "c2e0c6"
78 | name = "discussion"
79 | description = "Some discussion around the project"
80 |
81 | [hacktoberfest]
82 | color = "ffa663"
83 | name = "hacktoberfest"
84 | description = "Good issues for Hacktoberfest"
85 |
86 | [answered]
87 | color = "0ee2b6"
88 | name = "answered"
89 | description = "Automatically closes as answered after a delay"
90 |
91 | [waiting]
92 | color = "5f7972"
93 | name = "waiting"
94 | description = "Automatically closes if no answer after a delay"
95 |
96 | [fund]
97 | color = "0E8A16"
98 | name = "fund"
99 | description = "Add a section linking to polar.sh for funding the issue."
100 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | concurrency:
10 | group: ${{ github.head_ref || github.run_id }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | test:
15 | runs-on: ${{ matrix.os }}
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | python-version:
20 | - "3.9"
21 | - "3.10"
22 | - "3.11"
23 | - "3.12"
24 | - "3.13"
25 | os:
26 | - ubuntu-latest
27 | - windows-latest
28 | - macOS-latest
29 |
30 | steps:
31 | - uses: actions/checkout@v4
32 | - uses: actions/setup-python@v5
33 | with:
34 | python-version: ${{ matrix.python-version }}
35 | - uses: astral-sh/setup-uv@v6
36 | - run: uv sync --no-python-downloads
37 | shell: bash
38 | - run: uv run pytest --cov-report=xml
39 | shell: bash
40 | - uses: codecov/codecov-action@v5
41 | with:
42 | token: ${{ secrets.CODECOV_TOKEN }}
43 |
44 | commitlint:
45 | runs-on: ubuntu-latest
46 | steps:
47 | - uses: actions/checkout@v4
48 | with:
49 | fetch-depth: 0
50 | - uses: wagoid/commitlint-github-action@v6.2.1
51 |
52 | release:
53 | needs:
54 | - test
55 | - commitlint
56 |
57 | runs-on: ubuntu-latest
58 | environment: release
59 | concurrency: release
60 | permissions:
61 | id-token: write
62 | attestations: write
63 | contents: write
64 |
65 | steps:
66 | - uses: actions/checkout@v4
67 | with:
68 | fetch-depth: 0
69 | ref: ${{ github.sha }}
70 |
71 | - name: Checkout commit for release
72 | run: |
73 | git checkout -B ${{ github.ref_name }} ${{ github.sha }}
74 |
75 | # Do a dry run of PSR
76 | - name: Test release
77 | uses: python-semantic-release/python-semantic-release@v10
78 | if: github.ref_name != 'main'
79 | with:
80 | no_operation_mode: true
81 |
82 | # On main branch: actual PSR + upload to PyPI & GitHub
83 | - name: Release
84 | uses: python-semantic-release/python-semantic-release@v10
85 | id: release
86 | if: github.ref_name == 'main'
87 | with:
88 | github_token: ${{ secrets.GITHUB_TOKEN }}
89 |
90 | - name: Attest build provenance
91 | uses: actions/attest-build-provenance@v2
92 | if: steps.release.outputs.released == 'true'
93 | with:
94 | subject-path: "dist/*"
95 |
96 | - name: Publish package distributions to PyPI
97 | uses: pypa/gh-action-pypi-publish@release/v1
98 | if: steps.release.outputs.released == 'true'
99 |
100 | - name: Publish package distributions to GitHub Releases
101 | uses: python-semantic-release/publish-action@v10
102 | if: steps.release.outputs.released == 'true'
103 | with:
104 | github_token: ${{ secrets.GITHUB_TOKEN }}
105 | tag: ${{ steps.release.outputs.tag }}
106 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | pull_request:
7 | branches: ["main"]
8 | schedule:
9 | - cron: "34 23 * * 0"
10 |
11 | jobs:
12 | analyze:
13 | name: Analyze
14 | runs-on: ubuntu-latest
15 | permissions:
16 | actions: read
17 | contents: read
18 | security-events: write
19 |
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | language: [javascript, python]
24 |
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v4
28 |
29 | - name: Initialize CodeQL
30 | uses: github/codeql-action/init@v3
31 | with:
32 | languages: ${{ matrix.language }}
33 | queries: +security-and-quality
34 |
35 | - name: Autobuild
36 | uses: github/codeql-action/autobuild@v3
37 | if: ${{ matrix.language == 'javascript' || matrix.language == 'python' }}
38 |
39 | - name: Perform CodeQL Analysis
40 | uses: github/codeql-action/analyze@v3
41 | with:
42 | category: "/language:${{ matrix.language }}"
43 |
--------------------------------------------------------------------------------
/.github/workflows/hacktoberfest.yml:
--------------------------------------------------------------------------------
1 | name: Hacktoberfest
2 |
3 | on:
4 | schedule:
5 | # Run every day in October
6 | - cron: "0 0 * 10 *"
7 | # Run on the 1st of November to revert
8 | - cron: "0 13 1 11 *"
9 |
10 | jobs:
11 | hacktoberfest:
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: browniebroke/hacktoberfest-labeler-action@v2.3.0
16 | with:
17 | github_token: ${{ secrets.CPR_GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.github/workflows/issue-manager.yml:
--------------------------------------------------------------------------------
1 | name: Issue Manager
2 |
3 | on:
4 | schedule:
5 | - cron: "0 0 * * *"
6 | issue_comment:
7 | types:
8 | - created
9 | issues:
10 | types:
11 | - labeled
12 | pull_request_target:
13 | types:
14 | - labeled
15 | workflow_dispatch:
16 |
17 | jobs:
18 | issue-manager:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: tiangolo/issue-manager@0.5.1
22 | with:
23 | token: ${{ secrets.GITHUB_TOKEN }}
24 | config: >
25 | {
26 | "answered": {
27 | "message": "Assuming the original issue was solved, it will be automatically closed now."
28 | },
29 | "waiting": {
30 | "message": "Automatically closing. To re-open, please provide the additional information requested."
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.github/workflows/labels.yml:
--------------------------------------------------------------------------------
1 | name: Sync Github labels
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - ".github/**"
9 |
10 | jobs:
11 | labels:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - name: Set up Python
16 | uses: actions/setup-python@v5
17 | with:
18 | python-version: 3.x
19 | - name: Install labels
20 | run: pip install labels
21 | - name: Sync config with Github
22 | run: labels -u ${{ github.repository_owner }} -t ${{ secrets.GITHUB_TOKEN }} sync -f .github/labels.toml
23 |
--------------------------------------------------------------------------------
/.github/workflows/upgrader.yml:
--------------------------------------------------------------------------------
1 | name: Upgrader
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: "50 8 6 1-9,11-12 *"
7 |
8 | jobs:
9 | upgrade:
10 | uses: browniebroke/github-actions/.github/workflows/uv-upgrade.yml@v1
11 | secrets:
12 | gh_pat: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 |
58 | # Flask stuff:
59 | instance/
60 | .webassets-cache
61 |
62 | # Scrapy stuff:
63 | .scrapy
64 |
65 | # Sphinx documentation
66 | docs/_build/
67 |
68 | # PyBuilder
69 | target/
70 |
71 | # Jupyter Notebook
72 | .ipynb_checkpoints
73 |
74 | # pyenv
75 | .python-version
76 |
77 | # celery beat schedule file
78 | celerybeat-schedule
79 |
80 | # SageMath parsed files
81 | *.sage.py
82 |
83 | # dotenv
84 | .env
85 |
86 | # virtualenv
87 | .venv
88 | venv/
89 | ENV/
90 |
91 | # Spyder project settings
92 | .spyderproject
93 | .spyproject
94 |
95 | # Rope project settings
96 | .ropeproject
97 |
98 | # mkdocs documentation
99 | /site
100 |
101 | # mypy
102 | .mypy_cache/
103 |
104 | # pytest
105 | .pytest_cache/
106 |
107 | mypy-report/
108 | tests/reports/
109 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - command: |
3 | pip install uv
4 | PIP_USER=false uv sync
5 | - command: |
6 | pip install pre-commit
7 | pre-commit install
8 | PIP_USER=false pre-commit install-hooks
9 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # See https://pre-commit.com for more information
2 | # See https://pre-commit.com/hooks.html for more hooks
3 | exclude: "CHANGELOG.md|.all-contributorsrc"
4 | default_stages: [pre-commit]
5 |
6 | ci:
7 | autofix_commit_msg: "chore(pre-commit.ci): auto fixes"
8 | autoupdate_commit_msg: "chore(pre-commit.ci): pre-commit autoupdate"
9 |
10 | repos:
11 | - repo: https://github.com/pre-commit/pre-commit-hooks
12 | rev: v5.0.0
13 | hooks:
14 | - id: debug-statements
15 | - id: check-builtin-literals
16 | - id: check-case-conflict
17 | - id: check-docstring-first
18 | - id: check-json
19 | - id: check-toml
20 | - id: check-xml
21 | - id: check-yaml
22 | - id: detect-private-key
23 | - id: end-of-file-fixer
24 | - id: trailing-whitespace
25 | - id: debug-statements
26 | - repo: https://github.com/tox-dev/pyproject-fmt
27 | rev: "v2.6.0"
28 | hooks:
29 | - id: pyproject-fmt
30 | - repo: https://github.com/astral-sh/uv-pre-commit
31 | rev: 0.7.10
32 | hooks:
33 | - id: uv-lock
34 | - repo: https://github.com/pre-commit/mirrors-prettier
35 | rev: v3.1.0
36 | hooks:
37 | - id: prettier
38 | args: ["--tab-width", "2"]
39 | - repo: https://github.com/astral-sh/ruff-pre-commit
40 | rev: v0.11.12
41 | hooks:
42 | - id: ruff
43 | args: [--fix, --exit-non-zero-on-fix]
44 | - id: ruff-format
45 | - repo: https://github.com/codespell-project/codespell
46 | rev: v2.4.1
47 | hooks:
48 | - id: codespell
49 | - repo: https://github.com/commitizen-tools/commitizen
50 | rev: v4.8.2
51 | hooks:
52 | - id: commitizen
53 | stages: [commit-msg]
54 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## v1.1.0 (2024-10-31)
4 |
5 | ### Features
6 |
7 | - Drop support for python 3.8 (#650) ([`9c6e6d7`](https://github.com/browniebroke/flake8-django-migrations/commit/9c6e6d709593e35a28e1a25e06fa611cb3f8ae9c))
8 |
9 | ## v1.0.0 (2023-06-27)
10 |
11 | ### Feature
12 |
13 | - Drop support for Python 3.7 ([`d41a017`](https://github.com/browniebroke/flake8-django-migrations/commit/d41a017ef1a830c59d9b287694eddc1aad65bd64))
14 |
15 | ### Breaking
16 |
17 | - Drop support for Python 3.7 as it reached EOL on June 27, 2023. More infos: https://devguide.python.org/versions/ ([`d41a017`](https://github.com/browniebroke/flake8-django-migrations/commit/d41a017ef1a830c59d9b287694eddc1aad65bd64))
18 |
19 | ### Documentation
20 |
21 | - Update badge for CI workflow ([#333](https://github.com/browniebroke/flake8-django-migrations/issues/333)) ([`9072f9c`](https://github.com/browniebroke/flake8-django-migrations/commit/9072f9c56a8293f0bc578c8af0bf73efc23fa1ac))
22 |
23 | ## v0.3.0 (2022-11-20)
24 |
25 | ### Feature
26 |
27 | - Officially support Python 3.10 & 3.11 ([#301](https://github.com/browniebroke/flake8-django-migrations/issues/301)) ([`10a2572`](https://github.com/browniebroke/flake8-django-migrations/commit/10a25729ef8fb34f37b7b3490c858e076040d673))
28 |
29 | ## v0.2.1 (2022-06-06)
30 |
31 | ### Fix
32 |
33 | - **deps:** Revert PSR upgrade ([`a7f4cf7`](https://github.com/browniebroke/flake8-django-migrations/commit/a7f4cf762a3c6ccb2283532f552520c9ae3c98ec))
34 |
35 | ## v0.2.0 (2022-01-24)
36 |
37 | ### Feature
38 |
39 | - Drop support for Python 3.6 ([`cda2daa`](https://github.com/browniebroke/flake8-django-migrations/commit/cda2daa7a31d956a87f46862a83253f7535a5c36))
40 |
41 | ## v0.1.1 (2021-04-06)
42 |
43 | ### Fix
44 |
45 | - Release in a separate environment ([`71550b8`](https://github.com/browniebroke/flake8-django-migrations/commit/71550b8d06f245d6d6046312ba77002185a8a990))
46 |
47 | ## v0.1.0 (2020-11-10)
48 |
49 | ### Feature
50 |
51 | - DM001: `RemoveField` operation should be wrapped in `SeparateDatabaseAndState` ([`c571e0e`](https://github.com/browniebroke/flake8-django-migrations/commit/c571e0e026fbef9ba85782ff562cbdf9c6a763ed))
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flake8-django-migrations
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | ---
31 |
32 | **Source Code**: https://github.com/browniebroke/flake8-django-migrations
33 |
34 | ---
35 |
36 | Flake8 plugin to lint for backwards incompatible database migrations.
37 |
38 | ## Installation
39 |
40 | Install using `pip` (or your favourite package manager):
41 |
42 | ```sh
43 | pip install flake8-django-migrations
44 | ```
45 |
46 | ## Usage
47 |
48 | This plugin should be used automatically when running flake8:
49 |
50 | ```sh
51 | flake8
52 | ```
53 |
54 | ## Checks
55 |
56 | This is the list of checks currently implemented by this plugin.
57 |
58 | ### DM001
59 |
60 | `RemoveField` operation should be wrapped in `SeparateDatabaseAndState`.
61 |
62 | Such an operation should be run in two separate steps, using `SeparateDatabaseAndState`, otherwise it is not backwards compatible.
63 |
64 | - Step 1: remove the field from the model and code. For foreign key fields, the foreign key constraint should also be dropped.
65 | - Step 2: remove the column from the database.
66 |
67 | #### Bad
68 |
69 | ```python
70 | class Migration(migrations.Migration):
71 | operations = [
72 | migrations.RemoveField(
73 | model_name="order",
74 | name="total",
75 | ),
76 | ]
77 | ```
78 |
79 | #### Good
80 |
81 | ```python
82 | class Migration(migrations.Migration):
83 | operations = [
84 | migrations.SeparateDatabaseAndState(
85 | state_operations=[
86 | migrations.RemoveField(
87 | model_name="order",
88 | name="total",
89 | ),
90 | ],
91 | ),
92 | ]
93 | ```
94 |
--------------------------------------------------------------------------------
/commitlint.config.mjs:
--------------------------------------------------------------------------------
1 | export default {
2 | extends: ["@commitlint/config-conventional"],
3 | rules: {
4 | "header-max-length": [0, "always", Infinity],
5 | "body-max-line-length": [0, "always", Infinity],
6 | "footer-max-line-length": [0, "always", Infinity],
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | build-backend = "setuptools.build_meta"
3 | requires = [ "setuptools" ]
4 |
5 | [project]
6 | name = "flake8-django-migrations"
7 | version = "1.1.0"
8 | description = "Flake8 plugin to lint for backwards incompatible database migrations"
9 | readme = "README.md"
10 | keywords = [
11 | "django",
12 | "flake8",
13 | "lint",
14 | "migrations",
15 | ]
16 | license = { text = "MIT" }
17 | authors = [
18 | { name = "Bruno Alla", email = "bruno.alla@festicket.com" },
19 | ]
20 | requires-python = ">=3.9"
21 | classifiers = [
22 | "Development Status :: 1 - Planning",
23 | "Environment :: Console",
24 | "Framework :: Flake8",
25 | "Intended Audience :: Developers",
26 | "Natural Language :: English",
27 | "Operating System :: OS Independent",
28 | "Programming Language :: Python :: 3 :: Only",
29 | "Programming Language :: Python :: 3.9",
30 | "Programming Language :: Python :: 3.10",
31 | "Programming Language :: Python :: 3.11",
32 | "Programming Language :: Python :: 3.12",
33 | "Programming Language :: Python :: 3.13",
34 | "Topic :: Software Development :: Quality Assurance",
35 | ]
36 |
37 | dependencies = [
38 | "astor>=0.1",
39 | "flake8>=3.7",
40 | "importlib-metadata>=0.9; python_version<'3.8'",
41 | ]
42 | urls.repository = "https://github.com/browniebroke/flake8-django-migrations"
43 | entry-points."flake8.extension".DM = "flake8_django_migrations:Plugin"
44 |
45 | [dependency-groups]
46 | dev = [
47 | "pytest>=8,<9",
48 | "pytest-cov>=6,<7",
49 | ]
50 |
51 | [tool.ruff]
52 | line-length = 88
53 |
54 | lint.select = [
55 | "B", # flake8-bugbear
56 | "C4", # flake8-comprehensions
57 | "D", # flake8-docstrings
58 | "E", # pycodestyle
59 | "F", # pyflake
60 | "I", # isort
61 | "RUF", # ruff specific
62 | "S", # flake8-bandit
63 | "UP", # pyupgrade
64 | "W", # pycodestyle
65 | ]
66 | lint.ignore = [
67 | "D100", # Missing docstring in public module
68 | "D104", # Missing docstring in public package
69 | "D107", # Missing docstring in `__init__`
70 | "D203", # 1 blank line required before class docstring
71 | "D212", # Multi-line docstring summary should start at the first line
72 | "D401", # First line of docstring should be in imperative mood
73 | ]
74 | lint.per-file-ignores."tests/**/*" = [
75 | "D100",
76 | "D101",
77 | "D102",
78 | "D103",
79 | "D104",
80 | "S101",
81 | ]
82 | lint.isort.known-first-party = [ "flake8_django_migrations" ]
83 |
84 | [tool.pytest.ini_options]
85 | addopts = "-v -Wdefault --cov=flake8_django_migrations"
86 | pythonpath = [ "src" ]
87 |
88 | [tool.coverage.run]
89 | branch = true
90 | source = [ "flake8_django_migrations" ]
91 |
92 | [tool.coverage.report]
93 | ignore_errors = true
94 |
95 | [tool.semantic_release.branches.main]
96 | match = "main"
97 |
98 | [tool.semantic_release.branches.noop]
99 | match = "(?!main$)"
100 | prerelease = true
101 |
102 | [tool.semantic_release]
103 | version_toml = [ "pyproject.toml:project.version" ]
104 | version_variables = [
105 | "src/flake8_django_migrations/__init__.py:__version__",
106 | ]
107 | build_command = """
108 | pip install uv
109 | uv lock
110 | git add uv.lock
111 | uv build
112 | """
113 |
114 | [tool.semantic_release.changelog]
115 | exclude_commit_patterns = [
116 | "chore.*",
117 | "ci.*",
118 | "Merge pull request .*",
119 | ]
120 |
121 | [tool.semantic_release.changelog.environment]
122 | keep_trailing_newline = true
123 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["github>browniebroke/renovate-configs:python"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/flake8_django_migrations/__init__.py:
--------------------------------------------------------------------------------
1 | from .plugin import Plugin
2 |
3 | __version__ = "1.1.0"
4 | __all__ = ("Plugin",)
5 |
--------------------------------------------------------------------------------
/src/flake8_django_migrations/checkers/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logger = logging.getLogger(__name__)
4 |
--------------------------------------------------------------------------------
/src/flake8_django_migrations/checkers/base.py:
--------------------------------------------------------------------------------
1 | import ast
2 |
3 |
4 | class Checker:
5 | """Base class for the plugin."""
6 |
7 | @staticmethod
8 | def get_call_name(node: ast.Call):
9 | """Return call name for the given node."""
10 | if isinstance(node.func, ast.Attribute):
11 | return node.func.attr
12 | elif isinstance(node.func, ast.Name):
13 | return node.func.id
14 |
15 | def run(self, node: ast.expr):
16 | """Entry point to be implemented in subclasses."""
17 | raise NotImplementedError()
18 |
--------------------------------------------------------------------------------
/src/flake8_django_migrations/checkers/issue.py:
--------------------------------------------------------------------------------
1 | class Issue:
2 | """Represent data points about a single issue."""
3 |
4 | code: str
5 | message: str
6 | lineno: int
7 | col: int
8 |
--------------------------------------------------------------------------------
/src/flake8_django_migrations/checkers/remove_field.py:
--------------------------------------------------------------------------------
1 | import ast
2 | from typing import Any
3 |
4 | from .base import Checker
5 | from .issue import Issue
6 |
7 |
8 | class DM001(Issue):
9 | """Dangerous RemoveField operation."""
10 |
11 | code: str = "DM001"
12 | message: str = (
13 | f"{code} RemoveField operation should be wrapped in SeparateDatabaseAndState"
14 | )
15 |
16 | def __init__(self, lineno: int, col: int) -> None:
17 | self.lineno = lineno
18 | self.col = col
19 |
20 |
21 | class RemoveFieldChecker(Checker):
22 | """Checker that detects dangerous RemoveField operation."""
23 |
24 | def run(self, node: ast.Call) -> Any:
25 | """Check whether the operation is safe."""
26 | issues = []
27 | call_name = self.get_call_name(node)
28 | if call_name == "SeparateDatabaseAndState":
29 | pass
30 | elif call_name == "RemoveField":
31 | issues.append(DM001(lineno=node.lineno, col=node.col_offset))
32 | return issues
33 |
--------------------------------------------------------------------------------
/src/flake8_django_migrations/plugin.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import ast
4 | import importlib.metadata as importlib_metadata
5 | from collections.abc import Generator
6 | from typing import Any, ClassVar
7 |
8 | from .checkers.base import Checker
9 | from .checkers.issue import Issue
10 | from .checkers.remove_field import RemoveFieldChecker
11 |
12 |
13 | class Visitor(ast.NodeVisitor):
14 | """Custom AST visitor."""
15 |
16 | checkers: ClassVar[list[Checker]] = [RemoveFieldChecker()]
17 |
18 | def __init__(self) -> None:
19 | self.issues: list[Issue] = []
20 |
21 | def visit_Call(self, node: ast.Call) -> Any:
22 | """Called when visiting a function called."""
23 | for checker in self.checkers:
24 | issues = checker.run(node)
25 | if issues:
26 | self.issues.extend(issues)
27 |
28 |
29 | class Plugin:
30 | """Declare the flake8 plugin."""
31 |
32 | name = "flake8_django_migrations"
33 | version = importlib_metadata.version(name)
34 |
35 | def __init__(self, tree: ast.AST):
36 | self._tree = tree
37 |
38 | def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]:
39 | """Plugin entry point."""
40 | visitor = Visitor()
41 | visitor.visit(self._tree)
42 |
43 | for issue in visitor.issues:
44 | yield issue.lineno, issue.col, issue.message, type(self)
45 |
--------------------------------------------------------------------------------
/templates/.changelog-old.md:
--------------------------------------------------------------------------------
1 | ## v1.0.0 (2023-06-27)
2 |
3 | ### Feature
4 |
5 | - Drop support for Python 3.7 ([`d41a017`](https://github.com/browniebroke/flake8-django-migrations/commit/d41a017ef1a830c59d9b287694eddc1aad65bd64))
6 |
7 | ### Breaking
8 |
9 | - Drop support for Python 3.7 as it reached EOL on June 27, 2023. More infos: https://devguide.python.org/versions/ ([`d41a017`](https://github.com/browniebroke/flake8-django-migrations/commit/d41a017ef1a830c59d9b287694eddc1aad65bd64))
10 |
11 | ### Documentation
12 |
13 | - Update badge for CI workflow ([#333](https://github.com/browniebroke/flake8-django-migrations/issues/333)) ([`9072f9c`](https://github.com/browniebroke/flake8-django-migrations/commit/9072f9c56a8293f0bc578c8af0bf73efc23fa1ac))
14 |
15 | ## v0.3.0 (2022-11-20)
16 |
17 | ### Feature
18 |
19 | - Officially support Python 3.10 & 3.11 ([#301](https://github.com/browniebroke/flake8-django-migrations/issues/301)) ([`10a2572`](https://github.com/browniebroke/flake8-django-migrations/commit/10a25729ef8fb34f37b7b3490c858e076040d673))
20 |
21 | ## v0.2.1 (2022-06-06)
22 |
23 | ### Fix
24 |
25 | - **deps:** Revert PSR upgrade ([`a7f4cf7`](https://github.com/browniebroke/flake8-django-migrations/commit/a7f4cf762a3c6ccb2283532f552520c9ae3c98ec))
26 |
27 | ## v0.2.0 (2022-01-24)
28 |
29 | ### Feature
30 |
31 | - Drop support for Python 3.6 ([`cda2daa`](https://github.com/browniebroke/flake8-django-migrations/commit/cda2daa7a31d956a87f46862a83253f7535a5c36))
32 |
33 | ## v0.1.1 (2021-04-06)
34 |
35 | ### Fix
36 |
37 | - Release in a separate environment ([`71550b8`](https://github.com/browniebroke/flake8-django-migrations/commit/71550b8d06f245d6d6046312ba77002185a8a990))
38 |
39 | ## v0.1.0 (2020-11-10)
40 |
41 | ### Feature
42 |
43 | - DM001: `RemoveField` operation should be wrapped in `SeparateDatabaseAndState` ([`c571e0e`](https://github.com/browniebroke/flake8-django-migrations/commit/c571e0e026fbef9ba85782ff562cbdf9c6a763ed))
44 |
--------------------------------------------------------------------------------
/templates/CHANGELOG.md.j2:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | {%- for version, release in context.history.released.items() %}
4 | {%- if version.as_tag() > "v1.0.0" %}
5 |
6 | ## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }})
7 |
8 | {%- for category, commits in release["elements"].items() %}{% if category != "unknown" %}
9 | {# Category title: Breaking, Fix, Documentation #}
10 | ### {{ category | capitalize }}
11 | {# List actual changes in the category #}
12 | {%- for commit in commits %}
13 | - {{ commit.descriptions[0] | capitalize }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
14 | {%- endfor %}{# for commit #}
15 |
16 | {%- endif %}{% endfor %}{# for category, commits #}
17 |
18 | {%- endif %}{# if version.as_tag() #}
19 |
20 | {%- endfor %}{# for version, release #}
21 |
22 | {% include ".changelog-old.md" %}{# include old changelog at the end -#}
23 |
--------------------------------------------------------------------------------
/tests/test_flake8_django_migrations.py:
--------------------------------------------------------------------------------
1 | """Tests for `flake8_django_migrations` package."""
2 |
3 | import ast
4 | from textwrap import dedent
5 |
6 | from flake8_django_migrations import Plugin
7 |
8 |
9 | def _results(s: str) -> set[str]:
10 | tree = ast.parse(dedent(s))
11 | plugin = Plugin(tree)
12 | return {f"{line}:{col} {msg}" for line, col, msg, _ in plugin.run()}
13 |
14 |
15 | def test_trivial_case():
16 | assert _results("") == set()
17 |
18 |
19 | def test_plugin_version():
20 | assert isinstance(Plugin.version, str)
21 | assert "." in Plugin.version
22 |
23 |
24 | def test_plugin_name():
25 | assert isinstance(Plugin.name, str)
26 |
27 |
28 | def test_unsafe_drop_column():
29 | input_code = """
30 | class Migration(migrations.Migration):
31 | operations = [
32 | migrations.RemoveField(
33 | model_name="order",
34 | name="total",
35 | ),
36 | ]
37 | """
38 | assert _results(input_code) == {
39 | "4:8 DM001 RemoveField operation should be wrapped in SeparateDatabaseAndState",
40 | }
41 |
42 |
43 | def test_safe_remove_field():
44 | input_code = """
45 | class Migration(migrations.Migration):
46 | operations = [
47 | migrations.SeparateDatabaseAndState(
48 | state_operations=[
49 | migrations.RemoveField(
50 | model_name="order",
51 | name="total",
52 | ),
53 | ],
54 | ),
55 | ]
56 | """
57 |
58 | assert _results(input_code) == set()
59 |
--------------------------------------------------------------------------------
/uv.lock:
--------------------------------------------------------------------------------
1 | version = 1
2 | revision = 2
3 | requires-python = ">=3.9"
4 |
5 | [[package]]
6 | name = "astor"
7 | version = "0.8.1"
8 | source = { registry = "https://pypi.org/simple" }
9 | sdist = { url = "https://files.pythonhosted.org/packages/5a/21/75b771132fee241dfe601d39ade629548a9626d1d39f333fde31bc46febe/astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e", size = 35090, upload-time = "2019-12-10T01:50:35.51Z" }
10 | wheels = [
11 | { url = "https://files.pythonhosted.org/packages/c3/88/97eef84f48fa04fbd6750e62dcceafba6c63c81b7ac1420856c8dcc0a3f9/astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5", size = 27488, upload-time = "2019-12-10T01:50:33.628Z" },
12 | ]
13 |
14 | [[package]]
15 | name = "colorama"
16 | version = "0.4.6"
17 | source = { registry = "https://pypi.org/simple" }
18 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
19 | wheels = [
20 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
21 | ]
22 |
23 | [[package]]
24 | name = "coverage"
25 | version = "7.8.0"
26 | source = { registry = "https://pypi.org/simple" }
27 | sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872, upload-time = "2025-03-30T20:36:45.376Z" }
28 | wheels = [
29 | { url = "https://files.pythonhosted.org/packages/78/01/1c5e6ee4ebaaa5e079db933a9a45f61172048c7efa06648445821a201084/coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe", size = 211379, upload-time = "2025-03-30T20:34:53.904Z" },
30 | { url = "https://files.pythonhosted.org/packages/e9/16/a463389f5ff916963471f7c13585e5f38c6814607306b3cb4d6b4cf13384/coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28", size = 211814, upload-time = "2025-03-30T20:34:56.959Z" },
31 | { url = "https://files.pythonhosted.org/packages/b8/b1/77062b0393f54d79064dfb72d2da402657d7c569cfbc724d56ac0f9c67ed/coverage-7.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3", size = 240937, upload-time = "2025-03-30T20:34:58.751Z" },
32 | { url = "https://files.pythonhosted.org/packages/d7/54/c7b00a23150083c124e908c352db03bcd33375494a4beb0c6d79b35448b9/coverage-7.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676", size = 238849, upload-time = "2025-03-30T20:35:00.521Z" },
33 | { url = "https://files.pythonhosted.org/packages/f7/ec/a6b7cfebd34e7b49f844788fda94713035372b5200c23088e3bbafb30970/coverage-7.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d", size = 239986, upload-time = "2025-03-30T20:35:02.307Z" },
34 | { url = "https://files.pythonhosted.org/packages/21/8c/c965ecef8af54e6d9b11bfbba85d4f6a319399f5f724798498387f3209eb/coverage-7.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a", size = 239896, upload-time = "2025-03-30T20:35:04.141Z" },
35 | { url = "https://files.pythonhosted.org/packages/40/83/070550273fb4c480efa8381735969cb403fa8fd1626d74865bfaf9e4d903/coverage-7.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c", size = 238613, upload-time = "2025-03-30T20:35:05.889Z" },
36 | { url = "https://files.pythonhosted.org/packages/07/76/fbb2540495b01d996d38e9f8897b861afed356be01160ab4e25471f4fed1/coverage-7.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f", size = 238909, upload-time = "2025-03-30T20:35:07.76Z" },
37 | { url = "https://files.pythonhosted.org/packages/a3/7e/76d604db640b7d4a86e5dd730b73e96e12a8185f22b5d0799025121f4dcb/coverage-7.8.0-cp310-cp310-win32.whl", hash = "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f", size = 213948, upload-time = "2025-03-30T20:35:09.144Z" },
38 | { url = "https://files.pythonhosted.org/packages/5c/a7/f8ce4aafb4a12ab475b56c76a71a40f427740cf496c14e943ade72e25023/coverage-7.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23", size = 214844, upload-time = "2025-03-30T20:35:10.734Z" },
39 | { url = "https://files.pythonhosted.org/packages/2b/77/074d201adb8383addae5784cb8e2dac60bb62bfdf28b2b10f3a3af2fda47/coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", size = 211493, upload-time = "2025-03-30T20:35:12.286Z" },
40 | { url = "https://files.pythonhosted.org/packages/a9/89/7a8efe585750fe59b48d09f871f0e0c028a7b10722b2172dfe021fa2fdd4/coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", size = 211921, upload-time = "2025-03-30T20:35:14.18Z" },
41 | { url = "https://files.pythonhosted.org/packages/e9/ef/96a90c31d08a3f40c49dbe897df4f1fd51fb6583821a1a1c5ee30cc8f680/coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", size = 244556, upload-time = "2025-03-30T20:35:15.616Z" },
42 | { url = "https://files.pythonhosted.org/packages/89/97/dcd5c2ce72cee9d7b0ee8c89162c24972fb987a111b92d1a3d1d19100c61/coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", size = 242245, upload-time = "2025-03-30T20:35:18.648Z" },
43 | { url = "https://files.pythonhosted.org/packages/b2/7b/b63cbb44096141ed435843bbb251558c8e05cc835c8da31ca6ffb26d44c0/coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", size = 244032, upload-time = "2025-03-30T20:35:20.131Z" },
44 | { url = "https://files.pythonhosted.org/packages/97/e3/7fa8c2c00a1ef530c2a42fa5df25a6971391f92739d83d67a4ee6dcf7a02/coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", size = 243679, upload-time = "2025-03-30T20:35:21.636Z" },
45 | { url = "https://files.pythonhosted.org/packages/4f/b3/e0a59d8df9150c8a0c0841d55d6568f0a9195692136c44f3d21f1842c8f6/coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", size = 241852, upload-time = "2025-03-30T20:35:23.525Z" },
46 | { url = "https://files.pythonhosted.org/packages/9b/82/db347ccd57bcef150c173df2ade97976a8367a3be7160e303e43dd0c795f/coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", size = 242389, upload-time = "2025-03-30T20:35:25.09Z" },
47 | { url = "https://files.pythonhosted.org/packages/21/f6/3f7d7879ceb03923195d9ff294456241ed05815281f5254bc16ef71d6a20/coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", size = 213997, upload-time = "2025-03-30T20:35:26.914Z" },
48 | { url = "https://files.pythonhosted.org/packages/28/87/021189643e18ecf045dbe1e2071b2747901f229df302de01c998eeadf146/coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", size = 214911, upload-time = "2025-03-30T20:35:28.498Z" },
49 | { url = "https://files.pythonhosted.org/packages/aa/12/4792669473297f7973518bec373a955e267deb4339286f882439b8535b39/coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", size = 211684, upload-time = "2025-03-30T20:35:29.959Z" },
50 | { url = "https://files.pythonhosted.org/packages/be/e1/2a4ec273894000ebedd789e8f2fc3813fcaf486074f87fd1c5b2cb1c0a2b/coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", size = 211935, upload-time = "2025-03-30T20:35:31.912Z" },
51 | { url = "https://files.pythonhosted.org/packages/f8/3a/7b14f6e4372786709a361729164125f6b7caf4024ce02e596c4a69bccb89/coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", size = 245994, upload-time = "2025-03-30T20:35:33.455Z" },
52 | { url = "https://files.pythonhosted.org/packages/54/80/039cc7f1f81dcbd01ea796d36d3797e60c106077e31fd1f526b85337d6a1/coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", size = 242885, upload-time = "2025-03-30T20:35:35.354Z" },
53 | { url = "https://files.pythonhosted.org/packages/10/e0/dc8355f992b6cc2f9dcd5ef6242b62a3f73264893bc09fbb08bfcab18eb4/coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", size = 245142, upload-time = "2025-03-30T20:35:37.121Z" },
54 | { url = "https://files.pythonhosted.org/packages/43/1b/33e313b22cf50f652becb94c6e7dae25d8f02e52e44db37a82de9ac357e8/coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", size = 244906, upload-time = "2025-03-30T20:35:39.07Z" },
55 | { url = "https://files.pythonhosted.org/packages/05/08/c0a8048e942e7f918764ccc99503e2bccffba1c42568693ce6955860365e/coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", size = 243124, upload-time = "2025-03-30T20:35:40.598Z" },
56 | { url = "https://files.pythonhosted.org/packages/5b/62/ea625b30623083c2aad645c9a6288ad9fc83d570f9adb913a2abdba562dd/coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", size = 244317, upload-time = "2025-03-30T20:35:42.204Z" },
57 | { url = "https://files.pythonhosted.org/packages/62/cb/3871f13ee1130a6c8f020e2f71d9ed269e1e2124aa3374d2180ee451cee9/coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", size = 214170, upload-time = "2025-03-30T20:35:44.216Z" },
58 | { url = "https://files.pythonhosted.org/packages/88/26/69fe1193ab0bfa1eb7a7c0149a066123611baba029ebb448500abd8143f9/coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", size = 214969, upload-time = "2025-03-30T20:35:45.797Z" },
59 | { url = "https://files.pythonhosted.org/packages/f3/21/87e9b97b568e223f3438d93072479c2f36cc9b3f6b9f7094b9d50232acc0/coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd", size = 211708, upload-time = "2025-03-30T20:35:47.417Z" },
60 | { url = "https://files.pythonhosted.org/packages/75/be/882d08b28a0d19c9c4c2e8a1c6ebe1f79c9c839eb46d4fca3bd3b34562b9/coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00", size = 211981, upload-time = "2025-03-30T20:35:49.002Z" },
61 | { url = "https://files.pythonhosted.org/packages/7a/1d/ce99612ebd58082fbe3f8c66f6d8d5694976c76a0d474503fa70633ec77f/coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64", size = 245495, upload-time = "2025-03-30T20:35:51.073Z" },
62 | { url = "https://files.pythonhosted.org/packages/dc/8d/6115abe97df98db6b2bd76aae395fcc941d039a7acd25f741312ced9a78f/coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067", size = 242538, upload-time = "2025-03-30T20:35:52.941Z" },
63 | { url = "https://files.pythonhosted.org/packages/cb/74/2f8cc196643b15bc096d60e073691dadb3dca48418f08bc78dd6e899383e/coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008", size = 244561, upload-time = "2025-03-30T20:35:54.658Z" },
64 | { url = "https://files.pythonhosted.org/packages/22/70/c10c77cd77970ac965734fe3419f2c98665f6e982744a9bfb0e749d298f4/coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733", size = 244633, upload-time = "2025-03-30T20:35:56.221Z" },
65 | { url = "https://files.pythonhosted.org/packages/38/5a/4f7569d946a07c952688debee18c2bb9ab24f88027e3d71fd25dbc2f9dca/coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323", size = 242712, upload-time = "2025-03-30T20:35:57.801Z" },
66 | { url = "https://files.pythonhosted.org/packages/bb/a1/03a43b33f50475a632a91ea8c127f7e35e53786dbe6781c25f19fd5a65f8/coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3", size = 244000, upload-time = "2025-03-30T20:35:59.378Z" },
67 | { url = "https://files.pythonhosted.org/packages/6a/89/ab6c43b1788a3128e4d1b7b54214548dcad75a621f9d277b14d16a80d8a1/coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d", size = 214195, upload-time = "2025-03-30T20:36:01.005Z" },
68 | { url = "https://files.pythonhosted.org/packages/12/12/6bf5f9a8b063d116bac536a7fb594fc35cb04981654cccb4bbfea5dcdfa0/coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487", size = 214998, upload-time = "2025-03-30T20:36:03.006Z" },
69 | { url = "https://files.pythonhosted.org/packages/2a/e6/1e9df74ef7a1c983a9c7443dac8aac37a46f1939ae3499424622e72a6f78/coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25", size = 212541, upload-time = "2025-03-30T20:36:04.638Z" },
70 | { url = "https://files.pythonhosted.org/packages/04/51/c32174edb7ee49744e2e81c4b1414ac9df3dacfcb5b5f273b7f285ad43f6/coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42", size = 212767, upload-time = "2025-03-30T20:36:06.503Z" },
71 | { url = "https://files.pythonhosted.org/packages/e9/8f/f454cbdb5212f13f29d4a7983db69169f1937e869a5142bce983ded52162/coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502", size = 256997, upload-time = "2025-03-30T20:36:08.137Z" },
72 | { url = "https://files.pythonhosted.org/packages/e6/74/2bf9e78b321216d6ee90a81e5c22f912fc428442c830c4077b4a071db66f/coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1", size = 252708, upload-time = "2025-03-30T20:36:09.781Z" },
73 | { url = "https://files.pythonhosted.org/packages/92/4d/50d7eb1e9a6062bee6e2f92e78b0998848a972e9afad349b6cdde6fa9e32/coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4", size = 255046, upload-time = "2025-03-30T20:36:11.409Z" },
74 | { url = "https://files.pythonhosted.org/packages/40/9e/71fb4e7402a07c4198ab44fc564d09d7d0ffca46a9fb7b0a7b929e7641bd/coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73", size = 256139, upload-time = "2025-03-30T20:36:13.86Z" },
75 | { url = "https://files.pythonhosted.org/packages/49/1a/78d37f7a42b5beff027e807c2843185961fdae7fe23aad5a4837c93f9d25/coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a", size = 254307, upload-time = "2025-03-30T20:36:16.074Z" },
76 | { url = "https://files.pythonhosted.org/packages/58/e9/8fb8e0ff6bef5e170ee19d59ca694f9001b2ec085dc99b4f65c128bb3f9a/coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883", size = 255116, upload-time = "2025-03-30T20:36:18.033Z" },
77 | { url = "https://files.pythonhosted.org/packages/56/b0/d968ecdbe6fe0a863de7169bbe9e8a476868959f3af24981f6a10d2b6924/coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada", size = 214909, upload-time = "2025-03-30T20:36:19.644Z" },
78 | { url = "https://files.pythonhosted.org/packages/87/e9/d6b7ef9fecf42dfb418d93544af47c940aa83056c49e6021a564aafbc91f/coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257", size = 216068, upload-time = "2025-03-30T20:36:21.282Z" },
79 | { url = "https://files.pythonhosted.org/packages/60/0c/5da94be095239814bf2730a28cffbc48d6df4304e044f80d39e1ae581997/coverage-7.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f", size = 211377, upload-time = "2025-03-30T20:36:23.298Z" },
80 | { url = "https://files.pythonhosted.org/packages/d5/cb/b9e93ebf193a0bb89dbcd4f73d7b0e6ecb7c1b6c016671950e25f041835e/coverage-7.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a", size = 211803, upload-time = "2025-03-30T20:36:25.74Z" },
81 | { url = "https://files.pythonhosted.org/packages/78/1a/cdbfe9e1bb14d3afcaf6bb6e1b9ba76c72666e329cd06865bbd241efd652/coverage-7.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82", size = 240561, upload-time = "2025-03-30T20:36:27.548Z" },
82 | { url = "https://files.pythonhosted.org/packages/59/04/57f1223f26ac018d7ce791bfa65b0c29282de3e041c1cd3ed430cfeac5a5/coverage-7.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814", size = 238488, upload-time = "2025-03-30T20:36:29.175Z" },
83 | { url = "https://files.pythonhosted.org/packages/b7/b1/0f25516ae2a35e265868670384feebe64e7857d9cffeeb3887b0197e2ba2/coverage-7.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c", size = 239589, upload-time = "2025-03-30T20:36:30.876Z" },
84 | { url = "https://files.pythonhosted.org/packages/e0/a4/99d88baac0d1d5a46ceef2dd687aac08fffa8795e4c3e71b6f6c78e14482/coverage-7.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd", size = 239366, upload-time = "2025-03-30T20:36:32.563Z" },
85 | { url = "https://files.pythonhosted.org/packages/ea/9e/1db89e135feb827a868ed15f8fc857160757f9cab140ffee21342c783ceb/coverage-7.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4", size = 237591, upload-time = "2025-03-30T20:36:34.721Z" },
86 | { url = "https://files.pythonhosted.org/packages/1b/6d/ac4d6fdfd0e201bc82d1b08adfacb1e34b40d21a22cdd62cfaf3c1828566/coverage-7.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899", size = 238572, upload-time = "2025-03-30T20:36:36.805Z" },
87 | { url = "https://files.pythonhosted.org/packages/25/5e/917cbe617c230f7f1745b6a13e780a3a1cd1cf328dbcd0fd8d7ec52858cd/coverage-7.8.0-cp39-cp39-win32.whl", hash = "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f", size = 213966, upload-time = "2025-03-30T20:36:38.551Z" },
88 | { url = "https://files.pythonhosted.org/packages/bd/93/72b434fe550135869f9ea88dd36068af19afce666db576e059e75177e813/coverage-7.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3", size = 214852, upload-time = "2025-03-30T20:36:40.209Z" },
89 | { url = "https://files.pythonhosted.org/packages/c4/f1/1da77bb4c920aa30e82fa9b6ea065da3467977c2e5e032e38e66f1c57ffd/coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", size = 203443, upload-time = "2025-03-30T20:36:41.959Z" },
90 | { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435, upload-time = "2025-03-30T20:36:43.61Z" },
91 | ]
92 |
93 | [package.optional-dependencies]
94 | toml = [
95 | { name = "tomli", marker = "python_full_version <= '3.11'" },
96 | ]
97 |
98 | [[package]]
99 | name = "exceptiongroup"
100 | version = "1.2.2"
101 | source = { registry = "https://pypi.org/simple" }
102 | sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" }
103 | wheels = [
104 | { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" },
105 | ]
106 |
107 | [[package]]
108 | name = "flake8"
109 | version = "7.2.0"
110 | source = { registry = "https://pypi.org/simple" }
111 | dependencies = [
112 | { name = "mccabe" },
113 | { name = "pycodestyle" },
114 | { name = "pyflakes" },
115 | ]
116 | sdist = { url = "https://files.pythonhosted.org/packages/e7/c4/5842fc9fc94584c455543540af62fd9900faade32511fab650e9891ec225/flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426", size = 48177, upload-time = "2025-03-29T20:08:39.329Z" }
117 | wheels = [
118 | { url = "https://files.pythonhosted.org/packages/83/5c/0627be4c9976d56b1217cb5187b7504e7fd7d3503f8bfd312a04077bd4f7/flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343", size = 57786, upload-time = "2025-03-29T20:08:37.902Z" },
119 | ]
120 |
121 | [[package]]
122 | name = "flake8-django-migrations"
123 | version = "1.1.0"
124 | source = { editable = "." }
125 | dependencies = [
126 | { name = "astor" },
127 | { name = "flake8" },
128 | ]
129 |
130 | [package.dev-dependencies]
131 | dev = [
132 | { name = "pytest" },
133 | { name = "pytest-cov" },
134 | ]
135 |
136 | [package.metadata]
137 | requires-dist = [
138 | { name = "astor", specifier = ">=0.1" },
139 | { name = "flake8", specifier = ">=3.7" },
140 | { name = "importlib-metadata", marker = "python_full_version < '3.8'", specifier = ">=0.9" },
141 | ]
142 |
143 | [package.metadata.requires-dev]
144 | dev = [
145 | { name = "pytest", specifier = ">=8,<9" },
146 | { name = "pytest-cov", specifier = ">=6,<7" },
147 | ]
148 |
149 | [[package]]
150 | name = "iniconfig"
151 | version = "2.1.0"
152 | source = { registry = "https://pypi.org/simple" }
153 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
154 | wheels = [
155 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
156 | ]
157 |
158 | [[package]]
159 | name = "mccabe"
160 | version = "0.7.0"
161 | source = { registry = "https://pypi.org/simple" }
162 | sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" }
163 | wheels = [
164 | { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" },
165 | ]
166 |
167 | [[package]]
168 | name = "packaging"
169 | version = "25.0"
170 | source = { registry = "https://pypi.org/simple" }
171 | sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
172 | wheels = [
173 | { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
174 | ]
175 |
176 | [[package]]
177 | name = "pluggy"
178 | version = "1.5.0"
179 | source = { registry = "https://pypi.org/simple" }
180 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" }
181 | wheels = [
182 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" },
183 | ]
184 |
185 | [[package]]
186 | name = "pycodestyle"
187 | version = "2.13.0"
188 | source = { registry = "https://pypi.org/simple" }
189 | sdist = { url = "https://files.pythonhosted.org/packages/04/6e/1f4a62078e4d95d82367f24e685aef3a672abfd27d1a868068fed4ed2254/pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae", size = 39312, upload-time = "2025-03-29T17:33:30.669Z" }
190 | wheels = [
191 | { url = "https://files.pythonhosted.org/packages/07/be/b00116df1bfb3e0bb5b45e29d604799f7b91dd861637e4d448b4e09e6a3e/pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9", size = 31424, upload-time = "2025-03-29T17:33:29.405Z" },
192 | ]
193 |
194 | [[package]]
195 | name = "pyflakes"
196 | version = "3.3.2"
197 | source = { registry = "https://pypi.org/simple" }
198 | sdist = { url = "https://files.pythonhosted.org/packages/af/cc/1df338bd7ed1fa7c317081dcf29bf2f01266603b301e6858856d346a12b3/pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b", size = 64175, upload-time = "2025-03-31T13:21:20.34Z" }
199 | wheels = [
200 | { url = "https://files.pythonhosted.org/packages/15/40/b293a4fa769f3b02ab9e387c707c4cbdc34f073f945de0386107d4e669e6/pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a", size = 63164, upload-time = "2025-03-31T13:21:18.503Z" },
201 | ]
202 |
203 | [[package]]
204 | name = "pytest"
205 | version = "8.3.5"
206 | source = { registry = "https://pypi.org/simple" }
207 | dependencies = [
208 | { name = "colorama", marker = "sys_platform == 'win32'" },
209 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
210 | { name = "iniconfig" },
211 | { name = "packaging" },
212 | { name = "pluggy" },
213 | { name = "tomli", marker = "python_full_version < '3.11'" },
214 | ]
215 | sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" }
216 | wheels = [
217 | { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" },
218 | ]
219 |
220 | [[package]]
221 | name = "pytest-cov"
222 | version = "6.1.1"
223 | source = { registry = "https://pypi.org/simple" }
224 | dependencies = [
225 | { name = "coverage", extra = ["toml"] },
226 | { name = "pytest" },
227 | ]
228 | sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload-time = "2025-04-05T14:07:51.592Z" }
229 | wheels = [
230 | { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" },
231 | ]
232 |
233 | [[package]]
234 | name = "tomli"
235 | version = "2.2.1"
236 | source = { registry = "https://pypi.org/simple" }
237 | sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" }
238 | wheels = [
239 | { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" },
240 | { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" },
241 | { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" },
242 | { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" },
243 | { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" },
244 | { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" },
245 | { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" },
246 | { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" },
247 | { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" },
248 | { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" },
249 | { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" },
250 | { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" },
251 | { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" },
252 | { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" },
253 | { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" },
254 | { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" },
255 | { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" },
256 | { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" },
257 | { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" },
258 | { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" },
259 | { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" },
260 | { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" },
261 | { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" },
262 | { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" },
263 | { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" },
264 | { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" },
265 | { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" },
266 | { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" },
267 | { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" },
268 | { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" },
269 | { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
270 | ]
271 |
--------------------------------------------------------------------------------