├── .git-blame-ignore-revs
├── .github
├── dependabot.yml
└── workflows
│ ├── dist-python.yml
│ ├── release-python.yml
│ ├── test-python.yml
│ └── zizmor.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── _themes
│ ├── LICENSE
│ ├── README
│ ├── flask_small
│ │ ├── layout.html
│ │ ├── static
│ │ │ └── flasky.css_t
│ │ └── theme.conf
│ └── flask_theme_support.py
├── conf.py
└── index.rst
├── examples
└── wiki
│ ├── requirements.txt
│ ├── templates
│ ├── 404.html
│ ├── edit.html
│ ├── page.html
│ └── upload.html
│ └── wiki.py
├── flask_pymongo
├── __init__.py
├── _version.py
├── helpers.py
├── py.typed
└── wrappers.py
├── justfile
├── pyproject.toml
├── tests
├── __init__.py
├── test_config.py
├── test_gridfs.py
├── test_json.py
├── test_url_converter.py
├── test_wrappers.py
└── util.py
└── uv.lock
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # ruff formatting of python files
2 | e7ea851e9b6298be479cdda6656f8c5a5dbe2614
3 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # GitHub Actions
4 | - package-ecosystem: "github-actions"
5 | directory: "/"
6 | schedule:
7 | interval: "weekly"
8 | groups:
9 | actions:
10 | patterns:
11 | - "*"
12 | # Python
13 | - package-ecosystem: "pip"
14 | directory: "/"
15 | schedule:
16 | interval: "weekly"
17 |
--------------------------------------------------------------------------------
/.github/workflows/dist-python.yml:
--------------------------------------------------------------------------------
1 | name: Python Dist
2 |
3 | on:
4 | push:
5 | tags:
6 | - "[0-9]+.[0-9]+.[0-9]+"
7 | - "[0-9]+.[0-9]+.[0-9]+.post[0-9]+"
8 | - "[0-9]+.[0-9]+.[0-9]+[a-b][0-9]+"
9 | - "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
10 | workflow_dispatch:
11 | pull_request:
12 | workflow_call:
13 | inputs:
14 | ref:
15 | required: true
16 | type: string
17 |
18 | concurrency:
19 | group: dist-${{ github.ref }}
20 | cancel-in-progress: true
21 |
22 | defaults:
23 | run:
24 | shell: bash -eux {0}
25 |
26 | jobs:
27 | make_dist:
28 | name: Make Dist
29 | runs-on: macos-latest
30 | steps:
31 | - uses: actions/checkout@v4
32 | with:
33 | persist-credentials: false
34 |
35 | - uses: actions/setup-python@v5
36 | with:
37 | # Build sdist on lowest supported Python
38 | python-version: '3.9'
39 |
40 | - name: Install python requirements
41 | run: |
42 | python -m pip install uv rust-just build twine
43 |
44 | - name: Build Dist
45 | run: |
46 | python -m build .
47 |
48 | - name: Test SDist
49 | run: |
50 | python -m twine check --strict dist/*.*
51 | python -m pip install dist/*.gz
52 | cd ..
53 | python -c "from flask_pymongo import PyMongo"
54 |
55 | - uses: actions/upload-artifact@v4
56 | with:
57 | name: "dist"
58 | path: ./dist/*.*
59 |
60 | collect_dist:
61 | runs-on: ubuntu-latest
62 | needs: [make_dist]
63 | name: Download Dist
64 | steps:
65 | - name: Download all workflow run artifacts
66 | uses: actions/download-artifact@v4
67 | - name: Flatten directory
68 | working-directory: .
69 | run: |
70 | find . -mindepth 2 -type f -exec mv {} . \;
71 | find . -type d -empty -delete
72 | - uses: actions/upload-artifact@v4
73 | with:
74 | name: all-dist-${{ github.run_id }}
75 | path: "./*"
76 |
--------------------------------------------------------------------------------
/.github/workflows/release-python.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | following_version:
7 | description: "The post (dev) version to set"
8 | dry_run:
9 | description: "Dry Run?"
10 | default: false
11 | type: boolean
12 | schedule:
13 | - cron: '30 5 * * *'
14 |
15 | env:
16 | # Changes per repo
17 | PRODUCT_NAME: Flask-PyMongo
18 | # Constant
19 | # inputs will be empty on a scheduled run. so, we only set dry_run
20 | # to 'false' when the input is set to 'false'.
21 | DRY_RUN: ${{ ! contains(inputs.dry_run, 'false') }}
22 | FOLLOWING_VERSION: ${{ inputs.following_version || '' }}
23 |
24 | concurrency:
25 | group: release-${{ github.ref }}
26 | cancel-in-progress: true
27 |
28 | defaults:
29 | run:
30 | shell: bash -eux {0}
31 |
32 | jobs:
33 | pre-publish:
34 | environment: release
35 | runs-on: ubuntu-latest
36 | if: github.repository_owner == 'mongodb-labs' || github.event_name == 'workflow_dispatch'
37 | permissions:
38 | id-token: write
39 | contents: write
40 | outputs:
41 | version: ${{ steps.pre-publish.outputs.version }}
42 | steps:
43 | - uses: mongodb-labs/drivers-github-tools/secure-checkout@v2
44 | with:
45 | app_id: ${{ vars.APP_ID }}
46 | private_key: ${{ secrets.APP_PRIVATE_KEY }}
47 | - uses: mongodb-labs/drivers-github-tools/setup@v2
48 | with:
49 | aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}
50 | aws_region_name: ${{ vars.AWS_REGION_NAME }}
51 | aws_secret_id: ${{ secrets.AWS_SECRET_ID }}
52 | artifactory_username: ${{ vars.ARTIFACTORY_USERNAME }}
53 | - uses: mongodb-labs/drivers-github-tools/python-labs/pre-publish@v2
54 | id: pre-publish
55 | with:
56 | dry_run: ${{ env.DRY_RUN }}
57 |
58 | build-dist:
59 | needs: [pre-publish]
60 | uses: ./.github/workflows/dist-python.yml
61 | with:
62 | ref: ${{ needs.pre-publish.outputs.version }}
63 |
64 | publish:
65 | # https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#publishing-the-distribution-to-pypi
66 | needs: [build-dist]
67 | if: (github.repository_owner == 'mongodb-labs' && github.event_name != 'pull_request') || github.event_name == 'workflow_dispatch'
68 | runs-on: ubuntu-latest
69 | environment: release
70 | permissions:
71 | id-token: write
72 | steps:
73 | - name: Download all the dists
74 | uses: actions/download-artifact@v4
75 | with:
76 | name: all-dist-${{ github.run_id }}
77 | path: dist/
78 | - name: Publish distribution 📦 to PyPI
79 | if: startsWith(env.DRY_RUN, 'false')
80 | uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # release/v1
81 |
82 | post-publish:
83 | needs: [publish]
84 | runs-on: ubuntu-latest
85 | environment: release
86 | permissions:
87 | id-token: write
88 | contents: write
89 | attestations: write
90 | security-events: write
91 | steps:
92 | - uses: mongodb-labs/drivers-github-tools/secure-checkout@v2
93 | with:
94 | app_id: ${{ vars.APP_ID }}
95 | private_key: ${{ secrets.APP_PRIVATE_KEY }}
96 | - uses: mongodb-labs/drivers-github-tools/setup@v2
97 | with:
98 | aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}
99 | aws_region_name: ${{ vars.AWS_REGION_NAME }}
100 | aws_secret_id: ${{ secrets.AWS_SECRET_ID }}
101 | artifactory_username: ${{ vars.ARTIFACTORY_USERNAME }}
102 | - uses: mongodb-labs/drivers-github-tools/python-labs/post-publish@v2
103 | with:
104 | following_version: ${{ env.FOLLOWING_VERSION }}
105 | product_name: ${{ env.PRODUCT_NAME }}
106 | token: ${{ github.token }}
107 | dry_run: ${{ env.DRY_RUN }}
108 |
--------------------------------------------------------------------------------
/.github/workflows/test-python.yml:
--------------------------------------------------------------------------------
1 | name: Python Tests
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | pull_request:
7 |
8 | concurrency:
9 | group: tests-${{ github.ref }}
10 | cancel-in-progress: true
11 |
12 | defaults:
13 | run:
14 | shell: bash -eux {0}
15 |
16 | env:
17 | MIN_PYTHON: "3.9"
18 | MIN_MONGODB: "4.0"
19 | MAX_MONGODB: "8.0"
20 |
21 | jobs:
22 | static:
23 | runs-on: ubuntu-latest
24 | steps:
25 | - uses: actions/checkout@v4
26 | with:
27 | persist-credentials: false
28 | fetch-depth: 0
29 | - name: Install uv
30 | uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v5
31 | with:
32 | enable-cache: true
33 | python-version: ${{ matrix.python-version }}
34 | - uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
35 | - run: just install
36 | - run: just lint
37 | - run: just docs
38 | - run: just doctest
39 | build:
40 | runs-on: ${{ matrix.os }}
41 | strategy:
42 | matrix:
43 | os: ["ubuntu-latest", "macos-latest", "windows-latest"]
44 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
45 | fail-fast: false
46 | name: CPython ${{ matrix.python-version }}-${{ matrix.os }}
47 | steps:
48 | - uses: actions/checkout@v4
49 | with:
50 | persist-credentials: false
51 | fetch-depth: 0
52 | - name: Install uv
53 | uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v5
54 | with:
55 | enable-cache: true
56 | python-version: ${{ matrix.python-version }}
57 | - uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
58 | - name: Start MongoDB on Linux
59 | if: ${{ startsWith(runner.os, 'Linux') }}
60 | uses: supercharge/mongodb-github-action@90004df786821b6308fb02299e5835d0dae05d0d # 1.12.0
61 | with:
62 | mongodb-version: ${{ env.MAX_MONGODB }}
63 | mongodb-replica-set: test-rs
64 | - name: Start MongoDB on MacOS
65 | if: ${{ startsWith(runner.os, 'macOS') }}
66 | run: |
67 | brew tap mongodb/brew
68 | brew install mongodb/brew/mongodb-community@${MAX_MONGODB}
69 | brew services start mongodb-community@${MAX_MONGODB}
70 | - name: Start MongoDB on Windows
71 | if: ${{ startsWith(runner.os, 'Windows') }}
72 | shell: powershell
73 | run: |
74 | mkdir data
75 | mongod --remove
76 | mongod --install --dbpath=$(pwd)/data --logpath=$PWD/mongo.log
77 | net start MongoDB
78 | - run: just install
79 | - run: just test
80 |
81 | build-min:
82 | runs-on: ubuntu-latest
83 | steps:
84 | - uses: actions/checkout@v4
85 | with:
86 | persist-credentials: false
87 | fetch-depth: 0
88 | - name: Install uv
89 | uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v5
90 | with:
91 | enable-cache: true
92 | python-version: ${{ env.MIN_PYTHON }}
93 | - uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
94 | - name: Install uv
95 | uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v5
96 | with:
97 | enable-cache: true
98 | python-version: ${{ env.MIN_PYTHON }}
99 | - uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
100 | - uses: supercharge/mongodb-github-action@90004df786821b6308fb02299e5835d0dae05d0d # 1.12.0
101 | with:
102 | mongodb-version: ${{ env.MIN_MONGODB }}
103 | mongodb-replica-set: test-rs
104 | - name: Run unit tests with minimum dependency versions
105 | run: |
106 | uv sync --python=${MIN_PYTHON} --resolution=lowest-direct
107 | just test
108 |
--------------------------------------------------------------------------------
/.github/workflows/zizmor.yml:
--------------------------------------------------------------------------------
1 | name: GitHub Actions Security Analysis with zizmor
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | pull_request:
7 | branches: ["**"]
8 |
9 | jobs:
10 | zizmor:
11 | name: zizmor latest via Cargo
12 | runs-on: ubuntu-latest
13 | permissions:
14 | security-events: write
15 | steps:
16 | - name: Checkout repository
17 | uses: actions/checkout@v4
18 | with:
19 | persist-credentials: false
20 | - name: Setup Rust
21 | uses: actions-rust-lang/setup-rust-toolchain@9d7e65c320fdb52dcd45ffaa68deb6c02c8754d9 # v1
22 | - name: Get zizmor
23 | run: cargo install zizmor
24 | - name: Run zizmor
25 | run: zizmor --format sarif . > results.sarif
26 | env:
27 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 | - name: Upload SARIF file
29 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3
30 | with:
31 | sarif_file: results.sarif
32 | category: zizmor
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Flask_PyMongo.egg-info
2 | nose*.egg-info
3 | nose*.egg
4 | _build
5 | .coverage
6 | dist/
7 | *.pyc
8 | .pytest_cache/
9 | .eggs/
10 | .tox/
11 | build/
12 |
13 | .idea
14 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 |
2 | repos:
3 | - repo: https://github.com/pre-commit/pre-commit-hooks
4 | rev: v5.0.0
5 | hooks:
6 | - id: check-added-large-files
7 | - id: check-case-conflict
8 | - id: check-toml
9 | - id: check-yaml
10 | - id: debug-statements
11 | - id: end-of-file-fixer
12 | - id: forbid-new-submodules
13 | - id: trailing-whitespace
14 |
15 | # We use the Python version instead of the original version which seems to require Docker
16 | # https://github.com/koalaman/shellcheck-precommit
17 | - repo: https://github.com/shellcheck-py/shellcheck-py
18 | rev: v0.10.0.1
19 | hooks:
20 | - id: shellcheck
21 | name: shellcheck
22 | args: ["--severity=warning"]
23 | stages: [manual]
24 |
25 | - repo: https://github.com/sirosen/check-jsonschema
26 | rev: 0.31.0
27 | hooks:
28 | - id: check-github-workflows
29 | args: ["--verbose"]
30 |
31 | - repo: https://github.com/codespell-project/codespell
32 | rev: "v2.3.0"
33 | hooks:
34 | - id: codespell
35 | args: ["-L", "nd"]
36 | stages: [manual]
37 |
38 | - repo: https://github.com/adamchainz/blacken-docs
39 | rev: "1.19.1"
40 | hooks:
41 | - id: blacken-docs
42 | additional_dependencies: [black==24.*]
43 |
44 | - repo: https://github.com/pre-commit/pygrep-hooks
45 | rev: "v1.10.0"
46 | hooks:
47 | - id: rst-backticks
48 | - id: rst-directive-colons
49 | - id: rst-inline-touching-normal
50 |
51 | - repo: https://github.com/hukkin/mdformat
52 | rev: 0.7.21
53 | hooks:
54 | - id: mdformat
55 | # Optionally add plugins
56 | additional_dependencies:
57 | - mdformat-gfm
58 |
59 | - repo: https://github.com/astral-sh/ruff-pre-commit
60 | # Ruff version.
61 | rev: v0.9.1
62 | hooks:
63 | # Run the linter.
64 | - id: ruff
65 | args: [ --fix, --show-fixes ]
66 | # Run the formatter.
67 | - id: ruff-format
68 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 | build:
3 | os: ubuntu-22.04
4 | tools:
5 | python: "3.11"
6 | jobs:
7 | create_environment:
8 | - asdf plugin add uv
9 | - asdf install uv latest
10 | - asdf global uv latest
11 | install:
12 | - uv sync
13 | build:
14 | html:
15 | - uv run sphinx-build -T -b html docs $READTHEDOCS_OUTPUT/html
16 | sphinx:
17 | configuration: docs/conf.py
18 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 3.1.0: Unreleased
4 |
5 | - TDB
6 |
7 | ## 3.0.1 Jan 29, 2025
8 |
9 | - Fix inclusion of `_version.py` file.
10 |
11 | ## 3.0.0: Jan 29, 2025
12 |
13 | - Support Flask 3.0+ and PyMongo 4.0+.
14 | - Support Python 3.9-3.13.
15 | - Support MongoDB 4.4+.
16 | - Add support for `~flask.json.jsonify()`.
17 |
18 | ## 2.3.0: April 24, 2019
19 |
20 | - Update version compatibility matrix in tests, drop official support
21 | for PyMongo less than 3.3.x.
22 |
23 | ## 2.2.0: November 1, 2018
24 |
25 | - [#117](https://github.com/dcrosta/flask-pymongo/pull/117) Allow
26 | URIs without database name.
27 |
28 | ## 2.1.0: August 6, 2018
29 |
30 | - [#114](https://github.com/dcrosta/flask-pymongo/pull/114) Accept
31 | keyword arguments to `~flask_pymongo.PyMongo.save_file` (Andrew C.
32 | Hawkins).
33 |
34 | ## 2.0.1: July 17, 2018
35 |
36 | - [#113](https://github.com/dcrosta/flask-pymongo/pull/113) Make the
37 | `app` argument to `PyMongo` optional (yarobob).
38 |
39 | ## 2.0.0: July 2, 2018
40 |
41 | **This release is not compatible with Flask-PyMongo 0.5.x or any
42 | earlier version.** You can see an explanation of the reasoning and
43 | changes in [issue
44 | #110](https://github.com/dcrosta/flask-pymongo/issues/110).
45 |
46 | - Only support configuration via URI.
47 | - Don't connect to MongoDB by default.
48 | - Clarify version support of Python, Flask, PyMongo, and MongoDB.
49 | - Readability improvement to `README.md` (MinJae Kwon).
50 |
51 | ## 0.5.2: May 19, 2018
52 |
53 | - [#102](https://github.com/dcrosta/flask-pymongo/pull/102) Return
54 | 404, not 400, when given an invalid input to
55 | BSONObjectIdConverter (Abraham Toriz
56 | Cruz).
57 |
58 | ## 0.5.1: May 24, 2017
59 |
60 | - [#93](https://github.com/dcrosta/flask-pymongo/pull/93) Supply a
61 | default `MONGO_AUTH_MECHANISM` (Mark Unsworth).
62 |
63 | ## 0.5.0: May 21, 2017
64 |
65 | > **This will be the last 0.x series release.** The next non-bugfix
66 | > release will be Flask-PyMongo 2.0, which will introduce backwards
67 | > breaking changes, and will be the foundation for improvements and
68 | > changes going forward. Flask-PyMongo 2.0 will no longer support
69 | > Python 2.6, but will support Python 2.7 and Python 3.3+.
70 |
71 | - [#44](https://github.com/dcrosta/flask-pymongo/issues/44),
72 | [#51](https://github.com/dcrosta/flask-pymongo/pull/51) Redirect
73 | `/` to `/HomePage` in the wiki example (David Awad)
74 | - [#76](https://github.com/dcrosta/flask-pymongo/pull/76) Build on
75 | more modern Python versions (Robson Roberto Souza Peixoto)
76 | - [#79](https://github.com/dcrosta/flask-pymongo/pull/79),
77 | [#84](https://github.com/dcrosta/flask-pymongo/issues/84),
78 | [#85](https://github.com/dcrosta/flask-pymongo/pull/85) Don't use
79 | `flask.ext` import paths any more (ratson, juliascript)
80 | - [#40](https://github.com/dcrosta/flask-pymongo/issues/40),
81 | [#83](https://github.com/dcrosta/flask-pymongo/pull/83),
82 | [#86](https://github.com/dcrosta/flask-pymongo/pull/86) Fix options
83 | parsing from `MONGO_URI` (jobou)
84 | - [#72](https://github.com/dcrosta/flask-pymongo/issues/72),
85 | [#80](https://github.com/dcrosta/flask-pymongo/pull/80) Support
86 | `MONGO_SERVER_SELECTION_TIMEOUT_MS` (Henrik Blidh)
87 | - [#34](https://github.com/dcrosta/flask-pymongo/issues/34),
88 | [#64](https://github.com/dcrosta/flask-pymongo/pull/64),
89 | [#88](https://github.com/dcrosta/flask-pymongo/pull/88) Support
90 | from `MONGO_AUTH_SOURCE` and `MONGO_AUTH_MECHANISM` (Craig Davis)
91 | - [#74](https://github.com/dcrosta/flask-pymongo/issues/74),
92 | [#77](https://github.com/dcrosta/flask-pymongo/issues/77),
93 | [#78](https://github.com/dcrosta/flask-pymongo/pull/78) Fixed
94 | `maxPoolSize` in PyMongo 3.0+ (Henrik Blidh)
95 | - [#82](https://github.com/dcrosta/flask-pymongo/issues/82) Fix
96 | "another user is already authenticated" error message.
97 | - [#54](https://github.com/dcrosta/flask-pymongo/issues/54)
98 | Authenticate against "admin" database if no `MONGO_DBNAME` is
99 | provided.
100 |
101 | ## 0.4.1: January 25, 2016
102 |
103 | - Add the connect keyword:
104 | [#67](https://github.com/dcrosta/flask-pymongo/pull/67).
105 |
106 | ## 0.4.0: October 19, 2015
107 |
108 | - Flask-Pymongo is now compatible with pymongo 3.0+:
109 | [#63](https://github.com/dcrosta/flask-pymongo/pull/63).
110 |
111 | ## 0.3.1: April 9, 2015
112 |
113 | - Flask-PyMongo is now tested against Python 2.6, 2.7, 3.3, and 3.4.
114 | - Flask-PyMongo installation now no longer depends on
115 | [nose](https://pypi.python.org/pypi/nose/).
116 | - [#58](https://github.com/dcrosta/flask-pymongo/pull/58) Update
117 | requirements for PyMongo 3.x (Emmanuel Valette).
118 | - [#43](https://github.com/dcrosta/flask-pymongo/pull/43) Ensure
119 | error is raised when URI database name is parsed as 'None' (Ben
120 | Jeffrey).
121 | - [#50](https://github.com/dcrosta/flask-pymongo/pull/50) Fix a bug
122 | in read preference handling (Kevin Funk).
123 | - [#46](https://github.com/dcrosta/flask-pymongo/issues/46) Cannot
124 | use multiple replicaset instances which run on different ports (Mark
125 | Unsworth).
126 | - [#30](https://github.com/dcrosta/flask-pymongo/issues/30)
127 | ConfiguationError with MONGO_READ_PREFERENCE (Mark Unsworth).
128 |
129 | ## 0.3.0: July 4, 2013
130 |
131 | - This is a minor version bump which introduces backwards breaking
132 | changes! Please read these change notes carefully.
133 | - Removed read preference constants from Flask-PyMongo; to set a read
134 | preference, use the string name or import constants directly from
135 | `pymongo.read_preferences.ReadPreference`.
136 | - [#22 (partial)](https://github.com/dcrosta/flask-pymongo/pull/22)
137 | Add support for `MONGO_SOCKET_TIMEOUT_MS` and
138 | `MONGO_CONNECT_TIMEOUT_MS` options (ultrabug).
139 | - [#27 (partial)](https://github.com/dcrosta/flask-pymongo/pull/27)
140 | Make Flask-PyMongo compatible with Python 3 (Vizzy).
141 |
142 | ## 0.2.1: December 22, 2012
143 |
144 | - [#19](https://github.com/dcrosta/flask-pymongo/pull/19) Added
145 | `MONGO_DOCUMENT_CLASS` config option (jeverling).
146 |
147 | ## 0.2.0: December 15, 2012
148 |
149 | - This is a minor version bump which may introduce backwards breaking
150 | changes! Please read these change notes carefully.
151 | - [#17](https://github.com/dcrosta/flask-pymongo/pull/17) Now using
152 | PyMongo 2.4's `MongoClient` and `MongoReplicaSetClient` objects
153 | instead of `Connection` and `ReplicaSetConnection` classes
154 | (tang0th).
155 | - [#17](https://github.com/dcrosta/flask-pymongo/pull/17) Now
156 | requiring at least PyMongo version 2.4 (tang0th).
157 | - [#17](https://github.com/dcrosta/flask-pymongo/pull/17) The wrapper
158 | class `flask_pymongo.wrappers.Connection` is renamed to
159 | `flask_pymongo.wrappers.MongoClient` (tang0th).
160 | - [#17](https://github.com/dcrosta/flask-pymongo/pull/17) The wrapper
161 | class `flask_pymongo.wrappers.ReplicaSetConnection` is renamed to
162 | `flask_pymongo.wrappers.MongoReplicaSetClient` (tang0th).
163 | - [#18](https://github.com/dcrosta/flask-pymongo/issues/18)
164 | `MONGO_AUTO_START_REQUEST` now defaults to `False` when connecting
165 | using a URI.
166 |
167 | ## 0.1.4: December 15, 2012
168 |
169 | - [#15](https://github.com/dcrosta/flask-pymongo/pull/15) Added
170 | support for `MONGO_MAX_POOL_SIZE` (Fabrice Aneche)
171 |
172 | ## 0.1.3: September 22, 2012
173 |
174 | - Added support for configuration from MongoDB URI.
175 |
176 | ## 0.1.2: June 18, 2012
177 |
178 | - Updated wiki example application
179 | - [#14](https://github.com/dcrosta/flask-pymongo/issues/14) Added
180 | examples and docs to PyPI package.
181 |
182 | ## 0.1.1: May 26, 2012
183 |
184 | - Added support for PyMongo 2.2's "auto start request" feature, by way
185 | of the `MONGO_AUTO_START_REQUEST` configuration flag.
186 | - [#13](https://github.com/dcrosta/flask-pymongo/pull/13) Added
187 | BSONObjectIdConverter (Christoph Herr)
188 | - [#12](https://github.com/dcrosta/flask-pymongo/pull/12) Corrected
189 | documentation typo (Thor Adam)
190 |
191 | ## 0.1: December 21, 2011
192 |
193 | - Initial Release
194 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute to Flask-PyMongo
2 |
3 | Thank you for considering contributing to Flask-PyMongo!
4 |
5 | ## Support questions
6 |
7 | For help and general questions, please consider using the [flask-pymongo
8 | tag](https://stackoverflow.com/questions/tagged/flask-pymongo) on
9 | [StackOverflow](https://stackoverflow.com/) instead of creating issues in
10 | the GitHub project. This will keep the issues in the project focused on
11 | actual bugs and improvement requests.
12 |
13 | ## Reporting issues
14 |
15 | - Describe what you expected to happen.
16 | - If possible, include a [minimal, complete, and verifiable
17 | example](https://stackoverflow.com/help/mcve) to help us identify the issue.
18 | This also helps check that the issue is not with your own code.
19 | - Describe what actually happened. Include the full traceback if there was an
20 | exception.
21 | - List your Flask-PyMongo, PyMongo, and MongoDB versions. If possible, check if
22 | this issue is already fixed in the repository.
23 |
24 | ## Submitting patches
25 |
26 | - All new features must include a test. Flask-PyMongo is tested against a
27 | matrix of all supported versions of Flask, PyMongo, and MongoDB, so tests
28 | ensure that your change works for all users.
29 | - Use [Sphinx](http://www.sphinx-doc.org/en/master/)-style docstrings
30 |
31 | ## Recommended development environment
32 |
33 | We use [justfile](https://just.systems/man/en/packages.html) for task running
34 | and [uv](https://docs.astral.sh/uv/getting-started/installation/) for python project management.
35 |
36 | To set up your dev environment, run `just install`.
37 |
38 | To run the tests, run `just test`. You can pass arguments through to `pytest`.
39 |
40 | To run the linters, run `just lint`.
41 |
42 | To build the docs, run `just docs` and open `_build/html/index.html` in your browser to view the docs.
43 |
44 | ## Contributors
45 |
46 | - [jeverling](https://github.com/jeverling)
47 | - [tang0th](https://github.com/tang0th)
48 | - [Fabrice Aneche](https://github.com/akhenakh)
49 | - [Thor Adam](https://github.com/thoradam)
50 | - [Christoph Herr](https://github.com/jarus)
51 | - [Mark Unsworth](https://github.com/markunsworth)
52 | - [Kevin Funk](https://github.com/k-funk)
53 | - [Ben Jeffrey](https://github.com/jeffbr13)
54 | - [Emmanuel Valette](https://github.com/karec)
55 | - [David Awad](https://github.com/DavidAwad)
56 | - [Robson Roberto Souza Peixoto](https://github.com/robsonpeixoto)
57 | - [juliascript](https://github.com/juliascript)
58 | - [Henrik Blidh](https://github.com/hbldh)
59 | - [jobou](https://github.com/jbouzekri)
60 | - [Craig Davis](https://github.com/blade2005)
61 | - [ratson](https://github.com/ratson)
62 | - [Abraham Toriz Cruz](https://github.com/categulario)
63 | - [MinJae Kwon](https://github.com/mingrammer)
64 | - [yarobob](https://github.com/yarobob)
65 | - [Andrew C. Hawkins](https://github.com/achawkins)
66 | - [Steven Silvester](https://github.com/blink1073)
67 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011-2017, Dan Crosta
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice,
8 | this list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flask-PyMongo
2 |
3 | PyMongo support for Flask applications. Requires `flask>=3.0` and `pymongo>=4.0`
4 |
5 | ## Quickstart
6 |
7 | ```python
8 | from flask import Flask, render_template
9 | from flask_pymongo import PyMongo
10 |
11 | app = Flask(__name__)
12 | app.config["MONGO_URI"] = "mongodb://localhost:27017/myDatabase"
13 | mongo = PyMongo(app)
14 |
15 |
16 | @app.route("/")
17 | def home_page():
18 | online_users = mongo.db.users.find({"online": True})
19 | return render_template("index.html", online_users=online_users)
20 | ```
21 |
22 | ## More Info
23 |
24 | - [Flask-PyMongo Documentation](https://flask-pymongo.readthedocs.org/)
25 |
26 | - [PyMongo Documentation](https://pymongo.readthedocs.io/en/stable/)
27 |
28 | - [Flask Documentation](https://flask.palletsprojects.com/)
29 |
30 | - [Quart-Motor (Motor for the Quart Framework)](https://github.com/marirs/quart-motor)
31 |
32 | - [Motor Non-Blocking mongodb driver for asyncio applications](https://github.com/mongodb/motor)
33 |
--------------------------------------------------------------------------------
/docs/_themes/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010 by Armin Ronacher.
2 |
3 | Some rights reserved.
4 |
5 | Redistribution and use in source and binary forms of the theme, with or
6 | without modification, are permitted provided that the following conditions
7 | are met:
8 |
9 | * Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above
13 | copyright notice, this list of conditions and the following
14 | disclaimer in the documentation and/or other materials provided
15 | with the distribution.
16 |
17 | * The names of the contributors may not be used to endorse or
18 | promote products derived from this software without specific
19 | prior written permission.
20 |
21 | We kindly ask you to only use these themes in an unmodified manner just
22 | for Flask and Flask-related products, not for unrelated projects. If you
23 | like the visual style and want to use it for your own projects, please
24 | consider making some larger changes to the themes (such as changing
25 | font faces, sizes, colors or margins).
26 |
27 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
37 | POSSIBILITY OF SUCH DAMAGE.
38 |
--------------------------------------------------------------------------------
/docs/_themes/README:
--------------------------------------------------------------------------------
1 | Flask Sphinx Styles
2 | ===================
3 |
4 | This repository contains sphinx styles for Flask and Flask related
5 | projects. To use this style in your Sphinx documentation, follow
6 | this guide:
7 |
8 | 1. put this folder as _themes into your docs folder. Alternatively
9 | you can also use git submodules to check out the contents there.
10 | 2. add this to your conf.py:
11 |
12 | sys.path.append(os.path.abspath('_themes'))
13 | html_theme_path = ['_themes']
14 | html_theme = 'flask'
15 |
16 | The following themes exist:
17 |
18 | - 'flask' - the standard flask documentation theme for large
19 | projects
20 | - 'flask_small' - small one-page theme. Intended to be used by
21 | very small addon libraries for flask.
22 |
23 | The following options exist for the flask_small theme:
24 |
25 | [options]
26 | index_logo = '' filename of a picture in _static
27 | to be used as replacement for the
28 | h1 in the index.rst file.
29 | index_logo_height = 120px height of the index logo
30 | github_fork = '' repository name on github for the
31 | "fork me" badge
32 |
--------------------------------------------------------------------------------
/docs/_themes/flask_small/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "basic/layout.html" %}
2 | {% block header %}
3 | {{ super() }}
4 | {% if pagename == 'index' %}
5 |
6 | {% endif %}
7 | {% endblock %}
8 | {% block footer %}
9 | {% if pagename == 'index' %}
10 |
11 | {% endif %}
12 | {% endblock %}
13 | {# do not display relbars #}
14 | {% block relbar1 %}{% endblock %}
15 | {% block relbar2 %}
16 | {% if theme_github_fork %}
17 |
19 | {% endif %}
20 | {% endblock %}
21 | {% block sidebar1 %}{% endblock %}
22 | {% block sidebar2 %}{% endblock %}
23 |
--------------------------------------------------------------------------------
/docs/_themes/flask_small/static/flasky.css_t:
--------------------------------------------------------------------------------
1 | /*
2 | * flasky.css_t
3 | * ~~~~~~~~~~~~
4 | *
5 | * Sphinx stylesheet -- flasky theme based on nature theme.
6 | *
7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
8 | * :license: BSD, see LICENSE for details.
9 | *
10 | */
11 |
12 | @import url("basic.css");
13 |
14 | /* -- page layout ----------------------------------------------------------- */
15 |
16 | body {
17 | font-family: 'Georgia', serif;
18 | font-size: 17px;
19 | color: #000;
20 | background: white;
21 | margin: 0;
22 | padding: 0;
23 | }
24 |
25 | div.documentwrapper {
26 | float: left;
27 | width: 100%;
28 | }
29 |
30 | div.bodywrapper {
31 | margin: 40px auto 0 auto;
32 | width: 700px;
33 | }
34 |
35 | hr {
36 | border: 1px solid #B1B4B6;
37 | }
38 |
39 | div.body {
40 | background-color: #ffffff;
41 | color: #3E4349;
42 | padding: 0 30px 30px 30px;
43 | }
44 |
45 | img.floatingflask {
46 | padding: 0 0 10px 10px;
47 | float: right;
48 | }
49 |
50 | div.footer {
51 | text-align: right;
52 | color: #888;
53 | padding: 10px;
54 | font-size: 14px;
55 | width: 650px;
56 | margin: 0 auto 40px auto;
57 | }
58 |
59 | div.footer a {
60 | color: #888;
61 | text-decoration: underline;
62 | }
63 |
64 | div.related {
65 | line-height: 32px;
66 | color: #888;
67 | }
68 |
69 | div.related ul {
70 | padding: 0 0 0 10px;
71 | }
72 |
73 | div.related a {
74 | color: #444;
75 | }
76 |
77 | /* -- body styles ----------------------------------------------------------- */
78 |
79 | a {
80 | color: #004B6B;
81 | text-decoration: underline;
82 | }
83 |
84 | a:hover {
85 | color: #6D4100;
86 | text-decoration: underline;
87 | }
88 |
89 | div.body {
90 | padding-bottom: 40px; /* saved for footer */
91 | }
92 |
93 | div.body h1,
94 | div.body h2,
95 | div.body h3,
96 | div.body h4,
97 | div.body h5,
98 | div.body h6 {
99 | font-family: 'Garamond', 'Georgia', serif;
100 | font-weight: normal;
101 | margin: 30px 0px 10px 0px;
102 | padding: 0;
103 | }
104 |
105 | {% if theme_index_logo %}
106 | div.indexwrapper h1 {
107 | text-indent: -999999px;
108 | background: url({{ theme_index_logo }}) no-repeat center center;
109 | height: {{ theme_index_logo_height }};
110 | }
111 | {% endif %}
112 |
113 | div.body h2 { font-size: 180%; }
114 | div.body h3 { font-size: 150%; }
115 | div.body h4 { font-size: 130%; }
116 | div.body h5 { font-size: 100%; }
117 | div.body h6 { font-size: 100%; }
118 |
119 | a.headerlink {
120 | color: white;
121 | padding: 0 4px;
122 | text-decoration: none;
123 | }
124 |
125 | a.headerlink:hover {
126 | color: #444;
127 | background: #eaeaea;
128 | }
129 |
130 | div.body p, div.body dd, div.body li {
131 | line-height: 1.4em;
132 | }
133 |
134 | div.admonition {
135 | background: #fafafa;
136 | margin: 20px -30px;
137 | padding: 10px 30px;
138 | border-top: 1px solid #ccc;
139 | border-bottom: 1px solid #ccc;
140 | }
141 |
142 | div.admonition p.admonition-title {
143 | font-family: 'Garamond', 'Georgia', serif;
144 | font-weight: normal;
145 | font-size: 24px;
146 | margin: 0 0 10px 0;
147 | padding: 0;
148 | line-height: 1;
149 | }
150 |
151 | div.admonition p.last {
152 | margin-bottom: 0;
153 | }
154 |
155 | div.highlight{
156 | background-color: white;
157 | }
158 |
159 | dt:target, .highlight {
160 | background: #FAF3E8;
161 | }
162 |
163 | div.note {
164 | background-color: #eee;
165 | border: 1px solid #ccc;
166 | }
167 |
168 | div.seealso {
169 | background-color: #ffc;
170 | border: 1px solid #ff6;
171 | }
172 |
173 | div.topic {
174 | background-color: #eee;
175 | }
176 |
177 | div.warning {
178 | background-color: #ffe4e4;
179 | border: 1px solid #f66;
180 | }
181 |
182 | p.admonition-title {
183 | display: inline;
184 | }
185 |
186 | p.admonition-title:after {
187 | content: ":";
188 | }
189 |
190 | pre, tt {
191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
192 | font-size: 0.85em;
193 | }
194 |
195 | img.screenshot {
196 | }
197 |
198 | tt.descname, tt.descclassname {
199 | font-size: 0.95em;
200 | }
201 |
202 | tt.descname {
203 | padding-right: 0.08em;
204 | }
205 |
206 | img.screenshot {
207 | -moz-box-shadow: 2px 2px 4px #eee;
208 | -webkit-box-shadow: 2px 2px 4px #eee;
209 | box-shadow: 2px 2px 4px #eee;
210 | }
211 |
212 | table.docutils {
213 | border: 1px solid #888;
214 | -moz-box-shadow: 2px 2px 4px #eee;
215 | -webkit-box-shadow: 2px 2px 4px #eee;
216 | box-shadow: 2px 2px 4px #eee;
217 | }
218 |
219 | table.docutils td, table.docutils th {
220 | border: 1px solid #888;
221 | padding: 0.25em 0.7em;
222 | }
223 |
224 | table.field-list, table.footnote {
225 | border: none;
226 | -moz-box-shadow: none;
227 | -webkit-box-shadow: none;
228 | box-shadow: none;
229 | }
230 |
231 | table.footnote {
232 | margin: 15px 0;
233 | width: 100%;
234 | border: 1px solid #eee;
235 | }
236 |
237 | table.field-list th {
238 | padding: 0 0.8em 0 0;
239 | }
240 |
241 | table.field-list td {
242 | padding: 0;
243 | }
244 |
245 | table.footnote td {
246 | padding: 0.5em;
247 | }
248 |
249 | dl {
250 | margin: 0;
251 | padding: 0;
252 | }
253 |
254 | dl dd {
255 | margin-left: 30px;
256 | }
257 |
258 | pre {
259 | padding: 0;
260 | margin: 15px -30px;
261 | padding: 8px;
262 | line-height: 1.3em;
263 | padding: 7px 30px;
264 | background: #eee;
265 | border-radius: 2px;
266 | -moz-border-radius: 2px;
267 | -webkit-border-radius: 2px;
268 | }
269 |
270 | dl pre {
271 | margin-left: -60px;
272 | padding-left: 60px;
273 | }
274 |
275 | tt {
276 | background-color: #ecf0f3;
277 | color: #222;
278 | /* padding: 1px 2px; */
279 | }
280 |
281 | tt.xref, a tt {
282 | background-color: #FBFBFB;
283 | }
284 |
285 | a:hover tt {
286 | background: #EEE;
287 | }
288 |
--------------------------------------------------------------------------------
/docs/_themes/flask_small/theme.conf:
--------------------------------------------------------------------------------
1 | [theme]
2 | inherit = basic
3 | stylesheet = flasky.css
4 | nosidebar = true
5 | pygments_style = flask_theme_support.FlaskyStyle
6 |
7 | [options]
8 | index_logo = ''
9 | index_logo_height = 120px
10 | github_fork = ''
11 |
--------------------------------------------------------------------------------
/docs/_themes/flask_theme_support.py:
--------------------------------------------------------------------------------
1 | # flasky extensions. flasky pygments style based on tango style
2 | from __future__ import annotations
3 |
4 | from pygments.style import Style
5 | from pygments.token import (
6 | Comment,
7 | Error,
8 | Generic,
9 | Keyword,
10 | Literal,
11 | Name,
12 | Number,
13 | Operator,
14 | Other,
15 | Punctuation,
16 | String,
17 | Whitespace,
18 | )
19 |
20 |
21 | class FlaskyStyle(Style):
22 | background_color = "#f8f8f8"
23 | default_style = ""
24 |
25 | styles = {
26 | # No corresponding class for the following:
27 | # Text: "", # class: ""
28 | Whitespace: "underline #f8f8f8", # class: "w"
29 | Error: "#a40000 border:#ef2929", # class: "err"
30 | Other: "#000000", # class "x"
31 | Comment: "italic #8f5902", # class: "c"
32 | Comment.Preproc: "noitalic", # class: "cp"
33 | Keyword: "bold #004461", # class: "k"
34 | Keyword.Constant: "bold #004461", # class: "kc"
35 | Keyword.Declaration: "bold #004461", # class: "kd"
36 | Keyword.Namespace: "bold #004461", # class: "kn"
37 | Keyword.Pseudo: "bold #004461", # class: "kp"
38 | Keyword.Reserved: "bold #004461", # class: "kr"
39 | Keyword.Type: "bold #004461", # class: "kt"
40 | Operator: "#582800", # class: "o"
41 | Operator.Word: "bold #004461", # class: "ow" - like keywords
42 | Punctuation: "bold #000000", # class: "p"
43 | # because special names such as Name.Class, Name.Function, etc.
44 | # are not recognized as such later in the parsing, we choose them
45 | # to look the same as ordinary variables.
46 | Name: "#000000", # class: "n"
47 | Name.Attribute: "#c4a000", # class: "na" - to be revised
48 | Name.Builtin: "#004461", # class: "nb"
49 | Name.Builtin.Pseudo: "#3465a4", # class: "bp"
50 | Name.Class: "#000000", # class: "nc" - to be revised
51 | Name.Constant: "#000000", # class: "no" - to be revised
52 | Name.Decorator: "#888", # class: "nd" - to be revised
53 | Name.Entity: "#ce5c00", # class: "ni"
54 | Name.Exception: "bold #cc0000", # class: "ne"
55 | Name.Function: "#000000", # class: "nf"
56 | Name.Property: "#000000", # class: "py"
57 | Name.Label: "#f57900", # class: "nl"
58 | Name.Namespace: "#000000", # class: "nn" - to be revised
59 | Name.Other: "#000000", # class: "nx"
60 | Name.Tag: "bold #004461", # class: "nt" - like a keyword
61 | Name.Variable: "#000000", # class: "nv" - to be revised
62 | Name.Variable.Class: "#000000", # class: "vc" - to be revised
63 | Name.Variable.Global: "#000000", # class: "vg" - to be revised
64 | Name.Variable.Instance: "#000000", # class: "vi" - to be revised
65 | Number: "#990000", # class: "m"
66 | Literal: "#000000", # class: "l"
67 | Literal.Date: "#000000", # class: "ld"
68 | String: "#4e9a06", # class: "s"
69 | String.Backtick: "#4e9a06", # class: "sb"
70 | String.Char: "#4e9a06", # class: "sc"
71 | String.Doc: "italic #8f5902", # class: "sd" - like a comment
72 | String.Double: "#4e9a06", # class: "s2"
73 | String.Escape: "#4e9a06", # class: "se"
74 | String.Heredoc: "#4e9a06", # class: "sh"
75 | String.Interpol: "#4e9a06", # class: "si"
76 | String.Other: "#4e9a06", # class: "sx"
77 | String.Regex: "#4e9a06", # class: "sr"
78 | String.Single: "#4e9a06", # class: "s1"
79 | String.Symbol: "#4e9a06", # class: "ss"
80 | Generic: "#000000", # class: "g"
81 | Generic.Deleted: "#a40000", # class: "gd"
82 | Generic.Emph: "italic #000000", # class: "ge"
83 | Generic.Error: "#ef2929", # class: "gr"
84 | Generic.Heading: "bold #000080", # class: "gh"
85 | Generic.Inserted: "#00A000", # class: "gi"
86 | Generic.Output: "#888", # class: "go"
87 | Generic.Prompt: "#745334", # class: "gp"
88 | Generic.Strong: "bold #000000", # class: "gs"
89 | Generic.Subheading: "bold #800080", # class: "gu"
90 | Generic.Traceback: "bold #a40000", # class: "gt"
91 | }
92 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | #
2 | # Flask-PyMongo documentation build configuration file, created by
3 | # sphinx-quickstart on Mon Dec 26 10:16:15 2011.
4 | #
5 | # This file is execfile()d with the current directory set to its containing dir.
6 | #
7 | # Note that not all possible configuration values are present in this
8 | # autogenerated file.
9 | #
10 | # All configuration values have a default; values that are commented out
11 | # serve to show the default.
12 | from __future__ import annotations
13 |
14 | import os
15 | import sys
16 |
17 | sys.path.insert(0, os.path.abspath(".."))
18 |
19 | from flask_pymongo._version import __version__
20 |
21 | # If extensions (or modules to document with autodoc) are in another directory,
22 | # add these directories to sys.path here. If the directory is relative to the
23 | # documentation root, use os.path.abspath to make it absolute, like shown here.
24 | # sys.path.insert(0, os.path.abspath("."))
25 |
26 | # -- General configuration -----------------------------------------------------
27 |
28 | # If your documentation needs a minimal Sphinx version, state it here.
29 | # needs_sphinx = "1.0"
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be extensions
32 | # coming with Sphinx (named "sphinx.ext.*") or your custom ones.
33 | extensions = ["sphinx.ext.intersphinx", "sphinx.ext.autodoc", "sphinx.ext.doctest"]
34 |
35 | # Add any paths that contain templates here, relative to this directory.
36 | templates_path = ["_templates"]
37 |
38 | # The suffix of source filenames.
39 | source_suffix = ".rst"
40 |
41 | # The encoding of source files.
42 | # source_encoding = "utf-8-sig"
43 |
44 | # The master toctree document.
45 | master_doc = "index"
46 |
47 | # General information about the project.
48 | project = "Flask-PyMongo"
49 | copyright = "2011 - 2017, Dan Crosta"
50 |
51 | # The version info for the project you"re documenting, acts as replacement for
52 | # |version| and |release|, also used in various other places throughout the
53 | # built documents.
54 | #
55 | # convert "x.y.z.devN" => "x.y"
56 | version = ".".join(__version__.split(".", 2)[:2])
57 |
58 | # The full version, including alpha/beta/rc tags.
59 | release = __version__
60 |
61 | # The language for content autogenerated by Sphinx. Refer to documentation
62 | # for a list of supported languages.
63 | # language = None
64 |
65 | # There are two options for replacing |today|: either, you set today to some
66 | # non-false value, then it is used:
67 | # today = ""
68 | # Else, today_fmt is used as the format for a strftime call.
69 | # today_fmt = "%B %d, %Y"
70 |
71 | # List of patterns, relative to source directory, that match files and
72 | # directories to ignore when looking for source files.
73 | exclude_patterns = ["_build"]
74 |
75 | # The reST default role (used for this markup: `text`) to use for all documents.
76 | # default_role = None
77 |
78 | # If true, "()" will be appended to :func: etc. cross-reference text.
79 | # add_function_parentheses = True
80 |
81 | # If true, the current module name will be prepended to all description
82 | # unit titles (such as .. function::).
83 | # add_module_names = True
84 |
85 | # If true, sectionauthor and moduleauthor directives will be shown in the
86 | # output. They are ignored by default.
87 | # show_authors = False
88 |
89 | # The name of the Pygments (syntax highlighting) style to use.
90 | pygments_style = "sphinx"
91 |
92 | # A list of ignored prefixes for module index sorting.
93 | # modindex_common_prefix = []
94 |
95 |
96 | # -- Options for HTML output ---------------------------------------------------
97 |
98 | # The theme to use for HTML and HTML Help pages. See the documentation for
99 | # a list of builtin themes.
100 | html_theme = "flask_small"
101 |
102 | # Theme options are theme-specific and customize the look and feel of a theme
103 | # further. For a list of options available for each theme, see the
104 | # documentation.
105 | # html_theme_options = {}
106 |
107 | # Add any paths that contain custom themes here, relative to this directory.
108 | sys.path.append(os.path.abspath("_themes"))
109 | html_theme_path = ["_themes"]
110 |
111 | # custom settings for flask theme
112 | html_theme_options = {
113 | "index_logo": "", # TODO
114 | "github_fork": "dcrosta/flask-pymongo",
115 | }
116 |
117 | # The name for this set of Sphinx documents. If None, it defaults to
118 | # " v documentation".
119 | # html_title = None
120 |
121 | # A shorter title for the navigation bar. Default is the same as html_title.
122 | # html_short_title = None
123 |
124 | # The name of an image file (relative to this directory) to place at the top
125 | # of the sidebar.
126 | # html_logo = None
127 |
128 | # The name of an image file (within the static path) to use as favicon of the
129 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
130 | # pixels large.
131 | # html_favicon = None
132 |
133 | # Add any paths that contain custom static files (such as style sheets) here,
134 | # relative to this directory. They are copied after the builtin static files,
135 | # so a file named "default.css" will overwrite the builtin "default.css".
136 | html_static_path: list[str] = []
137 |
138 | # If not "", a "Last updated on:" timestamp is inserted at every page bottom,
139 | # using the given strftime format.
140 | # html_last_updated_fmt = "%b %d, %Y"
141 |
142 | # If true, SmartyPants will be used to convert quotes and dashes to
143 | # typographically correct entities.
144 | # html_use_smartypants = True
145 |
146 | # Custom sidebar templates, maps document names to template names.
147 | # html_sidebars = {}
148 |
149 | # Additional templates that should be rendered to pages, maps page names to
150 | # template names.
151 | # html_additional_pages = {}
152 |
153 | # If false, no module index is generated.
154 | # html_domain_indices = True
155 |
156 | # If false, no index is generated.
157 | # html_use_index = True
158 |
159 | # If true, the index is split into individual pages for each letter.
160 | # html_split_index = False
161 |
162 | # If true, links to the reST sources are added to the pages.
163 | # html_show_sourcelink = True
164 |
165 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
166 | # html_show_sphinx = True
167 |
168 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
169 | # html_show_copyright = True
170 |
171 | # If true, an OpenSearch description file will be output, and all pages will
172 | # contain a tag referring to it. The value of this option must be the
173 | # base URL from which the finished HTML is served.
174 | # html_use_opensearch = ""
175 |
176 | # This is the file name suffix for HTML files (e.g. ".xhtml").
177 | # html_file_suffix = None
178 |
179 | # Output file base name for HTML help builder.
180 | htmlhelp_basename = "Flask-PyMongodoc"
181 |
182 |
183 | # -- Options for LaTeX output --------------------------------------------------
184 |
185 | # latex_elements = {
186 | # The paper size ("letterpaper" or "a4paper").
187 | # "papersize": "letterpaper",
188 | # The font size ("10pt", "11pt" or "12pt").
189 | # "pointsize": "10pt",
190 | # Additional stuff for the LaTeX preamble.
191 | # "preamble": "",
192 | # }
193 |
194 | # Grouping the document tree into LaTeX files. List of tuples
195 | # (source start file, target name, title, author, documentclass [howto/manual]).
196 | latex_documents = [
197 | (
198 | "index",
199 | "Flask-PyMongo.tex",
200 | "Flask-PyMongo Documentation",
201 | "Dan Crosta",
202 | "manual",
203 | ),
204 | ]
205 |
206 | # The name of an image file (relative to this directory) to place at the top of
207 | # the title page.
208 | # latex_logo = None
209 |
210 | # For "manual" documents, if this is true, then toplevel headings are parts,
211 | # not chapters.
212 | # latex_use_parts = False
213 |
214 | # If true, show page references after internal links.
215 | # latex_show_pagerefs = False
216 |
217 | # If true, show URL addresses after external links.
218 | # latex_show_urls = False
219 |
220 | # Documents to append as an appendix to all manuals.
221 | # latex_appendices = []
222 |
223 | # If false, no module index is generated.
224 | # latex_domain_indices = True
225 |
226 |
227 | # -- Options for manual page output --------------------------------------------
228 |
229 | # One entry per manual page. List of tuples
230 | # (source start file, name, description, authors, manual section).
231 | man_pages = [("index", "flask-pymongo", "Flask-PyMongo Documentation", ["Dan Crosta"], 1)]
232 |
233 | # If true, show URL addresses after external links.
234 | # man_show_urls = False
235 |
236 |
237 | # -- Options for Texinfo output ------------------------------------------------
238 |
239 | # Grouping the document tree into Texinfo files. List of tuples
240 | # (source start file, target name, title, author,
241 | # dir menu entry, description, category)
242 | texinfo_documents = [
243 | (
244 | "index",
245 | "Flask-PyMongo",
246 | "Flask-PyMongo Documentation",
247 | "Dan Crosta",
248 | "Flask-PyMongo",
249 | "One line description of project.",
250 | "Miscellaneous",
251 | ),
252 | ]
253 |
254 | # Documents to append as an appendix to all manuals.
255 | # texinfo_appendices = []
256 |
257 | # If false, no module index is generated.
258 | # texinfo_domain_indices = True
259 |
260 | # How to display URL addresses: "footnote", "no", or "inline".
261 | # texinfo_show_urls = "footnote"
262 |
263 |
264 | # Example configuration for intersphinx: refer to the Python standard library.
265 | intersphinx_mapping = {
266 | "python": ("https://docs.python.org/", None),
267 | "flask": ("https://flask.palletsprojects.com/", None),
268 | "pymongo": ("https://pymongo.readthedocs.io/en/stable/", None),
269 | }
270 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Flask-PyMongo
2 | =============
3 |
4 | `MongoDB `_ is an open source database that stores
5 | flexible JSON-like "documents," which can have any number, name, or
6 | hierarchy of fields within, instead of rows of data as in a relational
7 | database. Python developers can think of MongoDB as a persistent, searchable
8 | repository of Python dictionaries (and, in fact, this is how `PyMongo
9 | `_ represents MongoDB documents).
10 |
11 | Flask-PyMongo bridges Flask and PyMongo and provides some convenience
12 | helpers.
13 |
14 |
15 | Quickstart
16 | ----------
17 |
18 | First, install Flask-PyMongo:
19 |
20 | .. code-block:: bash
21 |
22 | $ pip install Flask-PyMongo
23 |
24 | Next, add a :class:`~flask_pymongo.PyMongo` to your code:
25 |
26 | .. code-block:: python
27 |
28 | from flask import Flask
29 | from flask_pymongo import PyMongo
30 |
31 | app = Flask(__name__)
32 | app.config["MONGO_URI"] = "mongodb://localhost:27017/myDatabase"
33 | mongo = PyMongo(app)
34 |
35 | :class:`~flask_pymongo.PyMongo` connects to the MongoDB server running on
36 | port 27017 on localhost, to the database named ``myDatabase``. This database
37 | is exposed as the :attr:`~flask_pymongo.PyMongo.db` attribute.
38 |
39 | You can use :attr:`~flask_pymongo.PyMongo.db` directly in views:
40 |
41 | .. code-block:: python
42 |
43 | @app.route("/")
44 | def home_page():
45 | online_users = mongo.db.users.find({"online": True})
46 | return render_template("index.html", online_users=online_users)
47 |
48 | .. note::
49 |
50 | Previous versions of Flask-PyMongo required that the MongoDB URI
51 | contained a database name; as of 2.2, this requirement is lifted. If
52 | there is no database name, the :attr:`~flask_pymongo.PyMongo.db`
53 | attribute will be ``None``.
54 |
55 |
56 | Compatibility
57 | -------------
58 |
59 | Flask-PyMongo depends on recent versions of Flask and PyMongo, where "recent"
60 | is defined to mean "was released in the last 3 years". Flask-PyMongo *may*
61 | work with older versions, but compatibility fixes for older versions will
62 | not be accepted, and future changes may break compatibility in older
63 | versions.
64 |
65 | Flask-PyMongo is tested against `supported versions
66 | `_ of MongoDB, and Python
67 | and 3.9+. For the exact list of version combinations that are tested and
68 | known to be compatible.
69 |
70 |
71 | Helpers
72 | -------
73 |
74 | Flask-PyMongo provides helpers for some common tasks:
75 |
76 | .. automethod:: flask_pymongo.wrappers.Collection.find_one_or_404
77 |
78 | .. automethod:: flask_pymongo.PyMongo.send_file
79 |
80 | .. automethod:: flask_pymongo.PyMongo.save_file
81 |
82 | .. autoclass:: flask_pymongo.helpers.BSONObjectIdConverter
83 |
84 | .. autoclass:: flask_pymongo.helpers.BSONProvider
85 |
86 | Configuration
87 | -------------
88 |
89 | You can configure Flask-PyMongo either by passing a `MongoDB URI
90 | `_ to the
91 | :class:`~flask_pymongo.PyMongo` constructor, or assigning it to the
92 | ``MONGO_URI`` `Flask configuration variable
93 | `_
94 |
95 | .. note::
96 |
97 | Flask-PyMongo passes the connection string directly to PyMongo, so all behavior about interpretation of the connection string is determined by PyMongo itself.
98 | See the Connection String examples on the `PyMongo docs `_ for more information.
99 |
100 |
101 | The :class:`~flask_pymongo.PyMongo` instance also accepts these additional
102 | customization options:
103 |
104 | * ``json_options``, a :class:`~bson.json_util.JSONOptions` instance which
105 | controls the JSON serialization of MongoDB objects when used with
106 | :func:`~flask.json.jsonify`.
107 |
108 | You may also pass additional keyword arguments to the ``PyMongo``
109 | constructor. These are passed directly through to the underlying
110 | :class:`~pymongo.mongo_client.MongoClient` object.
111 |
112 | .. note::
113 |
114 | By default, Flask-PyMongo sets the ``connect`` keyword argument to
115 | ``False``, to prevent PyMongo from connecting immediately. PyMongo
116 | itself `is not fork-safe
117 | `_,
118 | and delaying connection until the app is actually used is necessary to
119 | avoid issues. If you wish to change this default behavior, pass
120 | ``connect=True`` as a keyword argument to ``PyMongo``.
121 |
122 | You can create multiple ``PyMongo`` instances, to connect to multiple
123 | databases or database servers:
124 |
125 | .. code-block:: python
126 |
127 | app = Flask(__name__)
128 |
129 | # connect to MongoDB with the defaults
130 | mongo1 = PyMongo(app, uri="mongodb://localhost:27017/databaseOne")
131 |
132 | # connect to another MongoDB database on the same host
133 | mongo2 = PyMongo(app, uri="mongodb://localhost:27017/databaseTwo")
134 |
135 | # connect to another MongoDB server altogether
136 | mongo3 = PyMongo(app, uri="mongodb://another.host:27017/databaseThree")
137 |
138 | Each instance is independent of the others and shares no state.
139 |
140 |
141 | API
142 | ===
143 |
144 | Classes
145 | -------
146 |
147 | .. autoclass:: flask_pymongo.PyMongo
148 | :members:
149 |
150 | .. attribute:: cx
151 |
152 | The :class:`~flask_pymongo.wrappers.MongoClient` connected to the
153 | MongoDB server.
154 |
155 | .. attribute:: db
156 |
157 | The :class:`~flask_pymongo.wrappers.Database` if the URI used
158 | named a database, and ``None`` otherwise.
159 |
160 |
161 | Wrappers
162 | --------
163 |
164 | Flask-PyMongo wraps PyMongo's :class:`~pymongo.mongo_client.MongoClient`,
165 | :class:`~pymongo.database.Database`, and
166 | :class:`~pymongo.collection.Collection` classes, and overrides their
167 | attribute and item accessors. Wrapping the PyMongo classes in this way lets
168 | Flask-PyMongo add methods to ``Collection`` while allowing user code to use
169 | MongoDB-style dotted expressions.
170 |
171 | .. code-block:: pycon
172 |
173 | >>> type(mongo.cx)
174 |
175 | >>> type(mongo.db)
176 |
177 | >>> type(mongo.db.some_collection)
178 |
179 |
180 | .. autoclass:: flask_pymongo.wrappers.Collection(...)
181 | :members:
182 |
183 |
184 |
185 | Troubleshooting
186 | ---------------
187 |
188 | If you have problem like ``TypeError: argument must be an int, or have a fileno() method`` you should run uwsgi with the ``--wsgi-disable-file-wrapper`` flag or add the following entry in your **uwsgi.ini** file:
189 |
190 | .. code-block:: bash
191 |
192 | wsgi-disable-file-wrapper = true
193 |
--------------------------------------------------------------------------------
/examples/wiki/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask-PyMongo
2 | markdown2
3 |
--------------------------------------------------------------------------------
/examples/wiki/templates/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Not Found — Flask PyMongo Wiki
5 |
6 |
7 | Not Found
8 | Sorry about that.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/wiki/templates/edit.html:
--------------------------------------------------------------------------------
1 | {%- if page != None %}
2 | {%- set verb = "Edit" %}
3 | {%- else %}
4 | {%- set verb = "Create" %}
5 | {%- endif %}
6 |
7 |
8 |
9 | {{verb}} {{pagepath|totitle}} — Flask PyMongo Wiki
10 |
11 |
12 | {{verb}} {{pagepath|totitle}}
13 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/wiki/templates/page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{pagepath|totitle}} — Flask PyMongo Wiki
5 |
6 |
7 | {{page.body|wikify|safe}}
8 |
9 | Edit {{pagepath|totitle}}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/wiki/templates/upload.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Upload {{filename}} — Flask PyMongo Wiki
5 |
6 |
7 | Upload {{filename}}
8 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/wiki/wiki.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import re
4 | from typing import TYPE_CHECKING, Any
5 |
6 | import markdown2 # type:ignore[import-untyped]
7 | from flask import Flask, redirect, render_template, request, url_for
8 |
9 | from flask_pymongo import PyMongo
10 |
11 | if TYPE_CHECKING:
12 | from werkzeug.wrappers.response import Response
13 |
14 | app = Flask(__name__)
15 | mongo = PyMongo(app, "mongodb://localhost/wiki")
16 |
17 | WIKIPART = re.compile(r"([A-Z][a-z0-9_]+)")
18 | WIKIWORD = re.compile(r"([A-Z][a-z0-9_]+(?:[A-Z][a-z0-9_]+)+)")
19 |
20 |
21 | @app.route("/", methods=["GET"])
22 | def redirect_to_homepage() -> Response:
23 | return redirect(url_for("show_page", pagepath="HomePage"))
24 |
25 |
26 | @app.template_filter()
27 | def totitle(value: str) -> str:
28 | return " ".join(WIKIPART.findall(value))
29 |
30 |
31 | @app.template_filter()
32 | def wikify(value: str) -> Any:
33 | parts = WIKIWORD.split(value)
34 | for i, part in enumerate(parts):
35 | if WIKIWORD.match(part):
36 | name = totitle(part)
37 | url = url_for("show_page", pagepath=part)
38 | parts[i] = f"[{name}]({url})"
39 | return markdown2.markdown("".join(parts))
40 |
41 |
42 | @app.route("/")
43 | def show_page(pagepath: str) -> str:
44 | assert mongo.db is not None
45 | page: dict[str, Any] = mongo.db.pages.find_one_or_404({"_id": pagepath})
46 | return render_template("page.html", page=page, pagepath=pagepath)
47 |
48 |
49 | @app.route("/edit/", methods=["GET"])
50 | def edit_page(pagepath: str) -> str:
51 | assert mongo.db is not None
52 | page: dict[str, Any] = mongo.db.pages.find_one_or_404({"_id": pagepath})
53 | return render_template("edit.html", page=page, pagepath=pagepath)
54 |
55 |
56 | @app.route("/edit/", methods=["POST"])
57 | def save_page(pagepath: str) -> Response:
58 | if "cancel" not in request.form:
59 | assert mongo.db is not None
60 | mongo.db.pages.update(
61 | {"_id": pagepath},
62 | {"$set": {"body": request.form["body"]}},
63 | w=1,
64 | upsert=True,
65 | )
66 | return redirect(url_for("show_page", pagepath=pagepath))
67 |
68 |
69 | @app.errorhandler(404)
70 | def new_page(error: Any) -> str:
71 | pagepath = request.path.lstrip("/")
72 | if pagepath.startswith("uploads"):
73 | filename = pagepath[len("uploads") :].lstrip("/")
74 | return render_template("upload.html", filename=filename)
75 | return render_template("edit.html", page=None, pagepath=pagepath)
76 |
77 |
78 | @app.route("/uploads/")
79 | def get_upload(filename: str) -> Response:
80 | return mongo.send_file(filename)
81 |
82 |
83 | @app.route("/uploads/", methods=["POST"])
84 | def save_upload(filename: str) -> str | Response:
85 | if request.files.get("file"):
86 | mongo.save_file(filename, request.files["file"])
87 | return redirect(url_for("get_upload", filename=filename))
88 | return render_template("upload.html", filename=filename)
89 |
90 |
91 | if __name__ == "__main__":
92 | app.run(debug=True)
93 |
--------------------------------------------------------------------------------
/flask_pymongo/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2011-2017, Dan Crosta
2 | # All rights reserved.
3 | #
4 | # Redistribution and use in source and binary forms, with or without
5 | # modification, are permitted provided that the following conditions are met:
6 | #
7 | # * Redistributions of source code must retain the above copyright notice,
8 | # this list of conditions and the following disclaimer.
9 | #
10 | # * Redistributions in binary form must reproduce the above copyright notice,
11 | # this list of conditions and the following disclaimer in the documentation
12 | # and/or other materials provided with the distribution.
13 | #
14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | # POSSIBILITY OF SUCH DAMAGE.
25 | from __future__ import annotations
26 |
27 | __all__ = ("PyMongo", "ASCENDING", "DESCENDING", "BSONObjectIdConverter", "BSONProvider")
28 |
29 | import hashlib
30 | import warnings
31 | from mimetypes import guess_type
32 | from typing import Any
33 |
34 | import pymongo
35 | from flask import Flask, Response, abort, current_app, request
36 | from gridfs import GridFS, NoFile
37 | from pymongo import uri_parser
38 | from pymongo.driver_info import DriverInfo
39 | from werkzeug.wsgi import wrap_file
40 |
41 | from flask_pymongo._version import __version__
42 | from flask_pymongo.helpers import BSONObjectIdConverter, BSONProvider
43 | from flask_pymongo.wrappers import Database, MongoClient
44 |
45 | DESCENDING = pymongo.DESCENDING
46 | """Descending sort order."""
47 |
48 | ASCENDING = pymongo.ASCENDING
49 | """Ascending sort order."""
50 |
51 |
52 | class PyMongo:
53 | """Manages MongoDB connections for your Flask app.
54 |
55 | PyMongo objects provide access to the MongoDB server via the :attr:`db`
56 | and :attr:`cx` attributes. You must either pass the :class:`~flask.Flask`
57 | app to the constructor, or call :meth:`init_app`.
58 |
59 | PyMongo accepts a MongoDB URI via the ``MONGO_URI`` Flask configuration
60 | variable, or as an argument to the constructor or ``init_app``. See
61 | :meth:`init_app` for more detail.
62 |
63 | """
64 |
65 | def __init__(
66 | self, app: Flask | None = None, uri: str | None = None, *args: Any, **kwargs: Any
67 | ) -> None:
68 | self.cx: MongoClient | None = None
69 | self.db: Database | None = None
70 |
71 | if app is not None:
72 | self.init_app(app, uri, *args, **kwargs)
73 |
74 | def init_app(self, app: Flask, uri: str | None = None, *args: Any, **kwargs: Any) -> None:
75 | """Initialize this :class:`PyMongo` for use.
76 |
77 | Configure a :class:`~pymongo.mongo_client.MongoClient`
78 | in the following scenarios:
79 |
80 | 1. If ``uri`` is not ``None``, pass the ``uri`` and any positional
81 | or keyword arguments to :class:`~pymongo.mongo_client.MongoClient`
82 | 2. If ``uri`` is ``None``, and a Flask config variable named
83 | ``MONGO_URI`` exists, use that as the ``uri`` as above.
84 |
85 | The caller is responsible for ensuring that additional positional
86 | and keyword arguments result in a valid call.
87 |
88 | .. versionchanged:: 2.2
89 |
90 | The ``uri`` is no longer required to contain a database name. If it
91 | does not, then the :attr:`db` attribute will be ``None``.
92 |
93 | .. versionchanged:: 2.0
94 |
95 | Flask-PyMongo no longer accepts many of the configuration variables
96 | it did in previous versions. You must now use a MongoDB URI to
97 | configure Flask-PyMongo.
98 |
99 | """
100 | if uri is None:
101 | uri = app.config.get("MONGO_URI", None)
102 | if uri is not None:
103 | args = tuple([uri] + list(args))
104 | else:
105 | raise ValueError(
106 | "You must specify a URI or set the MONGO_URI Flask config variable",
107 | )
108 |
109 | parsed_uri = uri_parser.parse_uri(uri)
110 | database_name = parsed_uri["database"]
111 |
112 | # Try to delay connecting, in case the app is loaded before forking, per
113 | # https://www.mongodb.com/docs/languages/python/pymongo-driver/current/connect/mongoclient/#forking-a-process-causes-a-deadlock
114 | kwargs.setdefault("connect", False)
115 | if DriverInfo is not None:
116 | kwargs.setdefault("driver", DriverInfo("Flask-PyMongo", __version__))
117 |
118 | self.cx = MongoClient(*args, **kwargs)
119 | if database_name:
120 | self.db = self.cx[database_name]
121 |
122 | app.url_map.converters["ObjectId"] = BSONObjectIdConverter
123 | app.json = BSONProvider(app)
124 |
125 | # view helpers
126 | def send_file(
127 | self,
128 | filename: str,
129 | base: str = "fs",
130 | version: int = -1,
131 | cache_for: int = 31536000,
132 | db: str | None = None,
133 | ) -> Response:
134 | """Respond with a file from GridFS.
135 |
136 | Returns an instance of the :attr:`~flask.Flask.response_class`
137 | containing the named file, and implement conditional GET semantics
138 | (using :meth:`~werkzeug.wrappers.ETagResponseMixin.make_conditional`).
139 |
140 | .. code-block:: python
141 |
142 | @app.route("/uploads/")
143 | def get_upload(filename):
144 | return mongo.send_file(filename)
145 |
146 | :param str filename: the filename of the file to return
147 | :param str base: the base name of the GridFS collections to use
148 | :param bool version: if positive, return the Nth revision of the file
149 | identified by filename; if negative, return the Nth most recent
150 | revision. If no such version exists, return with HTTP status 404.
151 | :param int cache_for: number of seconds that browsers should be
152 | instructed to cache responses
153 | :param str db: the target database, if different from the default database.
154 | """
155 | if not isinstance(base, str):
156 | raise TypeError("'base' must be string or unicode")
157 | if not isinstance(version, int):
158 | raise TypeError("'version' must be an integer")
159 | if not isinstance(cache_for, int):
160 | raise TypeError("'cache_for' must be an integer")
161 |
162 | if db:
163 | db_obj = self.cx[db]
164 | else:
165 | db_obj = self.db
166 |
167 | assert db_obj is not None, "Please initialize the app before calling send_file!"
168 | storage = GridFS(db_obj, base)
169 |
170 | try:
171 | fileobj = storage.get_version(filename=filename, version=version)
172 | except NoFile:
173 | abort(404)
174 |
175 | # mostly copied from flask/helpers.py, with
176 | # modifications for GridFS
177 | data = wrap_file(request.environ, fileobj, buffer_size=1024 * 255)
178 | content_type, _ = guess_type(filename)
179 | response = current_app.response_class(
180 | data,
181 | mimetype=content_type,
182 | direct_passthrough=True,
183 | )
184 | response.headers["Content-Disposition"] = f"attachment; filename={filename}"
185 | response.content_length = fileobj.length
186 | response.last_modified = fileobj.upload_date
187 |
188 | # GridFS does not manage its own checksum.
189 | # Try to use a sha1 sum that we have added during a save_file.
190 | # Fall back to a legacy md5 sum if it exists.
191 | # Otherwise, compute the sha1 sum directly.
192 | try:
193 | etag = fileobj.sha1
194 | except AttributeError:
195 | with warnings.catch_warnings():
196 | warnings.simplefilter("ignore")
197 | etag = fileobj.md5
198 | if etag is None:
199 | pos = fileobj.tell()
200 | raw_data = fileobj.read()
201 | fileobj.seek(pos)
202 | etag = hashlib.sha1(raw_data).hexdigest()
203 | response.set_etag(etag)
204 |
205 | response.cache_control.max_age = cache_for
206 | response.cache_control.public = True
207 | response.make_conditional(request)
208 | return response
209 |
210 | def save_file(
211 | self,
212 | filename: str,
213 | fileobj: Any,
214 | base: str = "fs",
215 | content_type: str | None = None,
216 | db: str | None = None,
217 | **kwargs: Any,
218 | ) -> Any:
219 | """Save a file-like object to GridFS using the given filename.
220 | Return the "_id" of the created file.
221 |
222 | .. code-block:: python
223 |
224 | @app.route("/uploads/", methods=["POST"])
225 | def save_upload(filename):
226 | mongo.save_file(filename, request.files["file"])
227 | return redirect(url_for("get_upload", filename=filename))
228 |
229 | :param str filename: the filename of the file to return
230 | :param file fileobj: the file-like object to save
231 | :param str base: base the base name of the GridFS collections to use
232 | :param str content_type: the MIME content-type of the file. If
233 | ``None``, the content-type is guessed from the filename using
234 | :func:`~mimetypes.guess_type`
235 | :param str db: the target database, if different from the default database.
236 | :param kwargs: extra attributes to be stored in the file's document,
237 | passed directly to :meth:`gridfs.GridFS.put`
238 | """
239 | if not isinstance(base, str):
240 | raise TypeError("'base' must be string or unicode")
241 | if not (hasattr(fileobj, "read") and callable(fileobj.read)):
242 | raise TypeError("'fileobj' must have read() method")
243 |
244 | if content_type is None:
245 | content_type, _ = guess_type(filename)
246 |
247 | if db:
248 | db_obj = self.cx[db]
249 | else:
250 | db_obj = self.db
251 | assert db_obj is not None, "Please initialize the app before calling save_file!"
252 | storage = GridFS(db_obj, base)
253 |
254 | # GridFS does not manage its own checksum, so we attach a sha1 to the file
255 | # for use as an etag.
256 | hashingfile = _Wrapper(fileobj)
257 | with storage.new_file(filename=filename, content_type=content_type, **kwargs) as grid_file:
258 | grid_file.write(hashingfile)
259 | grid_file.sha1 = hashingfile.hash.hexdigest()
260 | return grid_file._id
261 |
262 |
263 | class _Wrapper:
264 | def __init__(self, file):
265 | self.file = file
266 | self.hash = hashlib.sha1()
267 |
268 | def read(self, n):
269 | data = self.file.read(n)
270 | if data:
271 | self.hash.update(data)
272 | return data
273 |
--------------------------------------------------------------------------------
/flask_pymongo/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "3.1.0.dev0"
2 |
--------------------------------------------------------------------------------
/flask_pymongo/helpers.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2011-2019, Dan Crosta
2 | # All rights reserved.
3 | #
4 | # Redistribution and use in source and binary forms, with or without
5 | # modification, are permitted provided that the following conditions are met:
6 | #
7 | # * Redistributions of source code must retain the above copyright notice,
8 | # this list of conditions and the following disclaimer.
9 | #
10 | # * Redistributions in binary form must reproduce the above copyright notice,
11 | # this list of conditions and the following disclaimer in the documentation
12 | # and/or other materials provided with the distribution.
13 | #
14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | # POSSIBILITY OF SUCH DAMAGE.
25 | from __future__ import annotations
26 |
27 | __all__ = ("BSONObjectIdConverter", "BSONProvider")
28 |
29 | from typing import Any
30 |
31 | from bson import json_util
32 | from bson.errors import InvalidId
33 | from bson.json_util import RELAXED_JSON_OPTIONS
34 | from bson.objectid import ObjectId
35 | from flask import abort
36 | from flask.json.provider import JSONProvider
37 | from werkzeug.routing import BaseConverter
38 |
39 |
40 | def _iteritems(obj: Any) -> Any:
41 | if hasattr(obj, "iteritems"):
42 | return obj.iteritems()
43 | if hasattr(obj, "items"):
44 | return obj.items()
45 | raise TypeError(f"{obj!r} missing iteritems() and items()")
46 |
47 |
48 | class BSONObjectIdConverter(BaseConverter):
49 | """A simple converter for the RESTful URL routing system of Flask.
50 |
51 | .. code-block:: python
52 |
53 | @app.route("/")
54 | def show_task(task_id):
55 | task = mongo.db.tasks.find_one_or_404(task_id)
56 | return render_template("task.html", task=task)
57 |
58 | Valid object ID strings are converted into
59 | :class:`~bson.objectid.ObjectId` objects; invalid strings result
60 | in a 404 error. The converter is automatically registered by the
61 | initialization of :class:`~flask_pymongo.PyMongo` with keyword
62 | :attr:`ObjectId`.
63 |
64 | The :class:`~flask_pymongo.helpers.BSONObjectIdConverter` is
65 | automatically installed on the :class:`~flask_pymongo.PyMongo`
66 | instance at creation time.
67 |
68 | """
69 |
70 | def to_python(self, value: Any) -> ObjectId:
71 | try:
72 | return ObjectId(value)
73 | except InvalidId:
74 | raise abort(404) from None
75 |
76 | def to_url(self, value: Any) -> str:
77 | return str(value)
78 |
79 |
80 | class BSONProvider(JSONProvider):
81 | """A JSON encoder that uses :mod:`bson.json_util` for MongoDB documents.
82 |
83 | .. code-block:: python
84 |
85 | @app.route("/cart/")
86 | def json_route(cart_id):
87 | results = mongo.db.carts.find({"_id": cart_id})
88 | return jsonify(results)
89 |
90 |
91 | # returns a Response with JSON body and application/json content-type:
92 | # '[{"count":12,"item":"egg"},{"count":1,"item":"apple"}]'
93 |
94 | Since this uses PyMongo's JSON tools, certain types may serialize
95 | differently than you expect. See :class:`~bson.json_util.JSONOptions`
96 | for details on the particular serialization that will be used.
97 |
98 | A :class:`~flask_pymongo.helpers.JSONProvider` is automatically
99 | automatically installed on the :class:`~flask_pymongo.PyMongo`
100 | instance at creation time, using
101 | :const:`~bson.json_util.RELAXED_JSON_OPTIONS`.
102 | """
103 |
104 | def __init__(self, app: Any) -> None:
105 | self._default_kwargs = {"json_options": RELAXED_JSON_OPTIONS}
106 |
107 | super().__init__(app)
108 |
109 | def dumps(self, obj: Any, **kwargs: Any) -> str:
110 | """Serialize MongoDB object types using :mod:`bson.json_util`."""
111 | return json_util.dumps(obj)
112 |
113 | def loads(self, s: str | bytes, **kwargs: Any) -> Any:
114 | """Deserialize MongoDB object types using :mod:`bson.json_util`."""
115 | return json_util.loads(s)
116 |
--------------------------------------------------------------------------------
/flask_pymongo/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mongodb-labs/flask-pymongo/6245e8be1f9a7f96488d506659a803f50cecc2a0/flask_pymongo/py.typed
--------------------------------------------------------------------------------
/flask_pymongo/wrappers.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2011-2017, Dan Crosta
2 | # All rights reserved.
3 | #
4 | # Redistribution and use in source and binary forms, with or without
5 | # modification, are permitted provided that the following conditions are met:
6 | #
7 | # * Redistributions of source code must retain the above copyright notice,
8 | # this list of conditions and the following disclaimer.
9 | #
10 | # * Redistributions in binary form must reproduce the above copyright notice,
11 | # this list of conditions and the following disclaimer in the documentation
12 | # and/or other materials provided with the distribution.
13 | #
14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | # POSSIBILITY OF SUCH DAMAGE.
25 | from __future__ import annotations
26 |
27 | from typing import Any
28 |
29 | from flask import abort
30 | from pymongo import collection, database, mongo_client
31 |
32 |
33 | class MongoClient(mongo_client.MongoClient[dict[str, Any]]):
34 | """Wrapper for :class:`~pymongo.mongo_client.MongoClient`.
35 |
36 | Returns instances of Flask-PyMongo
37 | :class:`~flask_pymongo.wrappers.Database` instead of native PyMongo
38 | :class:`~pymongo.database.Database` when accessed with dot notation.
39 |
40 | """
41 |
42 | def __getattr__(self, name: str) -> Any:
43 | attr = super().__getattr__(name)
44 | if isinstance(attr, database.Database):
45 | return Database(self, name)
46 | return attr
47 |
48 | def __getitem__(self, name: str) -> Any:
49 | attr = super().__getitem__(name)
50 | if isinstance(attr, database.Database):
51 | return Database(self, name)
52 | return attr
53 |
54 |
55 | class Database(database.Database[dict[str, Any]]):
56 | """Wrapper for :class:`~pymongo.database.Database`.
57 |
58 | Returns instances of Flask-PyMongo
59 | :class:`~flask_pymongo.wrappers.Collection` instead of native PyMongo
60 | :class:`~pymongo.collection.Collection` when accessed with dot notation.
61 |
62 | """
63 |
64 | def __getattr__(self, name: str) -> Any:
65 | attr = super().__getattr__(name)
66 | if isinstance(attr, collection.Collection):
67 | return Collection(self, name)
68 | return attr
69 |
70 | def __getitem__(self, name: str) -> Any:
71 | item_ = super().__getitem__(name)
72 | if isinstance(item_, collection.Collection):
73 | return Collection(self, name)
74 | return item_
75 |
76 |
77 | class Collection(collection.Collection[dict[str, Any]]):
78 | """Sub-class of PyMongo :class:`~pymongo.collection.Collection` with helpers."""
79 |
80 | def __getattr__(self, name: str) -> Any:
81 | attr = super().__getattr__(name)
82 | if isinstance(attr, collection.Collection):
83 | db = self._Collection__database
84 | return Collection(db, attr.name)
85 | return attr
86 |
87 | def __getitem__(self, name: str) -> Any:
88 | item = super().__getitem__(name)
89 | if isinstance(item, collection.Collection):
90 | db = self._Collection__database
91 | return Collection(db, item.name)
92 | return item
93 |
94 | def find_one_or_404(self, *args: Any, **kwargs: Any) -> Any:
95 | """Find a single document or raise a 404.
96 |
97 | This is like :meth:`~pymongo.collection.Collection.find_one`, but
98 | rather than returning ``None``, cause a 404 Not Found HTTP status
99 | on the request.
100 |
101 | .. code-block:: python
102 |
103 | @app.route("/user/")
104 | def user_profile(username):
105 | user = mongo.db.users.find_one_or_404({"_id": username})
106 | return render_template("user.html", user=user)
107 |
108 | """
109 | found = self.find_one(*args, **kwargs)
110 | if found is None:
111 | abort(404)
112 | return found
113 |
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | docs_build := "docs/_build"
2 | sphinx_opts:= "-d " + docs_build + "/doctrees docs"
3 |
4 | # Default target executed when no arguments are given.
5 | [private]
6 | default:
7 | @just --list
8 |
9 | install:
10 | uv sync
11 | uv run pre-commit install
12 |
13 | test *args:
14 | uv run pytest {{args}}
15 |
16 | lint:
17 | uv run pre-commit run --hook-stage manual --all-files
18 |
19 | docs:
20 | uv run sphinx-build -T -b html {{sphinx_opts}} {{docs_build}}
21 |
22 | doctest:
23 | uv run python -m doctest -v examples/wiki/wiki.py
24 | uv run sphinx-build -E -b doctest {{sphinx_opts}} {{docs_build}}/doctest
25 | uv run sphinx-build -b linkcheck {{sphinx_opts}} {{docs_build}}/linkcheck
26 |
27 | typing:
28 | uv run mypy --install-types --non-interactive .
29 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["hatchling"]
3 | build-backend = "hatchling.build"
4 |
5 | [project]
6 | name = "Flask-PyMongo"
7 | dynamic = ["version"]
8 | description = "PyMongo support for Flask applications"
9 | readme = "README.md"
10 | license = { file="LICENSE" }
11 | requires-python = ">=3.9"
12 | authors = [
13 | { name = "Dan Crosta", email = "dcrosta@late.am" },
14 | ]
15 | classifiers = [
16 | "Environment :: Web Environment",
17 | "Framework :: Flask",
18 | "Intended Audience :: Developers",
19 | "License :: OSI Approved :: BSD License",
20 | "Operating System :: OS Independent",
21 | "Programming Language :: Python :: 3.9",
22 | "Programming Language :: Python :: Implementation :: CPython",
23 | "Programming Language :: Python :: Implementation :: PyPy",
24 | "Programming Language :: Python :: 3.10",
25 | "Programming Language :: Python :: 3.11",
26 | "Programming Language :: Python :: 3.12",
27 | "Programming Language :: Python :: 3.13",
28 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
29 | "Topic :: Software Development :: Libraries :: Python Modules",
30 | ]
31 | dependencies = [
32 | "Flask>=3.0",
33 | "PyMongo>=4.0",
34 | ]
35 |
36 | [project.urls]
37 | Download = "https://github.com/mongodb-labs/flask-pymongo/tags"
38 | Homepage = "http://flask-pymongo.readthedocs.org/"
39 |
40 | [dependency-groups]
41 | dev = [
42 | "markdown2>=2.5.2",
43 | "mypy>=1.14.1",
44 | "pre-commit>=4.0.1",
45 | "pytest>=8.3.4",
46 | "sphinx>=7.4.7",
47 | ]
48 |
49 | [tool.hatch.version]
50 | path = "flask_pymongo/_version.py"
51 |
52 | [tool.hatch.build.targets.sdist]
53 | include = [
54 | "/flask_pymongo",
55 | ]
56 |
57 | [tool.pytest.ini_options]
58 | minversion = "8.0"
59 | addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
60 | xfail_strict = true
61 | filterwarnings = ["error"]
62 | log_cli_level = "info"
63 | testpaths = [
64 | "tests",
65 | ]
66 |
67 | [tool.mypy]
68 | python_version = "3.9"
69 | strict = true
70 | enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
71 |
72 | [[tool.mypy.overrides]]
73 | module = ["tests.*"]
74 | disable_error_code = ["no-untyped-def", "no-untyped-call"]
75 |
76 | [tool.ruff]
77 | line-length = 100
78 |
79 | [tool.ruff.lint]
80 | select = [
81 | "E", # pycodestyle
82 | "F", # Pyflakes
83 | "UP", # pyupgrade
84 | "B", # flake8-bugbear
85 | "I", # isort
86 | ]
87 | unfixable = [
88 | "RUF100", # Unused noqa
89 | "T20", # Removes print statements
90 | "F401", # Unused imports
91 | ]
92 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mongodb-labs/flask-pymongo/6245e8be1f9a7f96488d506659a803f50cecc2a0/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_config.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import time
4 | from contextlib import contextmanager
5 | from typing import Any
6 |
7 | import pymongo
8 | import pytest
9 |
10 | import flask_pymongo
11 |
12 | from .util import FlaskRequestTest
13 |
14 |
15 | class CouldNotConnect(Exception):
16 | pass
17 |
18 |
19 | @contextmanager
20 | def doesnt_raise(exc=BaseException):
21 | try:
22 | yield
23 | except exc:
24 | pytest.fail(f"{exc} was raised but should not have been")
25 |
26 |
27 | class FlaskPyMongoConfigTest(FlaskRequestTest):
28 | def setUp(self):
29 | super().setUp()
30 |
31 | conn: pymongo.MongoClient[Any] = pymongo.MongoClient(port=self.port)
32 | conn.test.command("ping") # wait for server
33 | conn.close()
34 |
35 | def tearDown(self):
36 | super().tearDown()
37 |
38 | conn: pymongo.MongoClient[Any] = pymongo.MongoClient(port=self.port)
39 |
40 | conn.drop_database(self.dbname)
41 | conn.drop_database(self.dbname + "2")
42 | conn.close()
43 |
44 | def test_config_with_uri_in_flask_conf_var(self):
45 | uri = f"mongodb://localhost:{self.port}/{self.dbname}"
46 | self.app.config["MONGO_URI"] = uri
47 |
48 | mongo = flask_pymongo.PyMongo(self.app, connect=True)
49 |
50 | _wait_until_connected(mongo)
51 | assert mongo.cx is not None
52 | self.addCleanup(mongo.cx.close)
53 | assert mongo.db is not None
54 | assert mongo.db.name == self.dbname
55 | assert ("localhost", self.port) == mongo.cx.address or (
56 | "127.0.0.1",
57 | self.port,
58 | ) == mongo.cx.address
59 |
60 | def test_config_with_uri_passed_directly(self):
61 | uri = f"mongodb://localhost:{self.port}/{self.dbname}"
62 |
63 | mongo = flask_pymongo.PyMongo(self.app, uri, connect=True)
64 |
65 | _wait_until_connected(mongo)
66 | assert mongo.cx is not None
67 | self.addCleanup(mongo.cx.close)
68 | assert mongo.db is not None
69 | assert mongo.db.name == self.dbname
70 | assert ("localhost", self.port) == mongo.cx.address or (
71 | "127.0.0.1",
72 | self.port,
73 | ) == mongo.cx.address
74 |
75 | def test_it_fails_with_no_uri(self):
76 | self.app.config.pop("MONGO_URI", None)
77 |
78 | with pytest.raises(ValueError):
79 | flask_pymongo.PyMongo(self.app)
80 |
81 | def test_multiple_pymongos(self):
82 | uri1 = f"mongodb://localhost:{self.port}/{self.dbname}"
83 | uri2 = "mongodb://localhost:{}/{}".format(self.port, self.dbname + "2")
84 |
85 | mongo1 = flask_pymongo.PyMongo(self.app, uri1) # noqa: F841 unused variable
86 | mongo2 = flask_pymongo.PyMongo(self.app, uri2) # noqa: F841 unused variable
87 |
88 | # this test passes if it raises no exceptions
89 |
90 | def test_custom_document_class(self):
91 | class CustomDict(dict[str, Any]):
92 | pass
93 |
94 | uri = f"mongodb://localhost:{self.port}/{self.dbname}"
95 | mongo = flask_pymongo.PyMongo(self.app, uri, document_class=CustomDict)
96 | assert mongo.cx is not None
97 | self.addCleanup(mongo.cx.close)
98 | assert mongo.db is not None
99 | assert mongo.db.things.find_one() is None, "precondition failed"
100 |
101 | mongo.db.things.insert_one({"_id": "thing", "val": "foo"})
102 |
103 | assert type(mongo.db.things.find_one()) is CustomDict
104 |
105 | def test_it_doesnt_connect_by_default(self):
106 | uri = f"mongodb://localhost:{self.port}/{self.dbname}"
107 |
108 | mongo = flask_pymongo.PyMongo(self.app, uri)
109 |
110 | with pytest.raises(CouldNotConnect):
111 | _wait_until_connected(mongo, timeout=0.2)
112 |
113 | def test_it_doesnt_require_db_name_in_uri(self):
114 | uri = f"mongodb://localhost:{self.port}"
115 |
116 | with doesnt_raise(Exception):
117 | mongo = flask_pymongo.PyMongo(self.app, uri)
118 |
119 | assert mongo.db is None
120 |
121 |
122 | def _wait_until_connected(mongo, timeout=1.0):
123 | start = time.time()
124 | while time.time() < (start + timeout):
125 | if mongo.cx.nodes:
126 | return
127 | time.sleep(0.05)
128 | raise CouldNotConnect(f"could not prove mongodb connected in {timeout} seconds")
129 |
--------------------------------------------------------------------------------
/tests/test_gridfs.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import warnings
4 | from hashlib import md5, sha1
5 | from io import BytesIO
6 |
7 | import pytest
8 | from bson.objectid import ObjectId
9 | from gridfs import GridFS
10 | from werkzeug.exceptions import NotFound
11 |
12 | from .util import FlaskPyMongoTest
13 |
14 |
15 | class GridFSCleanupMixin:
16 | def tearDown(self):
17 | gridfs = GridFS(self.mongo.db) # type:ignore[attr-defined]
18 | files = list(gridfs.find())
19 | for gridfile in files:
20 | gridfs.delete(gridfile._id)
21 |
22 | super().tearDown() # type:ignore[misc]
23 |
24 |
25 | class TestSaveFile(GridFSCleanupMixin, FlaskPyMongoTest):
26 | def test_it_saves_files(self):
27 | fileobj = BytesIO(b"these are the bytes")
28 |
29 | self.mongo.save_file("my-file", fileobj)
30 | assert self.mongo.db is not None
31 | gridfs = GridFS(self.mongo.db)
32 | assert gridfs.exists({"filename": "my-file"})
33 |
34 | def test_it_saves_files_to_another_db(self):
35 | fileobj = BytesIO(b"these are the bytes")
36 |
37 | self.mongo.save_file("my-file", fileobj, db="other")
38 | assert self.mongo.db is not None
39 | gridfs = GridFS(self.mongo.cx["other"])
40 | assert gridfs.exists({"filename": "my-file"})
41 |
42 | def test_it_saves_files_with_props(self):
43 | fileobj = BytesIO(b"these are the bytes")
44 |
45 | self.mongo.save_file("my-file", fileobj, foo="bar")
46 |
47 | assert self.mongo.db is not None
48 | gridfs = GridFS(self.mongo.db)
49 | gridfile = gridfs.find_one({"filename": "my-file"})
50 | assert gridfile is not None
51 | assert gridfile.foo == "bar"
52 |
53 | def test_it_returns_id(self):
54 | fileobj = BytesIO(b"these are the bytes")
55 |
56 | _id = self.mongo.save_file("my-file", fileobj, foo="bar")
57 |
58 | assert type(_id) is ObjectId
59 |
60 |
61 | class TestSendFile(GridFSCleanupMixin, FlaskPyMongoTest):
62 | def setUp(self):
63 | super().setUp()
64 |
65 | # make it bigger than 1 gridfs chunk
66 | self.myfile = BytesIO(b"a" * 500 * 1024)
67 | self.mongo.save_file("myfile.txt", self.myfile)
68 | self.mongo.save_file("my_other_file.txt", self.myfile, db="other")
69 |
70 | def test_it_404s_for_missing_files(self):
71 | with pytest.raises(NotFound):
72 | self.mongo.send_file("no-such-file.txt")
73 |
74 | def test_it_sets_content_type(self):
75 | resp = self.mongo.send_file("myfile.txt")
76 | assert resp.content_type.startswith("text/plain")
77 |
78 | def test_it_sends_file_to_another_db(self):
79 | resp = self.mongo.send_file("my_other_file.txt", db="other")
80 | assert resp.content_type.startswith("text/plain")
81 |
82 | def test_it_sets_content_length(self):
83 | resp = self.mongo.send_file("myfile.txt")
84 | assert resp.content_length == len(self.myfile.getvalue())
85 |
86 | def test_it_sets_supports_conditional_gets(self):
87 | # a basic conditional GET
88 | environ_args = {
89 | "method": "GET",
90 | "headers": {
91 | "If-None-Match": sha1(self.myfile.getvalue()).hexdigest(),
92 | },
93 | }
94 |
95 | with self.app.test_request_context(**environ_args):
96 | resp = self.mongo.send_file("myfile.txt")
97 | assert resp.status_code == 304
98 |
99 | def test_it_sets_supports_conditional_gets_md5(self):
100 | # a basic conditional GET
101 | md5_hash = md5(self.myfile.getvalue()).hexdigest()
102 | environ_args = {
103 | "method": "GET",
104 | "headers": {
105 | "If-None-Match": md5_hash,
106 | },
107 | }
108 | storage = storage = GridFS(self.mongo.db)
109 | with storage.new_file(filename="myfile.txt") as grid_file:
110 | grid_file.write(self.myfile.getvalue())
111 | with warnings.catch_warnings():
112 | warnings.simplefilter("ignore")
113 | grid_file.set("md5", md5_hash)
114 |
115 | with self.app.test_request_context(**environ_args):
116 | resp = self.mongo.send_file("myfile.txt")
117 | assert resp.status_code == 304
118 |
119 | def test_it_sets_cache_headers(self):
120 | resp = self.mongo.send_file("myfile.txt", cache_for=60)
121 | assert resp.cache_control.max_age == 60
122 | assert resp.cache_control.public is True
123 |
124 | def test_it_streams_results(self):
125 | resp = self.mongo.send_file("myfile.txt")
126 | assert resp.is_streamed
127 |
--------------------------------------------------------------------------------
/tests/test_json.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import json
4 |
5 | from bson import ObjectId
6 | from flask import jsonify
7 |
8 | from .util import FlaskPyMongoTest
9 |
10 |
11 | class JSONTest(FlaskPyMongoTest):
12 | def test_it_encodes_json(self):
13 | resp = jsonify({"foo": "bar"})
14 | dumped = json.loads(resp.get_data().decode("utf-8"))
15 | self.assertEqual(dumped, {"foo": "bar"})
16 |
17 | def test_it_handles_pymongo_types(self):
18 | resp = jsonify({"id": ObjectId("5cf29abb5167a14c9e6e12c4")})
19 | dumped = json.loads(resp.get_data().decode("utf-8"))
20 | self.assertEqual(dumped, {"id": {"$oid": "5cf29abb5167a14c9e6e12c4"}})
21 |
22 | def test_it_jsonifies_a_cursor(self):
23 | assert self.mongo.db is not None
24 | self.mongo.db.rows.insert_many([{"foo": "bar"}, {"foo": "baz"}])
25 | curs = self.mongo.db.rows.find(projection={"_id": False}).sort("foo")
26 |
27 | resp = jsonify(curs)
28 | dumped = json.loads(resp.get_data().decode("utf-8"))
29 | self.assertEqual([{"foo": "bar"}, {"foo": "baz"}], dumped)
30 |
--------------------------------------------------------------------------------
/tests/test_url_converter.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from bson import ObjectId
4 | from werkzeug.exceptions import NotFound
5 | from werkzeug.routing.map import Map
6 |
7 | from flask_pymongo import BSONObjectIdConverter
8 |
9 | from .util import FlaskPyMongoTest
10 |
11 |
12 | class UrlConverterTest(FlaskPyMongoTest):
13 | def test_bson_object_id_converter(self):
14 | converter = BSONObjectIdConverter(Map())
15 |
16 | self.assertRaises(NotFound, converter.to_python, ("132"))
17 | assert converter.to_python("4e4ac5cfffc84958fa1f45fb") == ObjectId(
18 | "4e4ac5cfffc84958fa1f45fb"
19 | )
20 | assert converter.to_url(ObjectId("4e4ac5cfffc84958fa1f45fb")) == "4e4ac5cfffc84958fa1f45fb"
21 |
--------------------------------------------------------------------------------
/tests/test_wrappers.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import Any
4 |
5 | from werkzeug.exceptions import HTTPException
6 |
7 | from .util import FlaskPyMongoTest
8 |
9 |
10 | class CollectionTest(FlaskPyMongoTest):
11 | def test_find_one_or_404(self):
12 | assert self.mongo.db is not None
13 | self.mongo.db.things.delete_many({})
14 |
15 | try:
16 | self.mongo.db.things.find_one_or_404({"_id": "thing"})
17 | except HTTPException as notfound:
18 | assert notfound.code == 404, "raised wrong exception"
19 |
20 | self.mongo.db.things.insert_one({"_id": "thing", "val": "foo"})
21 |
22 | # now it should not raise
23 | thing: dict[str, Any] = self.mongo.db.things.find_one_or_404({"_id": "thing"})
24 | assert thing["val"] == "foo"
25 |
--------------------------------------------------------------------------------
/tests/util.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import unittest
4 |
5 | import flask
6 |
7 | import flask_pymongo
8 |
9 |
10 | class FlaskRequestTest(unittest.TestCase):
11 | def setUp(self):
12 | super().setUp()
13 |
14 | self.dbname = self.__class__.__name__
15 | self.app = flask.Flask("test")
16 | self.context = self.app.test_request_context("/")
17 | self.context.push()
18 | self.port = 27017
19 |
20 | def tearDown(self):
21 | super().tearDown()
22 |
23 | self.context.pop()
24 |
25 |
26 | class FlaskPyMongoTest(FlaskRequestTest):
27 | def setUp(self):
28 | super().setUp()
29 |
30 | uri = f"mongodb://localhost:{self.port}/{self.dbname}"
31 | self.mongo = flask_pymongo.PyMongo(self.app, uri)
32 |
33 | def tearDown(self):
34 | assert self.mongo.cx is not None
35 | self.mongo.cx.drop_database(self.dbname)
36 | self.mongo.cx.close()
37 | super().tearDown()
38 |
--------------------------------------------------------------------------------
/uv.lock:
--------------------------------------------------------------------------------
1 | version = 1
2 | requires-python = ">=3.9"
3 | resolution-markers = [
4 | "python_full_version >= '3.10'",
5 | "python_full_version < '3.10'",
6 | ]
7 |
8 | [[package]]
9 | name = "alabaster"
10 | version = "0.7.16"
11 | source = { registry = "https://pypi.org/simple" }
12 | resolution-markers = [
13 | "python_full_version < '3.10'",
14 | ]
15 | sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776 }
16 | wheels = [
17 | { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511 },
18 | ]
19 |
20 | [[package]]
21 | name = "alabaster"
22 | version = "1.0.0"
23 | source = { registry = "https://pypi.org/simple" }
24 | resolution-markers = [
25 | "python_full_version >= '3.10'",
26 | ]
27 | sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 }
28 | wheels = [
29 | { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 },
30 | ]
31 |
32 | [[package]]
33 | name = "babel"
34 | version = "2.16.0"
35 | source = { registry = "https://pypi.org/simple" }
36 | sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 }
37 | wheels = [
38 | { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 },
39 | ]
40 |
41 | [[package]]
42 | name = "blinker"
43 | version = "1.9.0"
44 | source = { registry = "https://pypi.org/simple" }
45 | sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 }
46 | wheels = [
47 | { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 },
48 | ]
49 |
50 | [[package]]
51 | name = "certifi"
52 | version = "2024.12.14"
53 | source = { registry = "https://pypi.org/simple" }
54 | sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 }
55 | wheels = [
56 | { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 },
57 | ]
58 |
59 | [[package]]
60 | name = "cfgv"
61 | version = "3.4.0"
62 | source = { registry = "https://pypi.org/simple" }
63 | sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
64 | wheels = [
65 | { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
66 | ]
67 |
68 | [[package]]
69 | name = "charset-normalizer"
70 | version = "3.4.1"
71 | source = { registry = "https://pypi.org/simple" }
72 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
73 | wheels = [
74 | { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 },
75 | { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 },
76 | { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 },
77 | { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 },
78 | { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 },
79 | { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 },
80 | { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 },
81 | { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 },
82 | { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 },
83 | { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 },
84 | { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 },
85 | { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 },
86 | { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 },
87 | { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 },
88 | { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 },
89 | { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 },
90 | { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 },
91 | { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 },
92 | { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 },
93 | { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 },
94 | { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 },
95 | { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 },
96 | { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 },
97 | { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 },
98 | { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 },
99 | { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 },
100 | { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
101 | { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
102 | { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
103 | { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
104 | { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
105 | { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
106 | { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
107 | { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
108 | { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
109 | { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
110 | { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
111 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
112 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
113 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
114 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
115 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
116 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
117 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
118 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
119 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
120 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
121 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
122 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
123 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
124 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
125 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
126 | { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 },
127 | { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 },
128 | { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 },
129 | { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 },
130 | { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 },
131 | { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 },
132 | { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 },
133 | { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 },
134 | { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 },
135 | { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 },
136 | { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 },
137 | { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 },
138 | { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 },
139 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
140 | ]
141 |
142 | [[package]]
143 | name = "click"
144 | version = "8.1.8"
145 | source = { registry = "https://pypi.org/simple" }
146 | dependencies = [
147 | { name = "colorama", marker = "sys_platform == 'win32'" },
148 | ]
149 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
150 | wheels = [
151 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
152 | ]
153 |
154 | [[package]]
155 | name = "colorama"
156 | version = "0.4.6"
157 | source = { registry = "https://pypi.org/simple" }
158 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
159 | wheels = [
160 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
161 | ]
162 |
163 | [[package]]
164 | name = "distlib"
165 | version = "0.3.9"
166 | source = { registry = "https://pypi.org/simple" }
167 | sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 }
168 | wheels = [
169 | { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 },
170 | ]
171 |
172 | [[package]]
173 | name = "dnspython"
174 | version = "2.7.0"
175 | source = { registry = "https://pypi.org/simple" }
176 | sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 }
177 | wheels = [
178 | { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 },
179 | ]
180 |
181 | [[package]]
182 | name = "docutils"
183 | version = "0.21.2"
184 | source = { registry = "https://pypi.org/simple" }
185 | sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 }
186 | wheels = [
187 | { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 },
188 | ]
189 |
190 | [[package]]
191 | name = "exceptiongroup"
192 | version = "1.2.2"
193 | source = { registry = "https://pypi.org/simple" }
194 | sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
195 | wheels = [
196 | { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
197 | ]
198 |
199 | [[package]]
200 | name = "filelock"
201 | version = "3.16.1"
202 | source = { registry = "https://pypi.org/simple" }
203 | sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 }
204 | wheels = [
205 | { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 },
206 | ]
207 |
208 | [[package]]
209 | name = "flask"
210 | version = "3.1.0"
211 | source = { registry = "https://pypi.org/simple" }
212 | dependencies = [
213 | { name = "blinker" },
214 | { name = "click" },
215 | { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
216 | { name = "itsdangerous" },
217 | { name = "jinja2" },
218 | { name = "werkzeug" },
219 | ]
220 | sdist = { url = "https://files.pythonhosted.org/packages/89/50/dff6380f1c7f84135484e176e0cac8690af72fa90e932ad2a0a60e28c69b/flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", size = 680824 }
221 | wheels = [
222 | { url = "https://files.pythonhosted.org/packages/af/47/93213ee66ef8fae3b93b3e29206f6b251e65c97bd91d8e1c5596ef15af0a/flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136", size = 102979 },
223 | ]
224 |
225 | [[package]]
226 | name = "flask-pymongo"
227 | version = "3.0.0.dev0"
228 | source = { editable = "." }
229 | dependencies = [
230 | { name = "flask" },
231 | { name = "pymongo" },
232 | ]
233 |
234 | [package.dev-dependencies]
235 | dev = [
236 | { name = "markdown2" },
237 | { name = "mypy" },
238 | { name = "pre-commit" },
239 | { name = "pytest" },
240 | { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
241 | { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
242 | ]
243 |
244 | [package.metadata]
245 | requires-dist = [
246 | { name = "flask", specifier = ">=3.0" },
247 | { name = "pymongo", specifier = ">=4.0" },
248 | ]
249 |
250 | [package.metadata.requires-dev]
251 | dev = [
252 | { name = "markdown2", specifier = ">=2.5.2" },
253 | { name = "mypy", specifier = ">=1.14.1" },
254 | { name = "pre-commit", specifier = ">=4.0.1" },
255 | { name = "pytest", specifier = ">=8.3.4" },
256 | { name = "sphinx", specifier = ">=7.4.7" },
257 | ]
258 |
259 | [[package]]
260 | name = "identify"
261 | version = "2.6.5"
262 | source = { registry = "https://pypi.org/simple" }
263 | sdist = { url = "https://files.pythonhosted.org/packages/cf/92/69934b9ef3c31ca2470980423fda3d00f0460ddefdf30a67adf7f17e2e00/identify-2.6.5.tar.gz", hash = "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc", size = 99213 }
264 | wheels = [
265 | { url = "https://files.pythonhosted.org/packages/ec/fa/dce098f4cdf7621aa8f7b4f919ce545891f489482f0bfa5102f3eca8608b/identify-2.6.5-py2.py3-none-any.whl", hash = "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566", size = 99078 },
266 | ]
267 |
268 | [[package]]
269 | name = "idna"
270 | version = "3.10"
271 | source = { registry = "https://pypi.org/simple" }
272 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
273 | wheels = [
274 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
275 | ]
276 |
277 | [[package]]
278 | name = "imagesize"
279 | version = "1.4.1"
280 | source = { registry = "https://pypi.org/simple" }
281 | sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 }
282 | wheels = [
283 | { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 },
284 | ]
285 |
286 | [[package]]
287 | name = "importlib-metadata"
288 | version = "8.5.0"
289 | source = { registry = "https://pypi.org/simple" }
290 | dependencies = [
291 | { name = "zipp", marker = "python_full_version < '3.10'" },
292 | ]
293 | sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 }
294 | wheels = [
295 | { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 },
296 | ]
297 |
298 | [[package]]
299 | name = "iniconfig"
300 | version = "2.0.0"
301 | source = { registry = "https://pypi.org/simple" }
302 | sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
303 | wheels = [
304 | { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
305 | ]
306 |
307 | [[package]]
308 | name = "itsdangerous"
309 | version = "2.2.0"
310 | source = { registry = "https://pypi.org/simple" }
311 | sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 }
312 | wheels = [
313 | { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 },
314 | ]
315 |
316 | [[package]]
317 | name = "jinja2"
318 | version = "3.1.5"
319 | source = { registry = "https://pypi.org/simple" }
320 | dependencies = [
321 | { name = "markupsafe" },
322 | ]
323 | sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 }
324 | wheels = [
325 | { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
326 | ]
327 |
328 | [[package]]
329 | name = "markdown2"
330 | version = "2.5.2"
331 | source = { registry = "https://pypi.org/simple" }
332 | sdist = { url = "https://files.pythonhosted.org/packages/a0/61/d3c0c21280ba1fc348822a4410847cf78f99bba8625755a5062a44d2e228/markdown2-2.5.2.tar.gz", hash = "sha256:3ac02226a901c4b2f6fc21dbd17c26d118d2c25bcbb28cee093a1f8b5c46f3f1", size = 141583 }
333 | wheels = [
334 | { url = "https://files.pythonhosted.org/packages/68/53/baf1f8bd93a9cd5a1b6116041f17dba742bef57cbddb0484f792753c3a2e/markdown2-2.5.2-py3-none-any.whl", hash = "sha256:bed80d301a33845be633acde47a67cf265c57ddf9cbe3cb11c49c18016c2f581", size = 48517 },
335 | ]
336 |
337 | [[package]]
338 | name = "markupsafe"
339 | version = "3.0.2"
340 | source = { registry = "https://pypi.org/simple" }
341 | sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
342 | wheels = [
343 | { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 },
344 | { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 },
345 | { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 },
346 | { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 },
347 | { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 },
348 | { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 },
349 | { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 },
350 | { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 },
351 | { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 },
352 | { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 },
353 | { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 },
354 | { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 },
355 | { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 },
356 | { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 },
357 | { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 },
358 | { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 },
359 | { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 },
360 | { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 },
361 | { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 },
362 | { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 },
363 | { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
364 | { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
365 | { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
366 | { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
367 | { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
368 | { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
369 | { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
370 | { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
371 | { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
372 | { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
373 | { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
374 | { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
375 | { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
376 | { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
377 | { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
378 | { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
379 | { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
380 | { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
381 | { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
382 | { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
383 | { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
384 | { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
385 | { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
386 | { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
387 | { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
388 | { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
389 | { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
390 | { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
391 | { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
392 | { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
393 | { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344 },
394 | { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389 },
395 | { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607 },
396 | { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728 },
397 | { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826 },
398 | { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843 },
399 | { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219 },
400 | { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946 },
401 | { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063 },
402 | { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506 },
403 | ]
404 |
405 | [[package]]
406 | name = "mypy"
407 | version = "1.14.1"
408 | source = { registry = "https://pypi.org/simple" }
409 | dependencies = [
410 | { name = "mypy-extensions" },
411 | { name = "tomli", marker = "python_full_version < '3.11'" },
412 | { name = "typing-extensions" },
413 | ]
414 | sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 }
415 | wheels = [
416 | { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002 },
417 | { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400 },
418 | { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172 },
419 | { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732 },
420 | { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197 },
421 | { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836 },
422 | { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432 },
423 | { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515 },
424 | { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791 },
425 | { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203 },
426 | { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900 },
427 | { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869 },
428 | { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 },
429 | { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 },
430 | { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 },
431 | { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 },
432 | { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 },
433 | { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 },
434 | { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 },
435 | { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 },
436 | { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 },
437 | { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 },
438 | { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 },
439 | { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 },
440 | { url = "https://files.pythonhosted.org/packages/ca/1f/186d133ae2514633f8558e78cd658070ba686c0e9275c5a5c24a1e1f0d67/mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", size = 11200493 },
441 | { url = "https://files.pythonhosted.org/packages/af/fc/4842485d034e38a4646cccd1369f6b1ccd7bc86989c52770d75d719a9941/mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", size = 10357702 },
442 | { url = "https://files.pythonhosted.org/packages/b4/e6/457b83f2d701e23869cfec013a48a12638f75b9d37612a9ddf99072c1051/mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", size = 12091104 },
443 | { url = "https://files.pythonhosted.org/packages/f1/bf/76a569158db678fee59f4fd30b8e7a0d75bcbaeef49edd882a0d63af6d66/mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", size = 12830167 },
444 | { url = "https://files.pythonhosted.org/packages/43/bc/0bc6b694b3103de9fed61867f1c8bd33336b913d16831431e7cb48ef1c92/mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", size = 13013834 },
445 | { url = "https://files.pythonhosted.org/packages/b0/79/5f5ec47849b6df1e6943d5fd8e6632fbfc04b4fd4acfa5a5a9535d11b4e2/mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", size = 9781231 },
446 | { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 },
447 | ]
448 |
449 | [[package]]
450 | name = "mypy-extensions"
451 | version = "1.0.0"
452 | source = { registry = "https://pypi.org/simple" }
453 | sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
454 | wheels = [
455 | { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
456 | ]
457 |
458 | [[package]]
459 | name = "nodeenv"
460 | version = "1.9.1"
461 | source = { registry = "https://pypi.org/simple" }
462 | sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
463 | wheels = [
464 | { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
465 | ]
466 |
467 | [[package]]
468 | name = "packaging"
469 | version = "24.2"
470 | source = { registry = "https://pypi.org/simple" }
471 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
472 | wheels = [
473 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
474 | ]
475 |
476 | [[package]]
477 | name = "platformdirs"
478 | version = "4.3.6"
479 | source = { registry = "https://pypi.org/simple" }
480 | sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
481 | wheels = [
482 | { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
483 | ]
484 |
485 | [[package]]
486 | name = "pluggy"
487 | version = "1.5.0"
488 | source = { registry = "https://pypi.org/simple" }
489 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
490 | wheels = [
491 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
492 | ]
493 |
494 | [[package]]
495 | name = "pre-commit"
496 | version = "4.0.1"
497 | source = { registry = "https://pypi.org/simple" }
498 | dependencies = [
499 | { name = "cfgv" },
500 | { name = "identify" },
501 | { name = "nodeenv" },
502 | { name = "pyyaml" },
503 | { name = "virtualenv" },
504 | ]
505 | sdist = { url = "https://files.pythonhosted.org/packages/2e/c8/e22c292035f1bac8b9f5237a2622305bc0304e776080b246f3df57c4ff9f/pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2", size = 191678 }
506 | wheels = [
507 | { url = "https://files.pythonhosted.org/packages/16/8f/496e10d51edd6671ebe0432e33ff800aa86775d2d147ce7d43389324a525/pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878", size = 218713 },
508 | ]
509 |
510 | [[package]]
511 | name = "pygments"
512 | version = "2.19.1"
513 | source = { registry = "https://pypi.org/simple" }
514 | sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
515 | wheels = [
516 | { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
517 | ]
518 |
519 | [[package]]
520 | name = "pymongo"
521 | version = "4.10.1"
522 | source = { registry = "https://pypi.org/simple" }
523 | dependencies = [
524 | { name = "dnspython" },
525 | ]
526 | sdist = { url = "https://files.pythonhosted.org/packages/1a/35/b62a3139f908c68b69aac6a6a3f8cc146869de0a7929b994600e2c587c77/pymongo-4.10.1.tar.gz", hash = "sha256:a9de02be53b6bb98efe0b9eda84ffa1ec027fcb23a2de62c4f941d9a2f2f3330", size = 1903902 }
527 | wheels = [
528 | { url = "https://files.pythonhosted.org/packages/c1/ca/f56b1dd84541de658d246f86828be27e32285f2151fab97efbce1db3ed57/pymongo-4.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e699aa68c4a7dea2ab5a27067f7d3e08555f8d2c0dc6a0c8c60cfd9ff2e6a4b1", size = 835459 },
529 | { url = "https://files.pythonhosted.org/packages/97/01/fe4ee34b33c6863be6a09d1e805ceb1122d9cd5d4a5d1664e360b91adf7e/pymongo-4.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:70645abc714f06b4ad6b72d5bf73792eaad14e3a2cfe29c62a9c81ada69d9e4b", size = 835716 },
530 | { url = "https://files.pythonhosted.org/packages/46/ff/9eb21c1d5861729ae1c91669b02f5bfbd23221ba9809fb97fade761f3f3b/pymongo-4.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae2fd94c9fe048c94838badcc6e992d033cb9473eb31e5710b3707cba5e8aee2", size = 1407173 },
531 | { url = "https://files.pythonhosted.org/packages/e5/d9/8cf042449d6804e00e38d3bb138b0e9acb8a8e0c9dd9dd989ffffd481c3b/pymongo-4.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ded27a4a5374dae03a92e084a60cdbcecd595306555bda553b833baf3fc4868", size = 1456455 },
532 | { url = "https://files.pythonhosted.org/packages/37/9a/da0d121f98c1413853e1172e2095fe77c1629c83a1db107d45a37ca935c2/pymongo-4.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ecc2455e3974a6c429687b395a0bc59636f2d6aedf5785098cf4e1f180f1c71", size = 1433360 },
533 | { url = "https://files.pythonhosted.org/packages/7d/6d/50480f0452e2fb59256d9d641d192366c0079920c36851b818ebeff0cec9/pymongo-4.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920fee41f7d0259f5f72c1f1eb331bc26ffbdc952846f9bd8c3b119013bb52c", size = 1410758 },
534 | { url = "https://files.pythonhosted.org/packages/cd/8f/b83b9910c54f63bfff34305074e79cd08cf5e12dda22d1a2b4ad009b32b3/pymongo-4.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0a15665b2d6cf364f4cd114d62452ce01d71abfbd9c564ba8c74dcd7bbd6822", size = 1380257 },
535 | { url = "https://files.pythonhosted.org/packages/ed/e3/8f381b576e5f912cf0fe34218c6b0ef23d7afdef13fed592900fb52f0ed4/pymongo-4.10.1-cp310-cp310-win32.whl", hash = "sha256:29e1c323c28a4584b7095378ff046815e39ff82cdb8dc4cc6dfe3acf6f9ad1f8", size = 812324 },
536 | { url = "https://files.pythonhosted.org/packages/ab/14/1cae5359e2c4677856527a2965c999c23f596cced4b7828d880cb8fc0f54/pymongo-4.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:88dc4aa45f8744ccfb45164aedb9a4179c93567bbd98a33109d7dc400b00eb08", size = 826774 },
537 | { url = "https://files.pythonhosted.org/packages/e4/a3/d6403ec53fa2fe922b4a5c86388ea5fada01dd51d803e17bb2a7c9cda839/pymongo-4.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:57ee6becae534e6d47848c97f6a6dff69e3cce7c70648d6049bd586764febe59", size = 889238 },
538 | { url = "https://files.pythonhosted.org/packages/29/a2/9643450424bcf241e80bb713497ec2e3273c183d548b4eca357f75d71885/pymongo-4.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f437a612f4d4f7aca1812311b1e84477145e950fdafe3285b687ab8c52541f3", size = 889504 },
539 | { url = "https://files.pythonhosted.org/packages/ec/40/4759984f34415509e9111be8ee863034611affdc1e0b41016c9d53b2f1b3/pymongo-4.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a970fd3117ab40a4001c3dad333bbf3c43687d90f35287a6237149b5ccae61d", size = 1649069 },
540 | { url = "https://files.pythonhosted.org/packages/56/0f/b6e917478a3ada81e768475516cd544982cc42cbb7d3be325182768139e1/pymongo-4.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c4d0e7cd08ef9f8fbf2d15ba281ed55604368a32752e476250724c3ce36c72e", size = 1714927 },
541 | { url = "https://files.pythonhosted.org/packages/56/c5/4237d94dfa19ebdf9a92b1071e2139c91f48908c5782e592c571c33b67ab/pymongo-4.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca6f700cff6833de4872a4e738f43123db34400173558b558ae079b5535857a4", size = 1683454 },
542 | { url = "https://files.pythonhosted.org/packages/9a/16/dbffca9d4ad66f2a325c280f1177912fa23235987f7b9033e283da889b7a/pymongo-4.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec237c305fcbeef75c0bcbe9d223d1e22a6e3ba1b53b2f0b79d3d29c742b45b", size = 1653840 },
543 | { url = "https://files.pythonhosted.org/packages/2b/4d/21df934ef5cf8f0e587bac922a129e13d4c0346c54e9bf2371b90dd31112/pymongo-4.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3337804ea0394a06e916add4e5fac1c89902f1b6f33936074a12505cab4ff05", size = 1613233 },
544 | { url = "https://files.pythonhosted.org/packages/24/07/dd9c3db30e754680606295d5574521956898005db0629411a89163cc6eee/pymongo-4.10.1-cp311-cp311-win32.whl", hash = "sha256:778ac646ce6ac1e469664062dfe9ae1f5c9961f7790682809f5ec3b8fda29d65", size = 857331 },
545 | { url = "https://files.pythonhosted.org/packages/02/68/b71c4106d03eef2482eade440c6f5737c2a4a42f6155726009f80ea38d06/pymongo-4.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:9df4ab5594fdd208dcba81be815fa8a8a5d8dedaf3b346cbf8b61c7296246a7a", size = 876473 },
546 | { url = "https://files.pythonhosted.org/packages/10/d1/60ad99fe3f64d45e6c71ac0e3078e88d9b64112b1bae571fc3707344d6d1/pymongo-4.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fbedc4617faa0edf423621bb0b3b8707836687161210d470e69a4184be9ca011", size = 943356 },
547 | { url = "https://files.pythonhosted.org/packages/ca/9b/21d4c6b4ee9c1fa9691c68dc2a52565e0acb644b9e95148569b4736a4ebd/pymongo-4.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7bd26b2aec8ceeb95a5d948d5cc0f62b0eb6d66f3f4230705c1e3d3d2c04ec76", size = 943142 },
548 | { url = "https://files.pythonhosted.org/packages/07/af/691b7454e219a8eb2d1641aecedd607e3a94b93650c2011ad8a8fd74ef9f/pymongo-4.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb104c3c2a78d9d85571c8ac90ec4f95bca9b297c6eee5ada71fabf1129e1674", size = 1909129 },
549 | { url = "https://files.pythonhosted.org/packages/0c/74/fd75d5ad4181d6e71ce0fca32404fb71b5046ac84d9a1a2f0862262dd032/pymongo-4.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4924355245a9c79f77b5cda2db36e0f75ece5faf9f84d16014c0a297f6d66786", size = 1987763 },
550 | { url = "https://files.pythonhosted.org/packages/8a/56/6d3d0ef63c6d8cb98c7c653a3a2e617675f77a95f3853851d17a7664876a/pymongo-4.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11280809e5dacaef4971113f0b4ff4696ee94cfdb720019ff4fa4f9635138252", size = 1950821 },
551 | { url = "https://files.pythonhosted.org/packages/70/ed/1603fa0c0e51444752c3fa91f16c3a97e6d92eb9fe5e553dae4f18df16f6/pymongo-4.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5d55f2a82e5eb23795f724991cac2bffbb1c0f219c0ba3bf73a835f97f1bb2e", size = 1912247 },
552 | { url = "https://files.pythonhosted.org/packages/c1/66/e98b2308971d45667cb8179d4d66deca47336c90663a7e0527589f1038b7/pymongo-4.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e974ab16a60be71a8dfad4e5afccf8dd05d41c758060f5d5bda9a758605d9a5d", size = 1862230 },
553 | { url = "https://files.pythonhosted.org/packages/6c/80/ba9b7ed212a5f8cf8ad7037ed5bbebc1c587fc09242108f153776e4a338b/pymongo-4.10.1-cp312-cp312-win32.whl", hash = "sha256:544890085d9641f271d4f7a47684450ed4a7344d6b72d5968bfae32203b1bb7c", size = 903045 },
554 | { url = "https://files.pythonhosted.org/packages/76/8b/5afce891d78159912c43726fab32641e3f9718f14be40f978c148ea8db48/pymongo-4.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:dcc07b1277e8b4bf4d7382ca133850e323b7ab048b8353af496d050671c7ac52", size = 926686 },
555 | { url = "https://files.pythonhosted.org/packages/83/76/df0fd0622a85b652ad0f91ec8a0ebfd0cb86af6caec8999a22a1f7481203/pymongo-4.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:90bc6912948dfc8c363f4ead54d54a02a15a7fee6cfafb36dc450fc8962d2cb7", size = 996981 },
556 | { url = "https://files.pythonhosted.org/packages/4c/39/fa50531de8d1d8af8c253caeed20c18ccbf1de5d970119c4a42c89f2bd09/pymongo-4.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:594dd721b81f301f33e843453638e02d92f63c198358e5a0fa8b8d0b1218dabc", size = 996769 },
557 | { url = "https://files.pythonhosted.org/packages/bf/50/6936612c1b2e32d95c30e860552d3bc9e55cfa79a4f73b73225fa05a028c/pymongo-4.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0783e0c8e95397c84e9cf8ab092ab1e5dd7c769aec0ef3a5838ae7173b98dea0", size = 2169159 },
558 | { url = "https://files.pythonhosted.org/packages/78/8c/45cb23096e66c7b1da62bb8d9c7ac2280e7c1071e13841e7fb71bd44fd9f/pymongo-4.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fb6a72e88df46d1c1040fd32cd2d2c5e58722e5d3e31060a0393f04ad3283de", size = 2260569 },
559 | { url = "https://files.pythonhosted.org/packages/29/b6/e5ec697087e527a6a15c5f8daa5bcbd641edb8813487345aaf963d3537dc/pymongo-4.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e3a593333e20c87415420a4fb76c00b7aae49b6361d2e2205b6fece0563bf40", size = 2218142 },
560 | { url = "https://files.pythonhosted.org/packages/ad/8a/c0b45bee0f0c57732c5c36da5122c1796efd5a62d585fbc504e2f1401244/pymongo-4.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72e2ace7456167c71cfeca7dcb47bd5dceda7db2231265b80fc625c5e8073186", size = 2170623 },
561 | { url = "https://files.pythonhosted.org/packages/3b/26/6c0a5360a571df24c9bfbd51b1dae279f4f0c511bdbc0906f6df6d1543fa/pymongo-4.10.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ad05eb9c97e4f589ed9e74a00fcaac0d443ccd14f38d1258eb4c39a35dd722b", size = 2111112 },
562 | { url = "https://files.pythonhosted.org/packages/38/bc/5b91b728e1cf505d931f04e24cbac71ae519523785570ed046cdc31e6efc/pymongo-4.10.1-cp313-cp313-win32.whl", hash = "sha256:ee4c86d8e6872a61f7888fc96577b0ea165eb3bdb0d841962b444fa36001e2bb", size = 948727 },
563 | { url = "https://files.pythonhosted.org/packages/0d/2a/7c24a6144eaa06d18ed52822ea2b0f119fd9267cd1abbb75dae4d89a3803/pymongo-4.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:45ee87a4e12337353242bc758accc7fb47a2f2d9ecc0382a61e64c8f01e86708", size = 976873 },
564 | { url = "https://files.pythonhosted.org/packages/39/69/a13a61fd9a19e659cb43cb98240a75dab2f8e7b60bee568c309a123f8edc/pymongo-4.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15b1492cc5c7cd260229590be7218261e81684b8da6d6de2660cf743445500ce", size = 781650 },
565 | { url = "https://files.pythonhosted.org/packages/14/13/a5a3da69ee146bac81baacc7b27995849bfbb89eaabfb3d19824c7056fb4/pymongo-4.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95207503c41b97e7ecc7e596d84a61f441b4935f11aa8332828a754e7ada8c82", size = 781929 },
566 | { url = "https://files.pythonhosted.org/packages/b0/65/118bc20fc66451ae4b8ceafbd9aab1168bb13cf4e5699b81807bb911a443/pymongo-4.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb99f003c720c6d83be02c8f1a7787c22384a8ca9a4181e406174db47a048619", size = 1167200 },
567 | { url = "https://files.pythonhosted.org/packages/d1/4a/19c3d45659835f814efef8b250456923899cc7542c52f8beb1a6a164106f/pymongo-4.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2bc1ee4b1ca2c4e7e6b7a5e892126335ec8d9215bcd3ac2fe075870fefc3358", size = 1200021 },
568 | { url = "https://files.pythonhosted.org/packages/1c/03/69cee6f23463251a8f6de6670fb665e2531cc0df500d61150e57d1aedf70/pymongo-4.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:93a0833c10a967effcd823b4e7445ec491f0bf6da5de0ca33629c0528f42b748", size = 1185518 },
569 | { url = "https://files.pythonhosted.org/packages/00/58/01d8b3b374e45931581364752bf7a4693e9c7422704f81e6e91ffa42c38d/pymongo-4.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f56707497323150bd2ed5d63067f4ffce940d0549d4ea2dfae180deec7f9363", size = 1169799 },
570 | { url = "https://files.pythonhosted.org/packages/be/6f/9aa0ba2f4cfa4c30c962bd1f5a38b2eb95b23e3965918d211da82ebaacf6/pymongo-4.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:409ab7d6c4223e5c85881697f365239dd3ed1b58f28e4124b846d9d488c86880", size = 1149344 },
571 | { url = "https://files.pythonhosted.org/packages/fc/af/40f0112605f75f5ae5da554b2f779a83c40ecf2298c57e120e6d7bfcff65/pymongo-4.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dac78a650dc0637d610905fd06b5fa6419ae9028cf4d04d6a2657bc18a66bbce", size = 1134277 },
572 | { url = "https://files.pythonhosted.org/packages/a1/92/9a4afaf02d4d633c3211b7535e6bfbb2527524ca8ac8039b8d7eb3c765eb/pymongo-4.10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1ec3fa88b541e0481aff3c35194c9fac96e4d57ec5d1c122376000eb28c01431", size = 1167588 },
573 | { url = "https://files.pythonhosted.org/packages/29/de/3c23e7739b0d0dafa4d802e9773011cf54e305c7cb788a52d50c3ef585d2/pymongo-4.10.1-cp39-cp39-win32.whl", hash = "sha256:e0e961923a7b8a1c801c43552dcb8153e45afa41749d9efbd3a6d33f45489f7a", size = 767325 },
574 | { url = "https://files.pythonhosted.org/packages/36/d4/49198168f296838c49d732743ae073f9ca9e8f65f15f5209637456892968/pymongo-4.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:dabe8bf1ad644e6b93f3acf90ff18536d94538ca4d27e583c6db49889e98e48f", size = 777078 },
575 | ]
576 |
577 | [[package]]
578 | name = "pytest"
579 | version = "8.3.4"
580 | source = { registry = "https://pypi.org/simple" }
581 | dependencies = [
582 | { name = "colorama", marker = "sys_platform == 'win32'" },
583 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
584 | { name = "iniconfig" },
585 | { name = "packaging" },
586 | { name = "pluggy" },
587 | { name = "tomli", marker = "python_full_version < '3.11'" },
588 | ]
589 | sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 }
590 | wheels = [
591 | { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 },
592 | ]
593 |
594 | [[package]]
595 | name = "pyyaml"
596 | version = "6.0.2"
597 | source = { registry = "https://pypi.org/simple" }
598 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
599 | wheels = [
600 | { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 },
601 | { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 },
602 | { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 },
603 | { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 },
604 | { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 },
605 | { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 },
606 | { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 },
607 | { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 },
608 | { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 },
609 | { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
610 | { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
611 | { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
612 | { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
613 | { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
614 | { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
615 | { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
616 | { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
617 | { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
618 | { 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 },
619 | { 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 },
620 | { 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 },
621 | { 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 },
622 | { 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 },
623 | { 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 },
624 | { 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 },
625 | { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
626 | { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
627 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
628 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
629 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
630 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
631 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
632 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
633 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
634 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
635 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
636 | { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 },
637 | { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 },
638 | { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 },
639 | { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 },
640 | { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 },
641 | { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 },
642 | { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 },
643 | { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 },
644 | { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 },
645 | ]
646 |
647 | [[package]]
648 | name = "requests"
649 | version = "2.32.3"
650 | source = { registry = "https://pypi.org/simple" }
651 | dependencies = [
652 | { name = "certifi" },
653 | { name = "charset-normalizer" },
654 | { name = "idna" },
655 | { name = "urllib3" },
656 | ]
657 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
658 | wheels = [
659 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
660 | ]
661 |
662 | [[package]]
663 | name = "snowballstemmer"
664 | version = "2.2.0"
665 | source = { registry = "https://pypi.org/simple" }
666 | sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 }
667 | wheels = [
668 | { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 },
669 | ]
670 |
671 | [[package]]
672 | name = "sphinx"
673 | version = "7.4.7"
674 | source = { registry = "https://pypi.org/simple" }
675 | resolution-markers = [
676 | "python_full_version < '3.10'",
677 | ]
678 | dependencies = [
679 | { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
680 | { name = "babel", marker = "python_full_version < '3.10'" },
681 | { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" },
682 | { name = "docutils", marker = "python_full_version < '3.10'" },
683 | { name = "imagesize", marker = "python_full_version < '3.10'" },
684 | { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
685 | { name = "jinja2", marker = "python_full_version < '3.10'" },
686 | { name = "packaging", marker = "python_full_version < '3.10'" },
687 | { name = "pygments", marker = "python_full_version < '3.10'" },
688 | { name = "requests", marker = "python_full_version < '3.10'" },
689 | { name = "snowballstemmer", marker = "python_full_version < '3.10'" },
690 | { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10'" },
691 | { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10'" },
692 | { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10'" },
693 | { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10'" },
694 | { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10'" },
695 | { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10'" },
696 | { name = "tomli", marker = "python_full_version < '3.10'" },
697 | ]
698 | sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911 }
699 | wheels = [
700 | { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624 },
701 | ]
702 |
703 | [[package]]
704 | name = "sphinx"
705 | version = "8.1.3"
706 | source = { registry = "https://pypi.org/simple" }
707 | resolution-markers = [
708 | "python_full_version >= '3.10'",
709 | ]
710 | dependencies = [
711 | { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
712 | { name = "babel", marker = "python_full_version >= '3.10'" },
713 | { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
714 | { name = "docutils", marker = "python_full_version >= '3.10'" },
715 | { name = "imagesize", marker = "python_full_version >= '3.10'" },
716 | { name = "jinja2", marker = "python_full_version >= '3.10'" },
717 | { name = "packaging", marker = "python_full_version >= '3.10'" },
718 | { name = "pygments", marker = "python_full_version >= '3.10'" },
719 | { name = "requests", marker = "python_full_version >= '3.10'" },
720 | { name = "snowballstemmer", marker = "python_full_version >= '3.10'" },
721 | { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.10'" },
722 | { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.10'" },
723 | { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.10'" },
724 | { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.10'" },
725 | { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.10'" },
726 | { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.10'" },
727 | { name = "tomli", marker = "python_full_version == '3.10.*'" },
728 | ]
729 | sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 }
730 | wheels = [
731 | { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 },
732 | ]
733 |
734 | [[package]]
735 | name = "sphinxcontrib-applehelp"
736 | version = "2.0.0"
737 | source = { registry = "https://pypi.org/simple" }
738 | sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 }
739 | wheels = [
740 | { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 },
741 | ]
742 |
743 | [[package]]
744 | name = "sphinxcontrib-devhelp"
745 | version = "2.0.0"
746 | source = { registry = "https://pypi.org/simple" }
747 | sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 }
748 | wheels = [
749 | { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 },
750 | ]
751 |
752 | [[package]]
753 | name = "sphinxcontrib-htmlhelp"
754 | version = "2.1.0"
755 | source = { registry = "https://pypi.org/simple" }
756 | sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 }
757 | wheels = [
758 | { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 },
759 | ]
760 |
761 | [[package]]
762 | name = "sphinxcontrib-jsmath"
763 | version = "1.0.1"
764 | source = { registry = "https://pypi.org/simple" }
765 | sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 }
766 | wheels = [
767 | { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 },
768 | ]
769 |
770 | [[package]]
771 | name = "sphinxcontrib-qthelp"
772 | version = "2.0.0"
773 | source = { registry = "https://pypi.org/simple" }
774 | sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 }
775 | wheels = [
776 | { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 },
777 | ]
778 |
779 | [[package]]
780 | name = "sphinxcontrib-serializinghtml"
781 | version = "2.0.0"
782 | source = { registry = "https://pypi.org/simple" }
783 | sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 }
784 | wheels = [
785 | { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 },
786 | ]
787 |
788 | [[package]]
789 | name = "tomli"
790 | version = "2.2.1"
791 | source = { registry = "https://pypi.org/simple" }
792 | sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 }
793 | wheels = [
794 | { 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 },
795 | { 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 },
796 | { 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 },
797 | { 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 },
798 | { 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 },
799 | { 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 },
800 | { 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 },
801 | { 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 },
802 | { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 },
803 | { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 },
804 | { 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 },
805 | { 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 },
806 | { 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 },
807 | { 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 },
808 | { 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 },
809 | { 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 },
810 | { 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 },
811 | { 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 },
812 | { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 },
813 | { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 },
814 | { 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 },
815 | { 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 },
816 | { 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 },
817 | { 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 },
818 | { 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 },
819 | { 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 },
820 | { 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 },
821 | { 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 },
822 | { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 },
823 | { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 },
824 | { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 },
825 | ]
826 |
827 | [[package]]
828 | name = "typing-extensions"
829 | version = "4.12.2"
830 | source = { registry = "https://pypi.org/simple" }
831 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
832 | wheels = [
833 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
834 | ]
835 |
836 | [[package]]
837 | name = "urllib3"
838 | version = "2.3.0"
839 | source = { registry = "https://pypi.org/simple" }
840 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
841 | wheels = [
842 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
843 | ]
844 |
845 | [[package]]
846 | name = "virtualenv"
847 | version = "20.28.1"
848 | source = { registry = "https://pypi.org/simple" }
849 | dependencies = [
850 | { name = "distlib" },
851 | { name = "filelock" },
852 | { name = "platformdirs" },
853 | ]
854 | sdist = { url = "https://files.pythonhosted.org/packages/50/39/689abee4adc85aad2af8174bb195a819d0be064bf55fcc73b49d2b28ae77/virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329", size = 7650532 }
855 | wheels = [
856 | { url = "https://files.pythonhosted.org/packages/51/8f/dfb257ca6b4e27cb990f1631142361e4712badab8e3ca8dc134d96111515/virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb", size = 4276719 },
857 | ]
858 |
859 | [[package]]
860 | name = "werkzeug"
861 | version = "3.1.3"
862 | source = { registry = "https://pypi.org/simple" }
863 | dependencies = [
864 | { name = "markupsafe" },
865 | ]
866 | sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925 }
867 | wheels = [
868 | { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 },
869 | ]
870 |
871 | [[package]]
872 | name = "zipp"
873 | version = "3.21.0"
874 | source = { registry = "https://pypi.org/simple" }
875 | sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 }
876 | wheels = [
877 | { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 },
878 | ]
879 |
--------------------------------------------------------------------------------