├── .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 | Fork me on GitHub 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 |
14 |
15 | 16 | 17 |
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 |
9 |
10 | 11 | 12 |
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 | --------------------------------------------------------------------------------