pax_global_header00006660000000000000000000000064145114071560014516gustar00rootroot0000000000000052 comment=f1ba236bbbc59314c4988dc54b12afef8ff6e9fa bachya-aiopurpleair-f1ba236/000077500000000000000000000000001451140715600160455ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/.bandit.yaml000066400000000000000000000002551451140715600202520ustar00rootroot00000000000000--- tests: - B103 - B108 - B306 - B307 - B313 - B314 - B315 - B316 - B317 - B318 - B319 - B320 - B325 - B601 - B602 - B604 - B608 - B609 bachya-aiopurpleair-f1ba236/.codeclimate.yml000066400000000000000000000003401451140715600211140ustar00rootroot00000000000000--- engines: duplication: enabled: true config: languages: - python fixme: enabled: true radon: enabled: true ratings: paths: - "**.py" exclude_paths: - dist/ - docs/ - tests/ bachya-aiopurpleair-f1ba236/.flake8000066400000000000000000000002651451140715600172230ustar00rootroot00000000000000[flake8] ignore = E203,E266,E501,F811,W503 max-line-length = 80 max-complexity = 18 per-file-ignores = aiopurpleair/model/*:F841 tests/*:DAR,S101 select = B,B9,LK,C,D,E,F,I,S,W bachya-aiopurpleair-f1ba236/.github/000077500000000000000000000000001451140715600174055ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/.github/ISSUE_TEMPLATE/000077500000000000000000000000001451140715600215705ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000007641451140715600242710ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. bachya-aiopurpleair-f1ba236/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000006461451140715600253230ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Additional context** Add any other context or screenshots about the feature request here. bachya-aiopurpleair-f1ba236/.github/dependabot.yml000066400000000000000000000005521451140715600222370ustar00rootroot00000000000000--- version: 2 updates: - package-ecosystem: pip directory: "/" schedule: interval: daily time: "06:00" - package-ecosystem: pip directory: "/.github/workflows" schedule: interval: daily time: "06:00" - package-ecosystem: "github-actions" directory: "/" schedule: interval: daily time: "06:00" bachya-aiopurpleair-f1ba236/.github/labels.yml000066400000000000000000000024241451140715600213740ustar00rootroot00000000000000--- - name: "breaking-change" color: ee0701 description: "A breaking change for existing users" - name: "bug" color: ee0701 description: "Bugs or issues which will cause a problem for users" - name: "documentation" color: 0052cc description: "Project documentation" - name: "enhancement" color: 1d76db description: "Enhancement of the code, not introducing new features." - name: "maintenance" color: 2af79e description: "Generic library tasks" - name: "dependencies" color: 1d76db description: "Upgrade or downgrade of project dependencies" - name: "in-progress" color: fbca04 description: "Issue is currently being resolved by a developer" - name: "stale" color: fef2c0 description: "There has not been activity on this issue or PR for some time" - name: "no-stale" color: fef2c0 description: "This issue or PR is exempted from the stale bot" - name: "security" color: ee0701 description: "Marks a security issue that needs to be resolved ASAP" - name: "incomplete" color: fef2c0 description: "Marks a PR or issue that is missing information" - name: "invalid" color: fef2c0 description: "Marks a PR or issue that is missing information" - name: "help-wanted" color: 0e8a16 description: "Needs a helping hang or expertise in order to resolve" bachya-aiopurpleair-f1ba236/.github/pull_request_template.md000066400000000000000000000005421451140715600243470ustar00rootroot00000000000000**Describe what the PR does:** **Does this fix a specific issue?** Fixes https://github.com/bachya/aiopurpleair/issues/ **Checklist:** - [ ] Confirm that one or more new tests are written for the new functionality. - [ ] Run tests and ensure everything passes (with 100% test coverage). - [ ] Update `README.md` with any new documentation. bachya-aiopurpleair-f1ba236/.github/release-drafter.yml000066400000000000000000000010051451140715600231710ustar00rootroot00000000000000--- categories: - title: "๐Ÿšจ Breaking Changes" labels: - "breaking-change" - title: "๐Ÿš€ Features" labels: - "enhancement" - title: "๐Ÿ› Bug Fixes" labels: - "bug" - title: "๐Ÿ“• Documentation" labels: - "documentation" - title: "๐Ÿงฐ Maintenance" labels: - "dependencies" - "maintenance" - "tooling" change-template: "- $TITLE (#$NUMBER)" name-template: "$NEXT_PATCH_VERSION" tag-template: "$NEXT_PATCH_VERSION" template: | $CHANGES bachya-aiopurpleair-f1ba236/.github/workflows/000077500000000000000000000000001451140715600214425ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/.github/workflows/codeql.yml000066400000000000000000000010021451140715600234250ustar00rootroot00000000000000--- name: CodeQL "on": push: branches: - dev - main pull_request: branches: - dev - main workflow_dispatch: schedule: - cron: "30 1 * * 0" jobs: codeql: name: Scanning runs-on: ubuntu-latest steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v4 - name: ๐Ÿ— Initialize CodeQL uses: github/codeql-action/init@v2 - name: ๐Ÿš€ Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 bachya-aiopurpleair-f1ba236/.github/workflows/labels.yml000066400000000000000000000006721451140715600234340ustar00rootroot00000000000000--- name: Sync Labels "on": push: branches: - main paths: - .github/labels.yml workflow_dispatch: jobs: labels: name: โ™ป๏ธ Sync labels runs-on: ubuntu-latest steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v4 - name: ๐Ÿš€ Run Label Syncer uses: micnncim/action-label-syncer@v1.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} bachya-aiopurpleair-f1ba236/.github/workflows/lock.yml000066400000000000000000000006251451140715600231200ustar00rootroot00000000000000--- name: Lock Closed Issues and PRs "on": schedule: - cron: "0 9 * * *" workflow_dispatch: jobs: lock: name: ๐Ÿ”’ Lock! runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v4.0.1 with: github-token: ${{ github.token }} issue-inactive-days: "30" issue-lock-reason: "" pr-inactive-days: "1" pr-lock-reason: "" bachya-aiopurpleair-f1ba236/.github/workflows/publish.yml000066400000000000000000000007771451140715600236460ustar00rootroot00000000000000--- name: Publish to PyPI "on": push: tags: - "*" jobs: publish_to_pypi: runs-on: ubuntu-latest steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v4 - name: ๐Ÿ— Set up Python 3.12 id: python uses: actions/setup-python@v4 with: python-version: "3.12" - name: ๐Ÿš€ Publish to PyPi run: | pip install poetry poetry publish --build -u __token__ -p ${{ secrets.PYPI_API_KEY }} bachya-aiopurpleair-f1ba236/.github/workflows/release-drafter.yml000066400000000000000000000005341451140715600252340ustar00rootroot00000000000000--- name: Release Drafter "on": push: branches: - main workflow_dispatch: jobs: update_release_draft: name: โœ๏ธ Draft Release runs-on: ubuntu-latest steps: - name: ๐Ÿš€ Run Release Drafter uses: release-drafter/release-drafter@v5.24.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} bachya-aiopurpleair-f1ba236/.github/workflows/requirements.txt000066400000000000000000000000161451140715600247230ustar00rootroot00000000000000poetry==1.6.1 bachya-aiopurpleair-f1ba236/.github/workflows/stale.yml000066400000000000000000000023461451140715600233020ustar00rootroot00000000000000--- name: Stale "on": schedule: - cron: "0 8 * * *" workflow_dispatch: jobs: stale: name: ๐Ÿงน Clean up stale issues and PRs runs-on: ubuntu-latest steps: - name: ๐Ÿš€ Run stale uses: actions/stale@v8 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 days-before-close: 7 remove-stale-when-updated: true stale-issue-label: "stale" exempt-issue-labels: "no-stale,help-wanted" stale-issue-message: > There hasn't been any activity on this issue recently, so it has been marked as stale. Please make sure to update to the latest version and check if that solves the issue. Let us know if that works for you by leaving a comment. This issue will be closed if no further activity occurs. Thanks! stale-pr-label: "stale" exempt-pr-labels: "no-stale" stale-pr-message: > There hasn't been any activity on this pull request recently, so it has automatically been marked as stale and will be closed if no further action occurs within 7 days. Thank you for your contributions. bachya-aiopurpleair-f1ba236/.github/workflows/static-analysis.yml000066400000000000000000000032511451140715600252760ustar00rootroot00000000000000--- name: Linting and Static Analysis "on": pull_request: branches: - dev - main push: branches: - dev - main jobs: lint: name: "Linting & Static Analysis" runs-on: ubuntu-latest steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v4 - name: ๐Ÿ— Set up Python 3.12 id: setup-python uses: actions/setup-python@v4 with: python-version: "3.12" - name: โคต๏ธ Get pip cache directory id: pip-cache run: | echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - name: โคต๏ธ Establish pip cache uses: actions/cache@v3 with: path: ${{ steps.pip-cache.outputs.dir }} key: "${{ runner.os }}-pip-\ ${{ hashFiles('.github/workflows/requirements.txt') }}" restore-keys: | ${{ runner.os }}-pip- - name: ๐Ÿ— Install workflow dependencies run: | pip install -r .github/workflows/requirements.txt poetry config virtualenvs.create true poetry config virtualenvs.in-project true - name: โคต๏ธ Establish poetry cache uses: actions/cache@v3 with: path: .venv key: "venv-${{ steps.setup-python.outputs.python-version }}-\ ${{ hashFiles('poetry.lock') }}" restore-keys: | venv-${{ steps.setup-python.outputs.python-version }}- - name: ๐Ÿ— Install package dependencies run: | poetry install --no-interaction - name: ๐Ÿš€ Run pre-commit hooks uses: pre-commit/action@v3.0.0 env: SKIP: no-commit-to-branch,pytest bachya-aiopurpleair-f1ba236/.github/workflows/test.yml000066400000000000000000000072551451140715600231550ustar00rootroot00000000000000--- name: Tests and Coverage "on": pull_request: branches: - dev - main push: branches: - dev - main jobs: test: name: Tests runs-on: ubuntu-latest strategy: matrix: python-version: - "3.10" - "3.11" - "3.12" steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v4 - name: ๐Ÿ— Set up Python id: setup-python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: โคต๏ธ Get pip cache directory id: pip-cache run: | echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - name: โคต๏ธ Establish pip cache uses: actions/cache@v3 with: path: ${{ steps.pip-cache.outputs.dir }} key: "${{ runner.os }}-pip-\ ${{ hashFiles('.github/workflows/requirements.txt') }}" restore-keys: | ${{ runner.os }}-pip- - name: ๐Ÿ— Install workflow dependencies run: | pip install -r .github/workflows/requirements.txt poetry config virtualenvs.create true poetry config virtualenvs.in-project true - name: โคต๏ธ Establish poetry cache uses: actions/cache@v3 with: path: .venv key: "venv-${{ steps.setup-python.outputs.python-version }}-\ ${{ hashFiles('poetry.lock') }}" restore-keys: | venv-${{ steps.setup-python.outputs.python-version }}- - name: ๐Ÿ— Install package dependencies run: | poetry install --no-interaction - name: ๐Ÿš€ Run pytest run: poetry run pytest --cov aiopurpleair tests - name: โฌ†๏ธ Upload coverage artifact uses: actions/upload-artifact@v3 with: name: coverage-${{ matrix.python-version }} path: .coverage coverage: name: Code Coverage needs: test runs-on: ubuntu-latest steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v4 - name: โฌ‡๏ธ Download coverage data uses: actions/download-artifact@v3 - name: ๐Ÿ— Set up Python 3.12 id: setup-python uses: actions/setup-python@v4 with: python-version: "3.12" - name: โคต๏ธ Get pip cache directory id: pip-cache run: | echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - name: โคต๏ธ Establish pip cache uses: actions/cache@v3 with: path: ${{ steps.pip-cache.outputs.dir }} key: "${{ runner.os }}-pip-\ ${{ hashFiles('.github/workflows/requirements.txt') }}" restore-keys: | ${{ runner.os }}-pip- - name: ๐Ÿ— Install workflow dependencies run: | pip install -r .github/workflows/requirements.txt poetry config virtualenvs.create true poetry config virtualenvs.in-project true - name: โคต๏ธ Establish poetry cache uses: actions/cache@v3 with: path: .venv key: "venv-${{ steps.setup-python.outputs.python-version }}-\ ${{ hashFiles('poetry.lock') }}" restore-keys: | venv-${{ steps.setup-python.outputs.python-version }}- - name: ๐Ÿ— Install package dependencies run: | poetry install --no-interaction - name: ๐Ÿš€ Process coverage results run: | poetry run coverage combine coverage*/.coverage* poetry run coverage xml -i - name: ๐Ÿ“Š Upload coverage report to codecov.io uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} bachya-aiopurpleair-f1ba236/.gitignore000066400000000000000000000001451451140715600200350ustar00rootroot00000000000000*.egg-info .DS_Store .coverage .mypy_cache .nox .tox .venv __pycache__ coverage.xml docs/_build tags bachya-aiopurpleair-f1ba236/.pre-commit-config.yaml000066400000000000000000000123061451140715600223300ustar00rootroot00000000000000--- repos: - repo: local hooks: - id: bandit name: ๐ŸŽฐ Checking for vulnerabilities using bandit language: system types: [python] entry: poetry run bandit files: ^aiopurpleair/ require_serial: true - id: black name: โ˜•๏ธ Formatting code using black language: system types: [python] entry: poetry run black require_serial: true - id: blacken-docs name: โ˜•๏ธ Format documentation using black language: system files: '\.(rst|md|markdown|py|tex)$' entry: poetry run blacken-docs require_serial: true - id: check-ast name: ๐Ÿ Checking Python AST language: system types: [python] entry: poetry run check-ast - id: check-case-conflict name: ๐Ÿ”  Checking for case conflicts language: system entry: poetry run check-case-conflict - id: check-docstring-first name: โ„น๏ธ Checking docstrings are first language: system types: [python] entry: poetry run check-docstring-first - id: check-executables-have-shebangs name: ๐Ÿง Checking that executables have shebangs language: system types: [text, executable] entry: poetry run check-executables-have-shebangs stages: [commit, push, manual] - id: check-json name: ๏ฝ› Checking JSON files language: system types: [json] entry: poetry run check-json - id: check-merge-conflict name: ๐Ÿ’ฅ Checking for merge conflicts language: system types: [text] entry: poetry run check-merge-conflict - id: check-symlinks name: ๐Ÿ”— Checking for broken symlinks language: system types: [symlink] entry: poetry run check-symlinks - id: check-toml name: โœ… Checking TOML files language: system types: [toml] entry: poetry run check-toml - id: check-yaml name: โœ… Checking YAML files language: system types: [yaml] entry: poetry run check-yaml - id: codespell name: โœ… Checking code for misspellings language: system types: [text] exclude: ^poetry\.lock$ entry: poetry run codespell - id: debug-statements name: ๐Ÿชต Checking for debug statements and imports (Python) language: system types: [python] entry: poetry run debug-statement-hook - id: detect-private-key name: ๐Ÿ•ต๏ธ Detecting private keys language: system types: [text] entry: poetry run detect-private-key - id: end-of-file-fixer name: ๐Ÿ”š Checking end of files language: system types: [text] entry: poetry run end-of-file-fixer stages: [commit, push, manual] - id: fix-byte-order-marker name: ๐Ÿš Checking UTF-8 byte order marker language: system types: [text] entry: poetry run fix-byte-order-marker - id: isort name: ๐Ÿ”€ Sorting all imports with isort language: system types: [python] entry: poetry run isort - id: mypy name: ๐Ÿ†Ž Performing static type checking using mypy language: system types: [python] entry: poetry run mypy exclude: ^vulture_whitelist.py$ require_serial: true - id: no-commit-to-branch name: ๐Ÿ›‘ Checking for commit to protected branch language: system entry: poetry run no-commit-to-branch pass_filenames: false always_run: true args: - --branch=dev - --branch=main - id: poetry name: ๐Ÿ“œ Checking pyproject with Poetry language: system entry: poetry check pass_filenames: false always_run: true - id: pylint name: ๐ŸŒŸ Starring code with pylint language: system types: [python] entry: poetry run pylint exclude: ^vulture_whitelist.py$ - id: pyupgrade name: ๐Ÿ†™ Checking for upgradable syntax with pyupgrade language: system types: [python] entry: poetry run pyupgrade args: [--py39-plus, --keep-runtime-typing] - id: ruff name: ๐Ÿ‘” Enforcing style guide with ruff language: system types: [python] entry: poetry run ruff --fix exclude: ^vulture_whitelist.py$ - id: trailing-whitespace name: โœ„ Trimming trailing whitespace language: system types: [text] entry: poetry run trailing-whitespace-fixer stages: [commit, push, manual] - id: vulture name: ๐Ÿ” Finding unused Python code with Vulture language: system types: [python] entry: poetry run vulture vulture_whitelist.py pass_filenames: false require_serial: true - id: yamllint name: ๐ŸŽ— Checking YAML files with yamllint language: system types: [yaml] entry: poetry run yamllint - repo: https://github.com/pre-commit/mirrors-prettier rev: "v3.0.0-alpha.4" hooks: - id: prettier name: ๐Ÿ’„ Ensuring files are prettier bachya-aiopurpleair-f1ba236/.rtx.toml000066400000000000000000000000701451140715600176320ustar00rootroot00000000000000[tools] python = { version="3.12", virtualenv=".venv" } bachya-aiopurpleair-f1ba236/LICENSE000066400000000000000000000020531451140715600170520ustar00rootroot00000000000000MIT License Copyright (c) 2023 Aaron Bach Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bachya-aiopurpleair-f1ba236/README.md000066400000000000000000000173631451140715600173360ustar00rootroot00000000000000# ๐ŸŸฃ aiopurpleair: A Python3, asyncio-based library to interact with the PurpleAir API [![CI][ci-badge]][ci] [![PyPI][pypi-badge]][pypi] [![Version][version-badge]][version] [![License][license-badge]][license] [![Code Coverage][codecov-badge]][codecov] [![Maintainability][maintainability-badge]][maintainability] Buy Me A Coffee `aiopurpleair` is a Python3, asyncio-based library to interact with the [PurpleAir](https://www2.purpleair.com/) API. - [Installation](#installation) - [Python Versions](#python-versions) - [Usage](#usage) - [Checking an API Key](#checking-an-api-key) - [Getting Sensors](#getting-sensors) - [Getting a Single Sensor](#getting-a-single-sensor) - [Getting Nearby Sensors](#getting-nearby-sensors) - [Getting a Map URL](#getting-a-map-url) - [Connection Pooling](#connection-pooling) - [Contributing](#contributing) # Installation ```bash pip install aiopurpleair ``` # Python Versions `aiopurpleair` is currently supported on: - Python 3.10 - Python 3.11 - Python 3.12 # Usage In-depth documentation on the API can be found [here][purpleair-api]. Unless otherwise noted, `aiopurpleair` endeavors to follow the API as closely as possible. ## Checking an API Key To check whether an API key is valid and what properties it has: ```python import asyncio from aiopurpleair import API async def main() -> None: """Run.""" api = API("") response = await api.async_check_api_key() # >>> response.api_key_type == ApiKeyType.READ # >>> response.api_version == "V1.0.11-0.0.41" # >>> response.timestamp_utc == datetime(2022, 10, 27, 18, 25, 41) asyncio.run(main()) ``` ## Getting Sensors ```python import asyncio from aiopurpleair import API async def main() -> None: """Run.""" api = API("") response = await api.sensors.async_get_sensors(["name"]) # >>> response.api_version == "V1.0.11-0.0.41" # >>> response.data == { # >>> 131075: SensorModel(sensor_index=131075, name=Mariners Bluff), # >>> 131079: SensorModel(sensor_index=131079, name=BRSKBV-outside), # >>> } # >>> response.data_timestamp_utc == datetime(2022, 11, 3, 19, 25, 31) # >>> response.fields == ["sensor_index", "name"] # >>> response.firmware_default_version == "7.02" # >>> response.max_age == 604800 # >>> response.timestamp_utc == datetime(2022, 11, 3, 19, 26, 29) asyncio.run(main()) ``` ### Method Parameters - `fields` (required): The sensor data fields to include - `location_type` (optional): An LocationType to filter by - `max_age` (optional): Filter results modified within these seconds - `modified_since` (optional): Filter results modified since a UTC datetime - `read_keys` (optional): Read keys for private sensors - `sensor_indices` (optional): Filter results by sensor index ## Getting a Single Sensor ```python import asyncio from aiopurpleair import API async def main() -> None: """Run.""" api = API("") response = await api.sensors.async_get_sensor(131075) # >>> response.api_version == "V1.0.11-0.0.41" # >>> response.data_timestamp_utc == datetime(2022, 11, 5, 16, 36, 21) # >>> response.sensor == SensorModel(sensor_index=131075, ...), # >>> response.timestamp_utc == datetime(2022, 11, 5, 16, 37, 3) asyncio.run(main()) ``` ### Method Parameters - `sensor_index` (required): The sensor index of the sensor to retrieve. - `fields` (optional): The sensor data fields to include. - `read_key` (optional): A read key for a private sensor. ## Getting Nearby Sensors This method returns a list of `NearbySensorResult` objects that are within a bounding box around a given latitude/longitude pair. The list is sorted from nearest to furthest (i.e., the first index in the list is the closest to the latitude/longitude). `NearbySensorResult` objects have two properties: - `sensor`: the corresponding `SensorModel` object - `distance`: the calculated distance (in kilometers) between this sensor and the provided latitude/longitude ```python import asyncio from aiopurpleair import API async def main() -> None: """Run.""" api = API("") sensors = await api.sensors.async_get_nearby_sensors( ["name"], 51.5285582, -0.2416796, 10 ) # >>> [NearbySensorResult(...), NearbySensorResult(...)] asyncio.run(main()) ``` ### Method Parameters - `fields` (required): The sensor data fields to include - `latitude` (required): The latitude of the point to measure distance from - `longitude` (required): The longitude of the point to measure distance from - `distance` (required): The distance from the measured point to search (in kilometers) - `limit` (optional): Limit the results ## Getting a Map URL If you need to get the URL to a particular sensor index on the PurpleAir map website, simply pass the appropriate sensor index to the `get_map_url` method: ```python import asyncio from aiopurpleair import API async def main() -> None: """Run.""" api = API("") map_url = api.get_map_url(12345) # >>> https://map.purpleair.com/1/mAQI/a10/p604800/cC0?select=12345 asyncio.run(main()) ``` ## Connection Pooling By default, the library creates a new connection to the PurpleAir API with each coroutine. If you are calling a large number of coroutines (or merely want to squeeze out every second of runtime savings possible), an [`aiohttp`][aiohttp] `ClientSession` can be used for connection pooling: ```python import asyncio from aiohttp import ClientSession from aiopurpleair import API async def main() -> None: """Run.""" async with ClientSession() as session: api = await API("") # Get to work... asyncio.run(main()) ``` # Contributing Thanks to all of [our contributors][contributors] so far! 1. [Check for open features/bugs][issues] or [initiate a discussion on one][new-issue]. 2. [Fork the repository][fork]. 3. (_optional, but highly recommended_) Create a virtual environment: `python3 -m venv .venv` 4. (_optional, but highly recommended_) Enter the virtual environment: `source ./.venv/bin/activate` 5. Install the dev environment: `script/setup` 6. Code your new feature or bug fix on a new branch. 7. Write tests that cover your new functionality. 8. Run tests and ensure 100% code coverage: `poetry run pytest --cov aiopurpleair tests` 9. Update `README.md` with any new documentation. 10. Submit a pull request! [aiohttp]: https://github.com/aio-libs/aiohttp [ci-badge]: https://github.com/bachya/aiopurpleair/workflows/CI/badge.svg [ci]: https://github.com/bachya/aiopurpleair/actions [codecov-badge]: https://codecov.io/gh/bachya/aiopurpleair/branch/dev/graph/badge.svg [codecov]: https://codecov.io/gh/bachya/aiopurpleair [contributors]: https://github.com/bachya/aiopurpleair/graphs/contributors [fork]: https://github.com/bachya/aiopurpleair/fork [issues]: https://github.com/bachya/aiopurpleair/issues [license-badge]: https://img.shields.io/pypi/l/aiopurpleair.svg [license]: https://github.com/bachya/aiopurpleair/blob/main/LICENSE [maintainability-badge]: https://api.codeclimate.com/v1/badges/40e0f45570a0eb9aab24/maintainability [maintainability]: https://codeclimate.com/github/bachya/aiopurpleair/maintainability [new-issue]: https://github.com/bachya/aiopurpleair/issues/new [new-issue]: https://github.com/bachya/aiopurpleair/issues/new [notion]: https://getnotion.com [purpleair-api]: https://api.purpleair.com/#api-welcome [purpleair]: https://www2.purpleair.com/ [pypi-badge]: https://img.shields.io/pypi/v/aiopurpleair.svg [pypi]: https://pypi.python.org/pypi/aiopurpleair [version-badge]: https://img.shields.io/pypi/pyversions/aiopurpleair.svg [version]: https://pypi.python.org/pypi/aiopurpleair bachya-aiopurpleair-f1ba236/aiopurpleair/000077500000000000000000000000001451140715600205415ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/aiopurpleair/__init__.py000066400000000000000000000001041451140715600226450ustar00rootroot00000000000000"""Define the aiopurpleair package.""" from .api import API # noqa bachya-aiopurpleair-f1ba236/aiopurpleair/api.py000066400000000000000000000066631451140715600216770ustar00rootroot00000000000000"""Define an API client.""" from __future__ import annotations from typing import Any, cast from aiohttp import ClientSession, ClientTimeout from aiohttp.client_exceptions import ClientError from pydantic import ValidationError from aiopurpleair.const import LOGGER from aiopurpleair.endpoints.sensors import SensorsEndpoints from aiopurpleair.errors import RequestError, raise_error from aiopurpleair.helpers.model import PurpleAirBaseModel, PurpleAirBaseModelT from aiopurpleair.models.keys import GetKeysResponse API_URL_BASE = "https://api.purpleair.com/v1" DEFAULT_TIMEOUT = 10 MAP_URL_BASE = "https://map.purpleair.com/1/mAQI/a10/p604800/cC0" class API: """Define the API object.""" def __init__( self, api_key: str, *, session: ClientSession | None = None, ) -> None: """Initialize. Args: api_key: A PurpleAir API key. session: An optional aiohttp ClientSession. """ self._api_key = api_key self._session = session self.sensors = SensorsEndpoints(self.async_request) async def async_check_api_key(self) -> GetKeysResponse: """Check the validity of the API key. Returns: An API response payload. """ return await self.async_request("get", "/keys", GetKeysResponse) async def async_request( self, method: str, endpoint: str, response_model: type[PurpleAirBaseModel], **kwargs: dict[str, Any], ) -> PurpleAirBaseModelT: """Make an API request. Args: method: An HTTP method. endpoint: A relative API endpoint. response_model: A Pydantic model to parse the response data with. **kwargs: Additional kwargs to send with the request. Returns: An API response payload in the form of a Pydantic model. Raises: RequestError: Raised when response data can't be validated. """ url: str = f"{API_URL_BASE}{endpoint}" kwargs.setdefault("headers", {}) if self._api_key: kwargs["headers"]["X-API-Key"] = self._api_key if use_running_session := self._session and not self._session.closed: session = self._session else: session = ClientSession(timeout=ClientTimeout(total=DEFAULT_TIMEOUT)) data: dict[str, Any] = {} try: async with session.request(method, url, **kwargs) as resp: data = await resp.json() raising_err = None try: resp.raise_for_status() except ClientError as err: raising_err = err raise_error(resp, data, raising_err) finally: if not use_running_session: await session.close() LOGGER.debug("Data received for %s: %s", endpoint, data) try: return cast(PurpleAirBaseModelT, response_model.model_validate(data)) except ValidationError as err: raise RequestError( f"Error while parsing response from {endpoint}: {err}" ) from err def get_map_url(self, sensor_index: int) -> str: """Get the map URL for a sensor index. Args: sensor_index: The sensor index to get the map URL for. Returns: A MAP URL. """ return f"{MAP_URL_BASE}?select={sensor_index}" bachya-aiopurpleair-f1ba236/aiopurpleair/backports/000077500000000000000000000000001451140715600225315ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/aiopurpleair/backports/__init__.py000066400000000000000000000000301451140715600246330ustar00rootroot00000000000000"""Define backports.""" bachya-aiopurpleair-f1ba236/aiopurpleair/backports/enum.py000066400000000000000000000032451451140715600240530ustar00rootroot00000000000000"""Define enum backports from the standard library.""" from __future__ import annotations from enum import Enum from typing import Any, TypeVar _StrEnumSelfT = TypeVar("_StrEnumSelfT", bound="StrEnum") class StrEnum(str, Enum): """Define a partial backport of Python 3.11's StrEnum.""" def __new__( cls: type[_StrEnumSelfT], value: str, *args: Any, **kwargs: Any ) -> _StrEnumSelfT: """Create a new StrEnum instance. Args: value: The enum value. args: Additional args. kwargs: Additional kwargs. Returns: The enum. Raises: TypeError: Raised when an enumerated value isn't a string. """ if not isinstance(value, str): raise TypeError(f"{value!r} is not a string") return super().__new__(cls, value, *args, **kwargs) def __str__(self) -> str: """Return self.value. Returns: The string value. """ return str(self.value) @staticmethod def _generate_next_value_( name: str, start: int, count: int, last_values: list[Any], ) -> Any: """Make `auto()` explicitly unsupported. We may revisit this when it's very clear that Python 3.11's `StrEnum.auto()` behavior will no longer change. Args: name: The name of the enum. start: The starting index. count: The total number of enumerated values. last_values: Previously enumerated values. Raises: TypeError: Always raised. """ raise TypeError("auto() is not supported by this implementation") bachya-aiopurpleair-f1ba236/aiopurpleair/const.py000066400000000000000000000056111451140715600222440ustar00rootroot00000000000000"""Define package constants.""" import logging from enum import Enum LOGGER = logging.getLogger(__package__) class ChannelFlag(Enum): """Define a channel flag.""" NORMAL = 0 A_DOWNGRADED = 1 B_DOWNGRADED = 2 A_B_DOWNGRADED = 3 class ChannelState(Enum): """Define a channel state.""" NO_PM = 0 PM_A = 1 PM_B = 2 PM_A_PM_B = 3 class LocationType(Enum): """Define a location type.""" OUTSIDE = 0 INSIDE = 1 SENSOR_FIELDS = { "0.3_um_count", "0.3_um_count_a", "0.3_um_count_b", "0.5_um_count", "0.5_um_count_a", "0.5_um_count_b", "1.0_um_count", "1.0_um_count_a", "1.0_um_count_b", "10.0_um_count", "10.0_um_count_a", "10.0_um_count_b", "2.5_um_count", "2.5_um_count_a", "2.5_um_count_b", "5.0_um_count", "5.0_um_count_a", "5.0_um_count_b", "altitude", "analog_input", "channel_flags", "channel_flags_auto", "channel_flags_manual", "channel_state", "confidence", "confidence_auto", "confidence_manual", "date_created", "deciviews", "deciviews_a", "deciviews_b", "firmware_upgrade", "firmware_version", "hardware", "humidity", "humidity_a", "humidity_b", "icon", "last_modified", "last_seen", "latitude", "led_brightness", "location_type", "longitude", "memory", "model", "name", "ozone1", "pa_latency", "pm1.0", "pm1.0_a", "pm1.0_atm", "pm1.0_atm_a", "pm1.0_atm_b", "pm1.0_b", "pm1.0_cf_1", "pm1.0_cf_1_a", "pm1.0_cf_1_b", "pm10.0", "pm10.0_a", "pm10.0_atm", "pm10.0_atm_a", "pm10.0_atm_b", "pm10.0_b", "pm10.0_cf_1", "pm10.0_cf_1_a", "pm10.0_cf_1_b", "pm2.5", "pm2.5_10minute", "pm2.5_10minute_a", "pm2.5_10minute_b", "pm2.5_1week", "pm2.5_1week_a", "pm2.5_1week_b", "pm2.5_24hour", "pm2.5_24hour_a", "pm2.5_24hour_b", "pm2.5_30minute", "pm2.5_30minute_a", "pm2.5_30minute_b", "pm2.5_60minute", "pm2.5_60minute_a", "pm2.5_60minute_b", "pm2.5_6hour", "pm2.5_6hour_a", "pm2.5_6hour_b", "pm2.5_a", "pm2.5_alt", "pm2.5_alt_a", "pm2.5_alt_b", "pm2.5_atm", "pm2.5_atm_a", "pm2.5_atm_b", "pm2.5_b", "pm2.5_cf_1", "pm2.5_cf_1_a", "pm2.5_cf_1_b", "position_rating", "pressure", "pressure_a", "pressure_b", "primary_id_a", "primary_id_b", "primary_key_a", "primary_key_b", "private", "rssi", "scattering_coefficient", "scattering_coefficient_a", "scattering_coefficient_b", "secondary_id_a", "secondary_id_b", "secondary_key_a", "secondary_key_b", "sensor_index", "temperature", "temperature_a", "temperature_b", "uptime", "visual_range", "visual_range_a", "visual_range_b", "voc", "voc_a", "voc_b", } bachya-aiopurpleair-f1ba236/aiopurpleair/endpoints/000077500000000000000000000000001451140715600225445ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/aiopurpleair/endpoints/__init__.py000066400000000000000000000037511451140715600246630ustar00rootroot00000000000000"""Define API endpoints.""" from __future__ import annotations from collections.abc import Awaitable, Callable, Iterable from typing import Any from pydantic import ValidationError from aiopurpleair.errors import InvalidRequestError from aiopurpleair.helpers.model import PurpleAirBaseModel, PurpleAirBaseModelT class APIEndpointsBase: # pylint: disable=too-few-public-methods """Define a base API endpoints manager.""" def __init__( self, async_request: Callable[..., Awaitable[PurpleAirBaseModelT]] ) -> None: """Initialize. Args: async_request: The request method from the API object. """ self._async_request = async_request async def _async_endpoint_request_with_models( self, endpoint: str, query_param_map: Iterable[tuple[str, Any]], request_model: type[PurpleAirBaseModel], response_model: type[PurpleAirBaseModel], ) -> PurpleAirBaseModelT: """Perform an API endpoint request. Args: endpoint: The API endpoint to query. query_param_map: A tuple of API query parameters to include (if they exist). request_model: The Pydantic model for the request. response_model: The Pydantic model for the response. Returns: An API response payload in the form of a Pydantic model. Raises: InvalidRequestError: Raised on invalid parameters. """ try: request = request_model.model_validate( { api_query_param: func_param for api_query_param, func_param in query_param_map if func_param is not None } ) except ValidationError as err: raise InvalidRequestError(err) from err return await self._async_request( "get", endpoint, response_model, params=request.model_dump(exclude_none=True), ) bachya-aiopurpleair-f1ba236/aiopurpleair/endpoints/sensors.py000066400000000000000000000133061451140715600246150ustar00rootroot00000000000000"""Define an API endpoint for requests related to sensors.""" from __future__ import annotations from dataclasses import dataclass from datetime import datetime from typing import cast from aiopurpleair.endpoints import APIEndpointsBase from aiopurpleair.models.sensors import ( GetSensorRequest, GetSensorResponse, GetSensorsRequest, GetSensorsResponse, LocationType, SensorModel, ) from aiopurpleair.util.geo import GeoLocation @dataclass class NearbySensorResult: """Define a nearby sensor result.""" sensor: SensorModel distance: float class SensorsEndpoints(APIEndpointsBase): """Define the API manager object.""" async def async_get_sensor( self, sensor_index: int, *, fields: list[str] | None = None, read_key: str | None = None, ) -> GetSensorResponse: """Get all sensors. Args: sensor_index: The sensor index to get data for. fields: The optional sensor data fields to include. read_key: An optional read key for private sensors. Returns: An API response payload in the form of a Pydantic model. """ response: GetSensorResponse = await self._async_endpoint_request_with_models( f"/sensors/{sensor_index}", ( ("fields", fields), ("read_key", read_key), ), GetSensorRequest, GetSensorResponse, ) return response async def async_get_sensors( # pylint: disable=too-many-arguments self, fields: list[str], *, location_type: LocationType | None = None, max_age: int | None = None, modified_since_utc: datetime | None = None, nw_latitude: float | None = None, nw_longitude: float | None = None, read_keys: list[str] | None = None, se_latitude: float | None = None, se_longitude: float | None = None, sensor_indices: list[int] | None = None, ) -> GetSensorsResponse: """Get all sensors. Args: fields: The sensor data fields to include. location_type: An optional LocationType to filter by. max_age: Filter results modified within these seconds. modified_since_utc: Filter results modified since a datetime. nw_latitude: The latitude of the NE corner of an optional bounding box. nw_longitude: The longitude of the NE corner of an optional bounding box. read_keys: Optional read keys for private sensors. se_latitude: The latitude of the SE corner of an optional bounding box. se_longitude: The longitude of the SE corner of an optional bounding box. sensor_indices: Filter results by sensor index. Returns: An API response payload in the form of a Pydantic model. """ response: GetSensorsResponse = await self._async_endpoint_request_with_models( "/sensors", ( ("fields", fields), ("location_type", location_type), ("max_age", max_age), ("modified_since", modified_since_utc), ("nwlat", nw_latitude), ("nwlng", nw_longitude), ("read_keys", read_keys), ("selat", se_latitude), ("selng", se_longitude), ("show_only", sensor_indices), ), GetSensorsRequest, GetSensorsResponse, ) return response async def async_get_nearby_sensors( # pylint: disable=too-many-arguments self, fields: list[str], latitude: float, longitude: float, distance_km: float, *, limit_results: int | None = None, ) -> list[NearbySensorResult]: """Get sensors near a coordinate pair within a distance (in kilometers). The resulting list of sensors is ordered from nearest to furthest within the bounding box defined by the distance. Args: fields: The sensor data fields to include. latitude: The latitude of the "search center." longitude: The longitude of the "search center." distance_km: The radius of the "search center." limit_results: The number of results to limit. Returns: A sorted list of NearbySensorResult objects (containing both the sensor and the distance). """ center = GeoLocation.from_degrees(latitude, longitude) nw_coordinate_pair, se_coordinate_pair = center.bounding_box(distance_km) # Ensure that latitude and longitude are included in the fields no matter what: fields.extend( field for field in ("latitude", "longitude") if field not in fields ) sensors_response = await self.async_get_sensors( fields, nw_latitude=nw_coordinate_pair.latitude_degrees, nw_longitude=nw_coordinate_pair.longitude_degrees, se_latitude=se_coordinate_pair.latitude_degrees, se_longitude=se_coordinate_pair.longitude_degrees, ) nearby_results = [ NearbySensorResult( sensor=sensor, distance=center.distance_to( GeoLocation.from_degrees( cast(float, sensor.latitude), cast(float, sensor.longitude) ) ), ) for sensor in list(sensors_response.data.values()) ] sorted_results = sorted(nearby_results, key=lambda result: result.distance) if limit_results: return sorted_results[:limit_results] return sorted_results bachya-aiopurpleair-f1ba236/aiopurpleair/errors.py000066400000000000000000000026011451140715600224260ustar00rootroot00000000000000"""Define package exceptions.""" from __future__ import annotations from typing import Any from aiohttp import ClientResponse from aiohttp.client_exceptions import ClientError class PurpleAirError(Exception): """Define a base exception.""" pass class NotFoundError(PurpleAirError): """Define an unknown resource.""" pass class InvalidRequestError(PurpleAirError): """Define an invalid request.""" pass class RequestError(PurpleAirError): """Define a general HTTP request error.""" pass class InvalidApiKeyError(RequestError): """Define a base exception.""" pass ERROR_CODE_MAP = { "ApiKeyMissingError": InvalidApiKeyError, "ApiKeyInvalidError": InvalidApiKeyError, "NotFoundError": NotFoundError, } def raise_error( resp: ClientResponse, payload: dict[str, Any], raising_err: ClientError | None ) -> None: """Raise the appropriate error based on the response data. Args: resp: An aiohttp ClientResponse. payload: An API response payload. raising_err: The aiohttp ClientError that caused the overall issue. Raises: exc: Raised upon an HTTP error. """ if (error_code := payload.get("error")) is None: return exc = ERROR_CODE_MAP.get(error_code, RequestError) exc.__cause__ = raising_err raise exc(f"Error while querying {resp.url}: {payload['description']}") bachya-aiopurpleair-f1ba236/aiopurpleair/helpers/000077500000000000000000000000001451140715600222035ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/aiopurpleair/helpers/__init__.py000066400000000000000000000000261451140715600243120ustar00rootroot00000000000000"""Define helpers.""" bachya-aiopurpleair-f1ba236/aiopurpleair/helpers/model.py000066400000000000000000000004711451140715600236570ustar00rootroot00000000000000"""Define model helpers.""" from typing import TypeVar from pydantic import BaseModel, ConfigDict class PurpleAirBaseModel(BaseModel): """Define a PurpleAir-specific base model.""" model_config = ConfigDict(frozen=True) PurpleAirBaseModelT = TypeVar("PurpleAirBaseModelT", bound=PurpleAirBaseModel) bachya-aiopurpleair-f1ba236/aiopurpleair/helpers/validator/000077500000000000000000000000001451140715600241705ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/aiopurpleair/helpers/validator/__init__.py000066400000000000000000000005711451140715600263040ustar00rootroot00000000000000"""Define Pydantic validator helpers.""" from datetime import datetime, timezone def validate_timestamp(value: int) -> datetime: """Validate a timestamp. Args: value: An integer (epoch datetime) to evaluate. Returns: A parsed datetime.datetime object (UTC). """ return datetime.fromtimestamp(value, tz=timezone.utc).replace(tzinfo=None) bachya-aiopurpleair-f1ba236/aiopurpleair/helpers/validator/sensors.py000066400000000000000000000032361451140715600262420ustar00rootroot00000000000000"""Define reusable Pydantic validors for sensors.""" from aiopurpleair.const import SENSOR_FIELDS, ChannelFlag def validate_channel_flag(value: int) -> ChannelFlag: """Validate the channel flag. Args: value: The integer-based interpretation of a channel flag. Returns: A ChannelFlag value. Raises: ValueError: Raised upon an unknown location type. """ try: return ChannelFlag(value) except ValueError as err: raise ValueError(f"{value} is an unknown channel flag") from err def validate_fields_request(value: list[str]) -> str: """Validate sensor fields for a request payload. Args: value: A list of field strings. Returns: A comma-separate string of fields. Raises: ValueError: An invalid field was provided. """ for field in value: if field not in SENSOR_FIELDS: raise ValueError(f"{field} is an unknown field") return ",".join(value) def validate_latitude(value: float) -> float: """Validate a latitude. Args: value: An float to evaluate. Returns: The float, if valid. Raises: ValueError: Raised on an invalid latitude. """ if value < -90 or value > 90: raise ValueError(f"{value} is an invalid latitude") return value def validate_longitude(value: float) -> float: """Validate a longitude. Args: value: An float to evaluate. Returns: The float, if valid. Raises: ValueError: Raised on an invalid longitude. """ if value < -180 or value > 180: raise ValueError(f"{value} is an invalid longitude") return value bachya-aiopurpleair-f1ba236/aiopurpleair/models/000077500000000000000000000000001451140715600220245ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/aiopurpleair/models/__init__.py000066400000000000000000000000321451140715600241300ustar00rootroot00000000000000"""Define data models.""" bachya-aiopurpleair-f1ba236/aiopurpleair/models/keys.py000066400000000000000000000025051451140715600233530ustar00rootroot00000000000000"""Define request and response models for keys.""" from __future__ import annotations from datetime import datetime from pydantic import Field, field_validator from aiopurpleair.backports.enum import StrEnum from aiopurpleair.helpers.model import PurpleAirBaseModel from aiopurpleair.helpers.validator import validate_timestamp class ApiKeyType(StrEnum): """Define an API key type.""" READ = "READ" READ_DISABLED = "READ_DISABLED" UNKNOWN = "UNKNOWN" WRITE = "WRITE" WRITE_DISABLED = "WRITE_DISABLED" class GetKeysResponse(PurpleAirBaseModel): """Define a response to GET /v1/keys.""" api_key_type: str api_version: str timestamp_utc: datetime = Field(alias="time_stamp") @field_validator("api_key_type") @classmethod def validate_api_key_type(cls, value: str) -> ApiKeyType: """Validate the API key type. Args: value: An API key to validate. Returns: A parsed ApiKeyType. Raises: ValueError: An invalid API key type was received. """ try: return ApiKeyType(value) except ValueError as err: raise ValueError(f"{value} is an unknown API key type") from err validate_utc_timestamp = field_validator("timestamp_utc", mode="before")( validate_timestamp ) bachya-aiopurpleair-f1ba236/aiopurpleair/models/sensors.py000066400000000000000000000400631451140715600240750ustar00rootroot00000000000000"""Define request and response models for sensors.""" # pylint: disable=too-few-public-methods from __future__ import annotations from datetime import datetime from typing import Any, Optional from pydantic import Field, field_validator, model_validator from aiopurpleair.const import SENSOR_FIELDS, ChannelFlag, ChannelState, LocationType from aiopurpleair.helpers.model import PurpleAirBaseModel from aiopurpleair.helpers.validator import validate_timestamp from aiopurpleair.helpers.validator.sensors import ( validate_channel_flag, validate_fields_request, validate_latitude, validate_longitude, ) from aiopurpleair.util.dt import utc_to_timestamp class SensorModelStats(PurpleAirBaseModel): """Define a model for sensor statistics.""" pm2_5: float = Field(alias="pm2.5") pm2_5_10minute: float = Field(alias="pm2.5_10minute") pm2_5_1week: float = Field(alias="pm2.5_1week") pm2_5_24hour: float = Field(alias="pm2.5_24hour") pm2_5_30minute: float = Field(alias="pm2.5_30minute") pm2_5_60minute: float = Field(alias="pm2.5_60minute") pm2_5_6hour: float = Field(alias="pm2.5_6hour") timestamp_utc: datetime = Field(alias="time_stamp") validate_timestamp_utc = field_validator("timestamp_utc", mode="before")( validate_timestamp ) class SensorModel(PurpleAirBaseModel): """Define a model for a sensor.""" sensor_index: int altitude: Optional[float] = None analog_input: Optional[float] = None channel_flags: Optional[ChannelFlag] = None channel_flags_auto: Optional[ChannelFlag] = None channel_flags_manual: Optional[ChannelFlag] = None channel_state: Optional[ChannelState] = None confidence: Optional[float] = None confidence_auto: Optional[float] = None confidence_manual: Optional[float] = None date_created_utc: Optional[datetime] = Field(alias="date_created", default=None) deciviews: Optional[float] = None deciviews_a: Optional[float] = None deciviews_b: Optional[float] = None firmware_upgrade: Optional[str] = None firmware_version: Optional[str] = None hardware: Optional[str] = None humidity: Optional[float] = None humidity_a: Optional[float] = None humidity_b: Optional[float] = None icon: Optional[int] = None is_owner: Optional[bool] = None last_modified_utc: Optional[datetime] = Field(alias="last_modified", default=None) last_seen_utc: Optional[datetime] = Field(alias="last_seen", default=None) latitude: Optional[float] = None led_brightness: Optional[float] = None location_type: Optional[LocationType] = None longitude: Optional[float] = None memory: Optional[float] = None model: Optional[str] = None name: Optional[str] = None ozone1: Optional[float] = None pa_latency: Optional[int] = None pm0_3_um_count: Optional[float] = Field(alias="0.3_um_count", default=None) pm0_3_um_count_a: Optional[float] = Field(alias="0.3_um_count_a", default=None) pm0_3_um_count_b: Optional[float] = Field(alias="0.3_um_count_b", default=None) pm0_5_um_count: Optional[float] = Field(alias="0.5_um_count", default=None) pm0_5_um_count_a: Optional[float] = Field(alias="0.5_um_count_a", default=None) pm0_5_um_count_b: Optional[float] = Field(alias="0.5_um_count_b", default=None) pm10_0: Optional[float] = Field(alias="pm10.0", default=None) pm10_0_a: Optional[float] = Field(alias="pm10.0_a", default=None) pm10_0_atm: Optional[float] = Field(alias="pm10.0_atm", default=None) pm10_0_atm_a: Optional[float] = Field(alias="pm10.0_atm_a", default=None) pm10_0_atm_b: Optional[float] = Field(alias="pm10.0_atm_b", default=None) pm10_0_b: Optional[float] = Field(alias="pm10.0_b", default=None) pm10_0_cf_1: Optional[float] = Field(alias="pm10.0_cf_1", default=None) pm10_0_cf_1_a: Optional[float] = Field(alias="pm10.0_cf_1_a", default=None) pm10_0_cf_1_b: Optional[float] = Field(alias="pm10.0_cf_1_b", default=None) pm10_0_um_count: Optional[float] = Field(alias="10.0_um_count", default=None) pm10_0_um_count_a: Optional[float] = Field(alias="10.0_um_count_a", default=None) pm10_0_um_count_b: Optional[float] = Field(alias="10.0_um_count_b", default=None) pm1_0: Optional[float] = Field(alias="pm1.0", default=None) pm1_0_a: Optional[float] = Field(alias="pm1.0_a", default=None) pm1_0_atm: Optional[float] = Field(alias="pm1.0_atm", default=None) pm1_0_atm_a: Optional[float] = Field(alias="pm1.0_atm_a", default=None) pm1_0_atm_b: Optional[float] = Field(alias="pm1.0_atm_b", default=None) pm1_0_b: Optional[float] = Field(alias="pm1.0_b", default=None) pm1_0_cf_1: Optional[float] = Field(alias="pm1.0_cf_1", default=None) pm1_0_cf_1_a: Optional[float] = Field(alias="pm1.0_cf_1_a", default=None) pm1_0_cf_1_b: Optional[float] = Field(alias="pm1.0_cf_1_b", default=None) pm1_0_um_count: Optional[float] = Field(alias="1.0_um_count", default=None) pm1_0_um_count_a: Optional[float] = Field(alias="1.0_um_count_a", default=None) pm1_0_um_count_b: Optional[float] = Field(alias="1.0_um_count_b", default=None) pm2_5: Optional[float] = Field(alias="pm2.5", default=None) pm2_5_10minute: Optional[float] = Field(alias="pm2.5_10minute", default=None) pm2_5_10minute_a: Optional[float] = Field(alias="pm2.5_10minute_a", default=None) pm2_5_10minute_b: Optional[float] = Field(alias="pm2.5_10minute_b", default=None) pm2_5_1week: Optional[float] = Field(alias="pm2.5_1week", default=None) pm2_5_1week_a: Optional[float] = Field(alias="pm2.5_1week_a", default=None) pm2_5_1week_b: Optional[float] = Field(alias="pm2.5_1week_b", default=None) pm2_5_24hour: Optional[float] = Field(alias="pm2.5_24hour", default=None) pm2_5_24hour_a: Optional[float] = Field(alias="pm2.5_24hour_a", default=None) pm2_5_24hour_b: Optional[float] = Field(alias="pm2.5_24hour_b", default=None) pm2_5_30minute: Optional[float] = Field(alias="pm2.5_30minute", default=None) pm2_5_30minute_a: Optional[float] = Field(alias="pm2.5_30minute_a", default=None) pm2_5_30minute_b: Optional[float] = Field(alias="pm2.5_30minute_b", default=None) pm2_5_60minute: Optional[float] = Field(alias="pm2.5_60minute", default=None) pm2_5_60minute_a: Optional[float] = Field(alias="pm2.5_60minute_a", default=None) pm2_5_60minute_b: Optional[float] = Field(alias="pm2.5_60minute_b", default=None) pm2_5_6hour: Optional[float] = Field(alias="pm2.5_6hour", default=None) pm2_5_6hour_a: Optional[float] = Field(alias="pm2.5_6hour_a", default=None) pm2_5_6hour_b: Optional[float] = Field(alias="pm2.5_6hour_b", default=None) pm2_5_a: Optional[float] = Field(alias="pm2.5_a", default=None) pm2_5_alt: Optional[float] = Field(alias="pm2.5_alt", default=None) pm2_5_alt_a: Optional[float] = Field(alias="pm2.5_alt_a", default=None) pm2_5_alt_b: Optional[float] = Field(alias="pm2.5_alt_b", default=None) pm2_5_atm: Optional[float] = Field(alias="pm2.5_atm", default=None) pm2_5_atm_a: Optional[float] = Field(alias="pm2.5_atm_a", default=None) pm2_5_atm_b: Optional[float] = Field(alias="pm2.5_atm_b", default=None) pm2_5_b: Optional[float] = Field(alias="pm2.5_b", default=None) pm2_5_cf_1: Optional[float] = Field(alias="pm2.5_cf_1", default=None) pm2_5_cf_1_a: Optional[float] = Field(alias="pm2.5_cf_1_a", default=None) pm2_5_cf_1_b: Optional[float] = Field(alias="pm2.5_cf_1_b", default=None) pm2_5_um_count: Optional[float] = Field(alias="2.5_um_count", default=None) pm2_5_um_count_a: Optional[float] = Field(alias="2.5_um_count_a", default=None) pm2_5_um_count_b: Optional[float] = Field(alias="2.5_um_count_b", default=None) pm5_0_um_count: Optional[float] = Field(alias="5.0_um_count", default=None) pm5_0_um_count_a: Optional[float] = Field(alias="5.0_um_count_a", default=None) pm5_0_um_count_b: Optional[float] = Field(alias="5.0_um_count_b", default=None) position_rating: Optional[int] = None pressure: Optional[float] = None pressure_a: Optional[float] = None pressure_b: Optional[float] = None primary_id_a: Optional[int] = None primary_id_b: Optional[int] = None primary_key_a: Optional[str] = None primary_key_b: Optional[str] = None private: Optional[bool] = None rssi: Optional[int] = None scattering_coefficient: Optional[float] = None scattering_coefficient_a: Optional[float] = None scattering_coefficient_b: Optional[float] = None secondary_id_a: Optional[int] = None secondary_id_b: Optional[int] = None secondary_key_a: Optional[str] = None secondary_key_b: Optional[str] = None stats: Optional[SensorModelStats] = None stats_a: Optional[SensorModelStats] = None stats_b: Optional[SensorModelStats] = None temperature: Optional[float] = None temperature_a: Optional[float] = None temperature_b: Optional[float] = None uptime: Optional[int] = None visual_range: Optional[float] = None visual_range_a: Optional[float] = None visual_range_b: Optional[float] = None voc: Optional[float] = None voc_a: Optional[float] = None voc_b: Optional[float] = None validate_channel_flags = field_validator("channel_flags", mode="before")( validate_channel_flag ) validate_channel_flags_auto = field_validator("channel_flags_auto", mode="before")( validate_channel_flag ) validate_channel_flags_manual = field_validator( "channel_flags_manual", mode="before" )(validate_channel_flag) @field_validator("channel_state", mode="before") @classmethod def validate_channel_state(cls, value: int) -> ChannelState: """Validate the channel state. Args: value: The integer-based interpretation of a channel state. Returns: A ChannelState value. Raises: ValueError: Raised upon an unknown location type. """ try: return ChannelState(value) except ValueError as err: raise ValueError(f"{value} is an unknown channel state") from err validate_date_created_utc = field_validator("date_created_utc", mode="before")( validate_timestamp ) validate_last_modified_utc = field_validator("last_modified_utc", mode="before")( validate_timestamp ) validate_last_seen_utc = field_validator("last_seen_utc", mode="before")( validate_timestamp ) validate_latitude = field_validator("latitude")(validate_latitude) @field_validator("location_type", mode="before") @classmethod def validate_location_type_response(cls, value: int) -> LocationType: """Validate a location type for a request payload. Args: value: The integer-based interpretation of a location type. Returns: A LocationType value. Raises: ValueError: Raised upon an unknown location type. """ try: return LocationType(value) except ValueError as err: raise ValueError(f"{value} is an unknown location type") from err validate_longitude = field_validator("longitude")(validate_longitude) class GetSensorRequest(PurpleAirBaseModel): """Define a request to GET /v1/sensors/:sensor_index.""" fields: Optional[str] = None read_key: Optional[str] = None validate_fields = field_validator("fields", mode="before")(validate_fields_request) class GetSensorResponse(PurpleAirBaseModel): """Define a response to GET /v1/sensors/:sensor_index.""" api_version: str sensor: SensorModel data_timestamp_utc: datetime = Field(alias="data_time_stamp") timestamp_utc: datetime = Field(alias="time_stamp") validate_data_timestamp_utc = field_validator("data_timestamp_utc", mode="before")( validate_timestamp ) validate_timestamp_utc = field_validator("timestamp_utc", mode="before")( validate_timestamp ) class GetSensorsRequest(PurpleAirBaseModel): """Define a request to GET /v1/sensors.""" fields: str location_type: Optional[LocationType] = None max_age: Optional[int] = None modified_since: Optional[datetime] = Field(alias="modified_since_utc", default=None) nwlat: Optional[float] = None nwlng: Optional[float] = None read_keys: Optional[list[str]] = None selat: Optional[float] = None selng: Optional[float] = None show_only: Optional[list[int]] = None @model_validator(mode="before") @classmethod def validate_bounding_box_missing_or_complete( cls, values: dict[str, Any] ) -> dict[str, Any]: """Validate the fields. Args: values: The fields passed into the model. Returns: The fields. Raises: ValueError: Only some of the bounding box coordinates have been provided. """ num_of_keys = len( [ key for key in ("nwlng", "nwlat", "selng", "selat") if values.get(key) is not None ] ) if num_of_keys not in (0, 4): raise ValueError("must pass none or all of the bounding box coordinates") return values validate_fields = field_validator("fields", mode="before")(validate_fields_request) @field_validator("location_type") @classmethod def validate_location_type(cls, value: LocationType) -> int: """Validate the location type. Args: value: A LocationType value. Returns: The integer-based interpretation of a location type. """ return value.value @field_validator("modified_since") @classmethod def validate_modified_since(cls, value: datetime) -> int: """Validate the "modified since" datetime. Args: value: A "modified since" datetime object (in UTC). Returns: The timestamp of the datetime object. """ return round(utc_to_timestamp(value)) validate_nwlat = field_validator("nwlat")(validate_latitude) validate_nwlng = field_validator("nwlng")(validate_longitude) @field_validator("read_keys") @classmethod def validate_read_keys(cls, value: list[str]) -> str: """Validate the read keys. Args: value: A list of read key strings. Returns: A comma-separate string of read keys. """ return ",".join(value) validate_selat = field_validator("selat")(validate_latitude) validate_selng = field_validator("selng")(validate_longitude) @field_validator("show_only") @classmethod def validate_show_only(cls, value: list[int]) -> str: """Validate the sensor ID list by which to filter the results. Args: value: A list of sensor IDs. Returns: A comma-separate string of sensor IDs. """ return ",".join([str(i) for i in value]) class GetSensorsResponse(PurpleAirBaseModel): """Define a response to GET /v1/sensors.""" fields: list[str] data: dict[int, SensorModel] api_version: str firmware_default_version: str max_age: int data_timestamp_utc: datetime = Field(alias="data_time_stamp") timestamp_utc: datetime = Field(alias="time_stamp") @model_validator(mode="before") @classmethod def validate_fields(cls, values: dict[str, Any]) -> dict[str, Any]: """Validate the fields string. Args: values: The response payload. Returns: The response paylioad with validated fields. Raises: ValueError: An invalid API key type was received. """ for field in values["fields"]: if field not in SENSOR_FIELDS: raise ValueError(f"{field} is an unknown field") values["data"] = { sensor_values[0]: SensorModel.model_validate( dict(zip(values["fields"], sensor_values)) # noqa: B905 ) for sensor_values in values["data"] } return values validate_data_timestamp_utc = field_validator("data_timestamp_utc", mode="before")( validate_timestamp ) validate_timestamp_utc = field_validator("timestamp_utc", mode="before")( validate_timestamp ) bachya-aiopurpleair-f1ba236/aiopurpleair/py.typed000066400000000000000000000000001451140715600222260ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/aiopurpleair/util/000077500000000000000000000000001451140715600215165ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/aiopurpleair/util/__init__.py000066400000000000000000000000401451140715600236210ustar00rootroot00000000000000"""Define package utilities.""" bachya-aiopurpleair-f1ba236/aiopurpleair/util/dt.py000066400000000000000000000010331451140715600224740ustar00rootroot00000000000000"""Define datetime utilities.""" from datetime import datetime EPOCHORDINAL = datetime(1970, 1, 1).toordinal() def utc_to_timestamp(utc_dt: datetime) -> float: """Define a fast conversion of a datetime in UTC to a timestamp. Args: utc_dt: A datetime object with a UTC timezone. Returns: A UTC timestamp. """ return ( (utc_dt.toordinal() - EPOCHORDINAL) * 86400 + utc_dt.hour * 3600 + utc_dt.minute * 60 + utc_dt.second + (utc_dt.microsecond / 1000000) ) bachya-aiopurpleair-f1ba236/aiopurpleair/util/geo.py000066400000000000000000000114601451140715600226440ustar00rootroot00000000000000"""Define various geographical utilities.""" from __future__ import annotations import math from dataclasses import dataclass EARTH_RAIDUS_KM = 6378.1 MINIMUM_LATITUDE = math.radians(-90) MAXIMUM_LATITUDE = math.radians(90) MINIMUM_LONGITUDE = math.radians(-180) MAXIMUM_LONGITUDE = math.radians(180) @dataclass class GeoLocation: """Define a representation of a single latitude/longitude coordinate. Inspiration from https://github.com/jfein/PyGeoTools/blob/master/geolocation.py. """ latitude_radians: float longitude_radians: float latitude_degrees: float longitude_degrees: float def __post_init__(self) -> None: """Perform some post-init processing. Raises: ValueError: Raised upon invalid latitude/longitude. """ for kind, value, minimum, maximum in ( ("latitude", self.latitude_radians, MINIMUM_LATITUDE, MAXIMUM_LATITUDE), ("longitude", self.longitude_radians, MINIMUM_LONGITUDE, MAXIMUM_LONGITUDE), ): if value < minimum or value > maximum: raise ValueError(f"Invalid {kind}: {value} radians") @classmethod def from_degrees( cls, latitude_degrees: float, longitude_degrees: float ) -> GeoLocation: """Create a GeoLocation object from a latitude/longitude in degrees. Args: latitude_degrees: A latitude in degrees. longitude_degrees: A longitude in degrees. Returns: A GeoLocation object. """ latitude_radians = math.radians(latitude_degrees) longitude_radians = math.radians(longitude_degrees) return cls( latitude_radians, longitude_radians, latitude_degrees, longitude_degrees ) @classmethod def from_radians( cls, latitude_radians: float, longitude_radians: float ) -> GeoLocation: """Create a GeoLocation object from a latitude/longitude in radians. Args: latitude_radians: A latitude in radians. longitude_radians: A longitude in radians. Returns: A GeoLocation object. """ latitude_degrees = math.degrees(latitude_radians) longitude_degrees = math.degrees(longitude_radians) return cls( latitude_radians, longitude_radians, latitude_degrees, longitude_degrees ) def bounding_box(self, distance_km: float) -> tuple[GeoLocation, GeoLocation]: """Calculate a bounding box a certain distance from this GeoLocation. Args: distance_km: A distance (in kilometers). Returns: Two GeoLocation objects (representing the NW and SE corners of the box). Raises: ValueError: Raised on a negative distance_km parameter. """ if distance_km < 0: raise ValueError("Cannot calculate a bounding box with negative distance") distance_radians = distance_km / EARTH_RAIDUS_KM box_minimum_latitude = self.latitude_radians - distance_radians box_maximum_latitude = self.latitude_radians + distance_radians if MINIMUM_LATITUDE < box_maximum_latitude < MAXIMUM_LATITUDE: delta_longitude = math.asin( math.sin(distance_radians) / math.cos(self.latitude_radians) ) box_minimum_longitude = self.longitude_radians - delta_longitude if box_minimum_longitude < MINIMUM_LONGITUDE: box_minimum_longitude += 2 * math.pi box_maximum_longitude = self.longitude_radians + delta_longitude if box_maximum_longitude > MAXIMUM_LONGITUDE: box_maximum_longitude -= 2 * math.pi else: # One of the poles is within the bounding box: box_minimum_latitude = max(box_minimum_latitude, MINIMUM_LATITUDE) box_maximum_latitude = min(box_maximum_latitude, MAXIMUM_LATITUDE) box_minimum_longitude = MINIMUM_LONGITUDE box_maximum_longitude = MAXIMUM_LONGITUDE return ( GeoLocation.from_radians(box_maximum_latitude, box_minimum_longitude), GeoLocation.from_radians(box_minimum_latitude, box_maximum_longitude), ) def distance_to(self, endpoint: GeoLocation) -> float: """Calculate the great circle distance between this GeoLocation and another. Args: endpoint: The GeoLocation to which the distance should be measured. Returns: The distance between this GeoLocation and the endpoint GeoLocation. """ return EARTH_RAIDUS_KM * math.acos( math.sin(self.latitude_radians) * math.sin(endpoint.latitude_radians) + math.cos(self.latitude_radians) * math.cos(endpoint.latitude_radians) * math.cos(self.longitude_radians - endpoint.longitude_radians) ) bachya-aiopurpleair-f1ba236/examples/000077500000000000000000000000001451140715600176635ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/examples/__init__.py000066400000000000000000000000271451140715600217730ustar00rootroot00000000000000"""Define examples.""" bachya-aiopurpleair-f1ba236/examples/test_api.py000066400000000000000000000016331451140715600220500ustar00rootroot00000000000000"""Run an example script to quickly test.""" import asyncio import logging from aiohttp import ClientSession from aiopurpleair import API from aiopurpleair.errors import PurpleAirError _LOGGER = logging.getLogger() API_KEY = "" # noqa: S105 async def main() -> None: """Create the aiohttp session and run the example.""" logging.basicConfig(level=logging.INFO) async with ClientSession() as session: try: api = API(API_KEY, session=session) sensors_response = await api.sensors.async_get_sensors(["name"]) _LOGGER.info(sensors_response) nearby_sensor_indices = await api.sensors.async_get_nearby_sensors( ["name"], 51.5285582, -0.2416796, 10 ) _LOGGER.info(nearby_sensor_indices) except PurpleAirError as err: _LOGGER.error("There was an error: %s", err) asyncio.run(main()) bachya-aiopurpleair-f1ba236/poetry.lock000066400000000000000000004306071451140715600202530ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "aiohttp" version = "3.9.0b0" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ {file = "aiohttp-3.9.0b0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:50b550b5e317e40a017bab8b25995676af3aa66dd0ef562cd7dce7f1684cd376"}, {file = "aiohttp-3.9.0b0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8f902ad26b9814852e0a17d48f98ba4c879d8136c4fa9b235b5c043dde0a0257"}, {file = "aiohttp-3.9.0b0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c2140de122ecf3eb7947105ceb91fb6632fb21cc1d17f6ff19c3973d2d12730d"}, {file = "aiohttp-3.9.0b0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e014b343225d8d358ee91962b588e863fded12a6e2f9b446bb3be85c678e04ae"}, {file = "aiohttp-3.9.0b0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c7016695087e616a2806ccdb1f83609e5fecb3958c270e3e5a42f69d225536f2"}, {file = "aiohttp-3.9.0b0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40407d5ec81682225ad5538d9bd68b0f8242caa91e72a6a9a95197fd7d9aebb2"}, {file = "aiohttp-3.9.0b0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd54502e6b4144785f2f14a5f1544ced0a77dbecb1fd422f21dfad95dcb7fcb8"}, {file = "aiohttp-3.9.0b0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f911fd2073621eecfe77b17926460e72980b9b996d0ab7dad5e38805ce2988"}, {file = "aiohttp-3.9.0b0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:638ba28af2c821b70574664a991dfdfaf1a7a7ae1a8068757f7d59cdf2d8361a"}, {file = "aiohttp-3.9.0b0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:76329f7c1f5f3185d91d61d64615d88fa3dfddf389a83f6cd46a205c5b61e01b"}, {file = "aiohttp-3.9.0b0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:597b5d44b613dea9c62779592eb0ecae87604628564ecaff8d516457def68184"}, {file = "aiohttp-3.9.0b0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:cd5edd7ba2b3f95346e0fc8ba2364bdd93917a1bf8528e7d60ec80cf21dfba7e"}, {file = "aiohttp-3.9.0b0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72556e0cce47c6e558454316fc5c6a3fb0980344eee8af7aa52b495d82ef12a5"}, {file = "aiohttp-3.9.0b0-cp310-cp310-win32.whl", hash = "sha256:01a3b241288c4d8171fe5e2434a799d0b82700d2ed2156b43f1d7f4f521ba382"}, {file = "aiohttp-3.9.0b0-cp310-cp310-win_amd64.whl", hash = "sha256:17962c404788c348ce5b58efaf4969249183c551890e30bfd9c035188d21e3d1"}, {file = "aiohttp-3.9.0b0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:94197a77859ab1039b9ca6c3c393b8e7b5fc34a9abfbcb58daac38ab89684a99"}, {file = "aiohttp-3.9.0b0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0c78d2cfe1515cfb31ba67edf0518c6677a963ec2039b652b03a886733e72e65"}, {file = "aiohttp-3.9.0b0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28b38a14f564c833e59c99f748b48803e4babeabc6a0307952b01e6c8d642cab"}, {file = "aiohttp-3.9.0b0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e596cfc52380f71e197e7cf0e2d3c4714b4bf66d2d562cdbd5442284bac18909"}, {file = "aiohttp-3.9.0b0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6190951b7933c834d9346e21c5a81642caa210d291cda4036daf85fc53162d35"}, {file = "aiohttp-3.9.0b0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb0cb2cbf95cf4cc40307d0d0187f59c4b86b1d7d1a624922a7d0b046deffba7"}, {file = "aiohttp-3.9.0b0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e27c283e21e94fa1582d31b57c514b87ab609882ade413ce43f585d73c8a33fc"}, {file = "aiohttp-3.9.0b0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6826c59b4e99673728bcdaecacbd699b7521f17ca165c63a5e26e23d42aeea5"}, {file = "aiohttp-3.9.0b0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aa4738f3b1b916b1cc69ed3d1dead9714919dc4d30ae0d5f6d55eadb2c511133"}, {file = "aiohttp-3.9.0b0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b2abd7936f687de3a3ab199b145a9de01ed046eb5640cd66f47da07a9050a78"}, {file = "aiohttp-3.9.0b0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:652cc00a97bc206c470db06276ce57ff2a53a625795bbce8435ef8b6a4cb0113"}, {file = "aiohttp-3.9.0b0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:d54529c1d95d5d200ecb7133a343785e5661a804f3dcee090a7bca3b48189d69"}, {file = "aiohttp-3.9.0b0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:324fe990c97721ea8eb4d439f12b59d1a93cd7e0dd188c7b145bffdfbd327dc3"}, {file = "aiohttp-3.9.0b0-cp311-cp311-win32.whl", hash = "sha256:3a2ef8318435f40f5906af36fda20b5432e07e6a7e05de3a4d2934c25320b8ff"}, {file = "aiohttp-3.9.0b0-cp311-cp311-win_amd64.whl", hash = "sha256:887d8757aafc7f6fbda76faaff21fc2aa31b9dca0911ecd6b60b0fe922a2abfc"}, {file = "aiohttp-3.9.0b0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9c430c706589a811b38e33e1492d194cbb0f6f2e027877bf038debced703446f"}, {file = "aiohttp-3.9.0b0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b820981f1c5d6da382e4859318ba78c9b5c583f0920e44a18efb3387b18487e"}, {file = "aiohttp-3.9.0b0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c64677a2df742bcd89b94c35689306663d8246a8534bea5835afc706416f8dd6"}, {file = "aiohttp-3.9.0b0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:903155c179cda589d01936953158685747af43d98cdd3673a671c6e7f5c94178"}, {file = "aiohttp-3.9.0b0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77cbb6e4a146449f805fa0e725b0b2a06411d21417d8eca699bbee55204201d0"}, {file = "aiohttp-3.9.0b0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc3cc9f5e6e493a2b9c3d241fca870b5a64aa4c247f1192f9e34fae990667df8"}, {file = "aiohttp-3.9.0b0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92071206e570b7da6380f8d376820e2a40230638b8fd8b45b28103b346704c5e"}, {file = "aiohttp-3.9.0b0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:242e3cb0b2d441a2d20443114eebe3032078d1894ac1d97ab2dd101165ea50e1"}, {file = "aiohttp-3.9.0b0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:044c5a8923bd44a4a0769a2886130c19f7f3a4a1a284f0ff68c2a751920ee39f"}, {file = "aiohttp-3.9.0b0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b1b0d0f63ff48f80aa89be3ff61bc2b980c5b02895c81dbc1e44ce7b6cb5b7"}, {file = "aiohttp-3.9.0b0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:f737a47b5df97b6da457a0b2739d6d819ffadea2f36336988b53dbdb1796ba89"}, {file = "aiohttp-3.9.0b0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:e6d79f8b8347afbecd8047a1f6e74c810eb82497256cc906ee384635174dcaea"}, {file = "aiohttp-3.9.0b0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2f1b0a821564e315ec5cfa0abaf048355e229995a812380ec7a2200d87a6ed11"}, {file = "aiohttp-3.9.0b0-cp312-cp312-win32.whl", hash = "sha256:ab2702f281ca504529e82be78dae2b9ca31d51a92ab8b239bd326b74c79d7af4"}, {file = "aiohttp-3.9.0b0-cp312-cp312-win_amd64.whl", hash = "sha256:b81722b88abd4aab656abfec122646b6171da64340ff92af3bcf1af5f0d1275e"}, {file = "aiohttp-3.9.0b0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:49e2ca017f506d1a9c60f44301ceff2eb8bbfe24b9cd9b4c4a363d9e5f68e92b"}, {file = "aiohttp-3.9.0b0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06cba5518d8e30b46fcec2a8ed22ec6027fc9864583e0b538da642507f66fe29"}, {file = "aiohttp-3.9.0b0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e5201d3f8d0b2748eba5093820861639cac1ea1dfdff537f67152a1c082e1243"}, {file = "aiohttp-3.9.0b0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c483d0a666f6cbec2e974f760f93499bbcfcb17a7c4035d4c4c653e6a3b21b1"}, {file = "aiohttp-3.9.0b0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04f48476ce3e96843b44084fd15139b195781c10ed6eb5ffb706fb9d2ca95ce4"}, {file = "aiohttp-3.9.0b0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09fdad08544a4479e5801c777697c155fa9d966c91b6dcf3e1a0d271ad3999f7"}, {file = "aiohttp-3.9.0b0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:127aa57415005eb04fb1a3685c9d7b42aef6718be72b8a62b4b30ee00f7d23f4"}, {file = "aiohttp-3.9.0b0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa8f29f0647f10f6bcd9f597f1319d13ce1d6efe2d55169226940093eeadf609"}, {file = "aiohttp-3.9.0b0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8dc394dea47594825ac2a662c4fac6a8b294acd937396aaec8e41ed03728898b"}, {file = "aiohttp-3.9.0b0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c332b343974c6fbfec53e3ac7afebd6ba6cc1777cda67c28fabb3562411a9b5a"}, {file = "aiohttp-3.9.0b0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6dfad718b328de3fa30d663393d51feea625322ec723bdecdec3f5f52ba6347f"}, {file = "aiohttp-3.9.0b0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:6edaeb63a4657672b04afcc25c253e960125e805f5a8f8cfa7bf682d15115f49"}, {file = "aiohttp-3.9.0b0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:20023087bce5f3adde4872042ea1193d31d98b29682c28a6309d72bce0d9725e"}, {file = "aiohttp-3.9.0b0-cp38-cp38-win32.whl", hash = "sha256:ad07ee4165a82e646310c152a74997c759d5782aef58bab9d77034b4cc87e153"}, {file = "aiohttp-3.9.0b0-cp38-cp38-win_amd64.whl", hash = "sha256:494062a8447c6665f5237c47ca8bb5659cd3128ad9b4af5543566a11bb88df5c"}, {file = "aiohttp-3.9.0b0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:aaff57bd1ab9eb1a205f3b7a00e2dc159d1e7e4373870be0d192358a656d9e60"}, {file = "aiohttp-3.9.0b0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c212f5066ffe9490856b706a9d9bd457f14716f4db4b1b73939245a1acecc4e"}, {file = "aiohttp-3.9.0b0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d80664b3b82fb9ee2c7b13072651cd68d65fbb3a69721040c08969bab4335628"}, {file = "aiohttp-3.9.0b0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7cf539fc98297e312308405949ca2f04a347eb021e30d004388cdb5d155a0ec"}, {file = "aiohttp-3.9.0b0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6be520717b895508c63df90e48135ba616c702a9229d4be71841dce2ea6a569f"}, {file = "aiohttp-3.9.0b0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b25e926cd16b44aeef29fffbb9fc9f577f52a6230e46926e391545b85cd0ce3"}, {file = "aiohttp-3.9.0b0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35f6cafe361c0323945c13122c282ea22fb0df96e845f34c4d8abd96e2a81995"}, {file = "aiohttp-3.9.0b0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5c9851e3d0396686d96a7e3559bf5912ed79c944ff1a6ae3cf7b1da320c3ad2b"}, {file = "aiohttp-3.9.0b0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ab413eddeb1a03ba84d06acf7024a646b049d991ed0616bcc1ee40dc8fffa9e"}, {file = "aiohttp-3.9.0b0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:89b271a8658472a9d400836ee8caee743246bae5c06405a63b6ba366f58df727"}, {file = "aiohttp-3.9.0b0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:dd941d473b86d0d5a413a1832499e5b80f648d66ca0c8246c26a4ccd66bcf7ec"}, {file = "aiohttp-3.9.0b0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:ce4f000279fb85527c017ef429615f2cb5a0cb614c088610849ddc6c2ac8d91b"}, {file = "aiohttp-3.9.0b0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f50a4f6773a9eedefb24b42c611e31dcd13f6139419a8656f7e525cb8a00687e"}, {file = "aiohttp-3.9.0b0-cp39-cp39-win32.whl", hash = "sha256:b14dcfcc5ad161d007da71e1c1211909d527d9d7c2795ea9e17191ba25e5d89a"}, {file = "aiohttp-3.9.0b0-cp39-cp39-win_amd64.whl", hash = "sha256:567245a91a57c41899f5d266814c9da8782d3d949dc1e66469429f08713a3ec6"}, {file = "aiohttp-3.9.0b0.tar.gz", hash = "sha256:cecc64fd7bae6debdf43437e3c83183c40d4f4d86486946f412c113960598eee"}, ] [package.dependencies] aiosignal = ">=1.1.2" async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" yarl = ">=1.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns", "brotlicffi"] [[package]] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.7" files = [ {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, ] [package.dependencies] frozenlist = ">=1.1.0" [[package]] name = "annotated-types" version = "0.5.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.7" files = [ {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"}, {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"}, ] [[package]] name = "aresponses" version = "2.1.6" description = "Asyncio response mocking. Similar to the responses library used for 'requests'" optional = false python-versions = ">=3.6" files = [ {file = "aresponses-2.1.6-py3-none-any.whl", hash = "sha256:06525f6911063f0f8d370cbc96bd273e6cddc89c7b5163ddf91e0c8abf148a32"}, {file = "aresponses-2.1.6.tar.gz", hash = "sha256:231dfa0756e39ca9f1e82212038f98e773d1ed9c0993caf2667e25ba535697ca"}, ] [package.dependencies] aiohttp = ">=3.1.0,<4.dev0" pytest-asyncio = "*" [[package]] name = "astroid" version = "3.0.0" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ {file = "astroid-3.0.0-py3-none-any.whl", hash = "sha256:f2510e7fdcd6cfda4ec50014726d4857abf79acfc010084ce8c26091913f1b25"}, {file = "astroid-3.0.0.tar.gz", hash = "sha256:1defdbca052635dd29657ea674edfc45e4b5be9cd53630c5b084fcfed94344a8"}, ] [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} [[package]] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.7" files = [ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [[package]] name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[docs,tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] [[package]] name = "bandit" version = "1.7.5" description = "Security oriented static analyser for python code." optional = false python-versions = ">=3.7" files = [ {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, ] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} GitPython = ">=1.0.1" PyYAML = ">=5.3.1" rich = "*" stevedore = ">=1.20.0" [package.extras] test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] toml = ["tomli (>=1.1.0)"] yaml = ["PyYAML"] [[package]] name = "black" version = "23.9.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"}, {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"}, {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "blacken-docs" version = "1.16.0" description = "Run Black on Python code blocks in documentation files." optional = false python-versions = ">=3.8" files = [ {file = "blacken_docs-1.16.0-py3-none-any.whl", hash = "sha256:b0dcb84b28ebfb352a2539202d396f50e15a54211e204a8005798f1d1edb7df8"}, {file = "blacken_docs-1.16.0.tar.gz", hash = "sha256:b4bdc3f3d73898dfbf0166f292c6ccfe343e65fc22ddef5319c95d1a8dcc6c1c"}, ] [package.dependencies] black = ">=22.1.0" [[package]] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] [[package]] name = "cfgv" version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] [[package]] name = "charset-normalizer" version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] [[package]] name = "click" version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "codespell" version = "2.2.6" description = "Codespell" optional = false python-versions = ">=3.8" files = [ {file = "codespell-2.2.6-py3-none-any.whl", hash = "sha256:9ee9a3e5df0990604013ac2a9f22fa8e57669c827124a2e961fe8a1da4cacc07"}, {file = "codespell-2.2.6.tar.gz", hash = "sha256:a8c65d8eb3faa03deabab6b3bbe798bea72e1799c7e9e955d57eca4096abcff9"}, ] [package.extras] dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"] hard-encoding-detection = ["chardet"] toml = ["tomli"] types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.3.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "darglint" version = "1.8.1" description = "A utility for ensuring Google-style docstrings stay up to date with the source code." optional = false python-versions = ">=3.6,<4.0" files = [ {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, ] [[package]] name = "dill" version = "0.3.7" description = "serialize all of Python" optional = false python-versions = ">=3.7" files = [ {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] [[package]] name = "distlib" version = "0.3.7" description = "Distribution utilities" optional = false python-versions = "*" files = [ {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, ] [[package]] name = "dparse" version = "0.6.3" description = "A parser for Python dependency files" optional = false python-versions = ">=3.6" files = [ {file = "dparse-0.6.3-py3-none-any.whl", hash = "sha256:0d8fe18714056ca632d98b24fbfc4e9791d4e47065285ab486182288813a5318"}, {file = "dparse-0.6.3.tar.gz", hash = "sha256:27bb8b4bcaefec3997697ba3f6e06b2447200ba273c0b085c3d012a04571b528"}, ] [package.dependencies] packaging = "*" tomli = {version = "*", markers = "python_version < \"3.11\""} [package.extras] conda = ["pyyaml"] pipenv = ["pipenv (<=2022.12.19)"] [[package]] name = "exceptiongroup" version = "1.1.3" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "filelock" version = "3.12.2" description = "A platform independent file lock." optional = false python-versions = ">=3.7" files = [ {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, ] [package.extras] docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "frozenlist" version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" files = [ {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, ] [[package]] name = "gitdb" version = "4.0.10" description = "Git Object Database" optional = false python-versions = ">=3.7" files = [ {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, ] [package.dependencies] smmap = ">=3.0.1,<6" [[package]] name = "gitpython" version = "3.1.37" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ {file = "GitPython-3.1.37-py3-none-any.whl", hash = "sha256:5f4c4187de49616d710a77e98ddf17b4782060a1788df441846bddefbb89ab33"}, {file = "GitPython-3.1.37.tar.gz", hash = "sha256:f9b9ddc0761c125d5780eab2d64be4873fc6817c2899cbcb34b02344bdc7bc54"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" [package.extras] test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-sugar"] [[package]] name = "identify" version = "2.5.26" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ {file = "identify-2.5.26-py2.py3-none-any.whl", hash = "sha256:c22a8ead0d4ca11f1edd6c9418c3220669b3b7533ada0a0ffa6cc0ef85cf9b54"}, {file = "identify-2.5.26.tar.gz", hash = "sha256:7243800bce2f58404ed41b7c002e53d4d22bcf3ae1b7900c2d7aefd95394bf7f"}, ] [package.extras] license = ["ukkonen"] [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "isort" version = "5.12.0" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" files = [ {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, ] [package.extras] colors = ["colorama (>=0.4.3)"] pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] [package.dependencies] mdurl = ">=0.1,<1.0" [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] code-style = ["pre-commit (>=3.0,<4.0)"] compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] [[package]] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] [[package]] name = "multidict" version = "6.0.4" description = "multidict implementation" optional = false python-versions = ">=3.7" files = [ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] [[package]] name = "mypy" version = "1.5.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] setuptools = "*" [[package]] name = "packaging" version = "23.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] name = "pathspec" version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.7" files = [ {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] [[package]] name = "pbr" version = "5.11.1" description = "Python Build Reasonableness" optional = false python-versions = ">=2.6" files = [ {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, ] [[package]] name = "platformdirs" version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, ] [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" version = "1.2.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.7" files = [ {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" version = "3.4.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.8" files = [ {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"}, {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"}, ] [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" [[package]] name = "pre-commit-hooks" version = "4.5.0" description = "Some out-of-the-box hooks for pre-commit." optional = false python-versions = ">=3.8" files = [ {file = "pre_commit_hooks-4.5.0-py2.py3-none-any.whl", hash = "sha256:b779d5c44ede9b1fda48e2d96b08e9aa5b1d2fdb8903ca09f0dbaca22d529edb"}, {file = "pre_commit_hooks-4.5.0.tar.gz", hash = "sha256:ffbe2af1c85ac9a7695866955680b4dee98822638b748a6f3debefad79748c8a"}, ] [package.dependencies] "ruamel.yaml" = ">=0.15" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "pydantic" version = "2.4.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.7" files = [ {file = "pydantic-2.4.2-py3-none-any.whl", hash = "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"}, {file = "pydantic-2.4.2.tar.gz", hash = "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7"}, ] [package.dependencies] annotated-types = ">=0.4.0" pydantic-core = "2.10.1" typing-extensions = ">=4.6.1" [package.extras] email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" version = "2.10.1" description = "" optional = false python-versions = ">=3.7" files = [ {file = "pydantic_core-2.10.1-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63"}, {file = "pydantic_core-2.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e"}, {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e"}, {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6"}, {file = "pydantic_core-2.10.1-cp310-none-win32.whl", hash = "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b"}, {file = "pydantic_core-2.10.1-cp310-none-win_amd64.whl", hash = "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0"}, {file = "pydantic_core-2.10.1-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea"}, {file = "pydantic_core-2.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4"}, {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607"}, {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f"}, {file = "pydantic_core-2.10.1-cp311-none-win32.whl", hash = "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6"}, {file = "pydantic_core-2.10.1-cp311-none-win_amd64.whl", hash = "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27"}, {file = "pydantic_core-2.10.1-cp311-none-win_arm64.whl", hash = "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325"}, {file = "pydantic_core-2.10.1-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921"}, {file = "pydantic_core-2.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d"}, {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f"}, {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c"}, {file = "pydantic_core-2.10.1-cp312-none-win32.whl", hash = "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f"}, {file = "pydantic_core-2.10.1-cp312-none-win_amd64.whl", hash = "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430"}, {file = "pydantic_core-2.10.1-cp312-none-win_arm64.whl", hash = "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94"}, {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede"}, {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221"}, {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15"}, {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f"}, {file = "pydantic_core-2.10.1-cp37-none-win32.whl", hash = "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c"}, {file = "pydantic_core-2.10.1-cp37-none-win_amd64.whl", hash = "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e"}, {file = "pydantic_core-2.10.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc"}, {file = "pydantic_core-2.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e"}, {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561"}, {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de"}, {file = "pydantic_core-2.10.1-cp38-none-win32.whl", hash = "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee"}, {file = "pydantic_core-2.10.1-cp38-none-win_amd64.whl", hash = "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e"}, {file = "pydantic_core-2.10.1-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970"}, {file = "pydantic_core-2.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429"}, {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7"}, {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595"}, {file = "pydantic_core-2.10.1-cp39-none-win32.whl", hash = "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a"}, {file = "pydantic_core-2.10.1-cp39-none-win_amd64.whl", hash = "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776"}, {file = "pydantic_core-2.10.1.tar.gz", hash = "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82"}, ] [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pygments" version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, ] [package.extras] plugins = ["importlib-metadata"] [[package]] name = "pylint" version = "3.0.1" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ {file = "pylint-3.0.1-py3-none-any.whl", hash = "sha256:9c90b89e2af7809a1697f6f5f93f1d0e518ac566e2ac4d2af881a69c13ad01ea"}, {file = "pylint-3.0.1.tar.gz", hash = "sha256:81c6125637be216b4652ae50cc42b9f8208dfb725cdc7e04c48f6902f4dbdf40"}, ] [package.dependencies] astroid = ">=3.0.0,<=3.1.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, ] isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} tomlkit = ">=0.10.1" [package.extras] spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] [[package]] name = "pytest" version = "7.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-aiohttp" version = "1.0.5" description = "Pytest plugin for aiohttp support" optional = false python-versions = ">=3.7" files = [ {file = "pytest-aiohttp-1.0.5.tar.gz", hash = "sha256:880262bc5951e934463b15e3af8bb298f11f7d4d3ebac970aab425aff10a780a"}, {file = "pytest_aiohttp-1.0.5-py3-none-any.whl", hash = "sha256:63a5360fd2f34dda4ab8e6baee4c5f5be4cd186a403cabd498fced82ac9c561e"}, ] [package.dependencies] aiohttp = ">=3.8.1" pytest = ">=6.1.0" pytest-asyncio = ">=0.17.2" [package.extras] testing = ["coverage (==6.2)", "mypy (==0.931)"] [[package]] name = "pytest-asyncio" version = "0.21.1" description = "Pytest support for asyncio" optional = false python-versions = ">=3.7" files = [ {file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"}, {file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"}, ] [package.dependencies] pytest = ">=7.0.0" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] [[package]] name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.7" files = [ {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "pyupgrade" version = "3.15.0" description = "A tool to automatically upgrade syntax for newer versions." optional = false python-versions = ">=3.8.1" files = [ {file = "pyupgrade-3.15.0-py2.py3-none-any.whl", hash = "sha256:8dc8ebfaed43566e2c65994162795017c7db11f531558a74bc8aa077907bc305"}, {file = "pyupgrade-3.15.0.tar.gz", hash = "sha256:a7fde381060d7c224f55aef7a30fae5ac93bbc428367d27e70a603bc2acd4f00"}, ] [package.dependencies] tokenize-rt = ">=5.2.0" [[package]] name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" version = "13.5.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ {file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruamel-yaml" version = "0.17.32" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3" files = [ {file = "ruamel.yaml-0.17.32-py3-none-any.whl", hash = "sha256:23cd2ed620231677564646b0c6a89d138b6822a0d78656df7abda5879ec4f447"}, {file = "ruamel.yaml-0.17.32.tar.gz", hash = "sha256:ec939063761914e14542972a5cba6d33c23b0859ab6342f61cf070cfc600efc2"}, ] [package.dependencies] "ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.12\""} [package.extras] docs = ["ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] [[package]] name = "ruamel-yaml-clib" version = "0.2.7" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" optional = false python-versions = ">=3.5" files = [ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:1a6391a7cabb7641c32517539ca42cf84b87b667bad38b78d4d42dd23e957c81"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9c7617df90c1365638916b98cdd9be833d31d337dbcd722485597b43c4a215bf"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win32.whl", hash = "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e"}, {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win_amd64.whl", hash = "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646"}, {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f"}, {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0"}, {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282"}, {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7"}, {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win32.whl", hash = "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93"}, {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b"}, {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb"}, {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307"}, {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697"}, {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b"}, {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win32.whl", hash = "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac"}, {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f"}, {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9"}, {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1"}, {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640"}, {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b"}, {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win32.whl", hash = "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8"}, {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"}, {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, ] [[package]] name = "ruff" version = "0.0.292" description = "An extremely fast Python linter, written in Rust." optional = false python-versions = ">=3.7" files = [ {file = "ruff-0.0.292-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96"}, {file = "ruff-0.0.292-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade"}, {file = "ruff-0.0.292-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4"}, {file = "ruff-0.0.292-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4"}, {file = "ruff-0.0.292-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7"}, {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81"}, {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205"}, {file = "ruff-0.0.292-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8"}, {file = "ruff-0.0.292-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9"}, {file = "ruff-0.0.292-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a"}, {file = "ruff-0.0.292-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0"}, {file = "ruff-0.0.292-py3-none-musllinux_1_2_i686.whl", hash = "sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0"}, {file = "ruff-0.0.292-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016"}, {file = "ruff-0.0.292-py3-none-win32.whl", hash = "sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003"}, {file = "ruff-0.0.292-py3-none-win_amd64.whl", hash = "sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c"}, {file = "ruff-0.0.292-py3-none-win_arm64.whl", hash = "sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68"}, {file = "ruff-0.0.292.tar.gz", hash = "sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac"}, ] [[package]] name = "safety" version = "2.3.4" description = "Checks installed dependencies for known vulnerabilities and licenses." optional = false python-versions = "*" files = [ {file = "safety-2.3.4-py3-none-any.whl", hash = "sha256:6224dcd9b20986a2b2c5e7acfdfba6bca42bb11b2783b24ed04f32317e5167ea"}, {file = "safety-2.3.4.tar.gz", hash = "sha256:b9e74e794e82f54d11f4091c5d820c4d2d81de9f953bf0b4f33ac8bc402ae72c"}, ] [package.dependencies] Click = ">=8.0.2" dparse = ">=0.6.2" packaging = ">=21.0" requests = "*" "ruamel.yaml" = ">=0.17.21" setuptools = ">=19.3" [package.extras] github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] gitlab = ["python-gitlab (>=1.3.0)"] [[package]] name = "setuptools" version = "68.1.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ {file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"}, {file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5,<=7.1.2)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "smmap" version = "5.0.0" description = "A pure Python implementation of a sliding window memory map manager" optional = false python-versions = ">=3.6" files = [ {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] [[package]] name = "stevedore" version = "5.1.0" description = "Manage dynamic plugins for Python applications" optional = false python-versions = ">=3.8" files = [ {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, ] [package.dependencies] pbr = ">=2.0.0,<2.1.0 || >2.1.0" [[package]] name = "tokenize-rt" version = "5.2.0" description = "A wrapper around the stdlib `tokenize` which roundtrips." optional = false python-versions = ">=3.8" files = [ {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"}, {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"}, ] [[package]] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "tomlkit" version = "0.12.1" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ {file = "tomlkit-0.12.1-py3-none-any.whl", hash = "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899"}, {file = "tomlkit-0.12.1.tar.gz", hash = "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86"}, ] [[package]] name = "typing-extensions" version = "4.8.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [[package]] name = "urllib3" version = "2.0.6" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" version = "20.24.3" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ {file = "virtualenv-20.24.3-py3-none-any.whl", hash = "sha256:95a6e9398b4967fbcb5fef2acec5efaf9aa4972049d9ae41f95e0972a683fd02"}, {file = "virtualenv-20.24.3.tar.gz", hash = "sha256:e5c3b4ce817b0b328af041506a2a299418c98747c4b1e68cb7527e74ced23efc"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<4" [package.extras] docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "vulture" version = "2.10" description = "Find dead code" optional = false python-versions = ">=3.8" files = [ {file = "vulture-2.10-py2.py3-none-any.whl", hash = "sha256:568a4176db7468d0157817ae3bb1847a19f1ddc629849af487f9d3b279bff77d"}, {file = "vulture-2.10.tar.gz", hash = "sha256:2a5c3160bffba77595b6e6dfcc412016bd2a09cd4b66cdf7fbba913684899f6f"}, ] [package.dependencies] toml = "*" [[package]] name = "yamllint" version = "1.32.0" description = "A linter for YAML files." optional = false python-versions = ">=3.7" files = [ {file = "yamllint-1.32.0-py3-none-any.whl", hash = "sha256:d97a66e48da820829d96077d76b8dfbe6c6140f106e558dae87e81ac4e6b30b7"}, {file = "yamllint-1.32.0.tar.gz", hash = "sha256:d01dde008c65de5b235188ab3110bebc59d18e5c65fc8a58267cd211cd9df34a"}, ] [package.dependencies] pathspec = ">=0.5.3" pyyaml = "*" [package.extras] dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] [[package]] name = "yarl" version = "1.9.2" description = "Yet another URL library" optional = false python-versions = ">=3.7" files = [ {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<=3.12" content-hash = "f0680451b0f8c8585c19faf84f4ad79f4b2a590a47938b90cb07acc0bdedfd68" bachya-aiopurpleair-f1ba236/pyproject.toml000066400000000000000000000076441451140715600207740ustar00rootroot00000000000000[build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.black] target-version = ["py39"] [tool.coverage.report] exclude_lines = ["raise NotImplementedError", "TYPE_CHECKING"] fail_under = 100 show_missing = true [tool.coverage.run] omit = ["aiopurpleair/backports/*"] source = ["aiopurpleair"] [tool.isort] known_first_party = "aiopurpleair,examples,tests" multi_line_output = 3 profile = "black" [tool.mypy] check_untyped_defs = true disallow_incomplete_defs = true disallow_subclassing_any = true disallow_untyped_calls = true disallow_untyped_decorators = true disallow_untyped_defs = true follow_imports = "silent" ignore_missing_imports = true no_implicit_optional = true platform = "linux" plugins = ["pydantic.mypy"] python_version = "3.10" show_error_codes = true strict_equality = true warn_incomplete_stub = true warn_redundant_casts = true warn_return_any = true warn_unreachable = true warn_unused_configs = true warn_unused_ignores = true [tool.poetry] name = "aiopurpleair" version = "2023.10.0" description = "A Python 3, asyncio-based library to interact with the PurpleAir API" readme = "README.md" authors = ["Aaron Bach "] license = "MIT" repository = "https://github.com/bachya/aiopurpleair" classifiers = [ "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] [tool.poetry.dependencies] aiohttp = ">=3.9.0b0" certifi = ">=2023.07.22" pydantic = ">=2.0.0,<3.0.0" python = ">=3.10,<=3.12" yarl = ">=1.9.2" [tool.poetry.group.dev.dependencies] GitPython = ">=3.1.35" Pygments = ">=2.15.0" aresponses = "^2.1.6" bandit = "^1.7.4" black = ">=22.10,<24.0" blacken-docs = "^1.12.1" codespell = "^2.2.2" coverage = {version = ">=6.5,<8.0", extras = ["toml"]} darglint = "^1.8.1" isort = "^5.10.1" mypy = "^1.2.0" pre-commit = ">=2.20,<4.0" pre-commit-hooks = "^4.3.0" pylint = ">=2.15.5,<4.0.0" pytest = "^7.2.0" pytest-aiohttp = "^1.0.0" pytest-asyncio = ">=0.20.1,<0.22.0" pytest-cov = "^4.0.0" pyupgrade = "^3.1.0" pyyaml = "^6.0.1" requests = ">=2.31.0" ruff = ">=0.0.261,<0.0.293" safety = "^2.3.1" typing-extensions = "^4.8.0" vulture = "^2.6" yamllint = "^1.28.0" [tool.poetry.urls] "Bug Tracker" = "https://github.com/bachya/aiopurpleair/issues" Changelog = "https://github.com/bachya/aiopurpleair/releases" [tool.pylint.BASIC] expected-line-ending-format = "LF" [tool.pylint.DESIGN] max-attributes = 20 [tool.pylint.FORMAT] max-line-length = 88 [tool.pylint.MASTER] extension-pkg-whitelist = [ "pydantic", ] ignore = [ "tests", ] load-plugins = [ "pylint.extensions.bad_builtin", "pylint.extensions.code_style", "pylint.extensions.docparams", "pylint.extensions.docstyle", "pylint.extensions.empty_comment", "pylint.extensions.overlapping_exceptions", "pylint.extensions.typing", ] [tool.pylint."MESSAGES CONTROL"] # Reasons disabled: # consider-alternative-union-syntax - Pydantic + Python 3.9 struggles with | operators # unnecessary-pass - This can hurt readability disable = [ "consider-alternative-union-syntax", "unnecessary-pass" ] [tool.pylint.REPORTS] score = false [tool.pylint.SIMILARITIES] # Minimum lines number of a similarity. # We set this higher because of some cases where V2 and V3 functionality are # similar, but abstracting them isn't feasible. min-similarity-lines = 4 # Ignore comments when computing similarities. ignore-comments = true # Ignore docstrings when computing similarities. ignore-docstrings = true # Ignore imports when computing similarities. ignore-imports = true [tool.pyright] include = ["aiopurpleair"] [tool.vulture] min_confidence = 80 paths = ["aiopurpleair", "tests"] verbose = false bachya-aiopurpleair-f1ba236/script/000077500000000000000000000000001451140715600173515ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/script/release000077500000000000000000000024631451140715600207240ustar00rootroot00000000000000#!/usr/bin/env bash set -e REPO_PATH="$( dirname "$( cd "$(dirname "$0")" ; pwd -P )" )" if [ "$(git rev-parse --abbrev-ref HEAD)" != "dev" ]; then echo "Refusing to publish a release from a branch other than dev" exit 1 fi if [ -z "$(command -v poetry)" ]; then echo "Poetry needs to be installed to run this script: pip3 install poetry" exit 1 fi function generate_version { latest_tag="$(git tag --sort=committerdate | tail -1)" month="$(date +'%Y.%m')" if [[ "$latest_tag" =~ "$month".* ]]; then patch="$(echo "$latest_tag" | cut -d . -f 3)" ((patch=patch+1)) echo "$month.$patch" else echo "$month.0" fi } # Temporarily uninstall pre-commit hooks so that we can push to dev and main: pre-commit uninstall # Pull the latest dev: git pull origin dev # Generate the next version (in the format YEAR.MONTH.RELEASE_NUMER): new_version=$(generate_version) # Update the PyPI package version: sed -i "" "s/^version = \".*\"/version = \"$new_version\"/g" "$REPO_PATH/pyproject.toml" git add pyproject.toml # Commit, tag, and push: git commit -m "Bump version to $new_version" git tag "$new_version" git push && git push --tags # Merge dev into main: git checkout main git merge dev git push git checkout dev # Re-initialize pre-commit: pre-commit install bachya-aiopurpleair-f1ba236/script/setup000077500000000000000000000002551451140715600204410ustar00rootroot00000000000000#!/bin/sh set -e if command -v "rtx"; then rtx install fi # Install all dependencies: pip3 install poetry poetry install # Install pre-commit hooks: pre-commit install bachya-aiopurpleair-f1ba236/tests/000077500000000000000000000000001451140715600172075ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/tests/__init__.py000066400000000000000000000000341451140715600213150ustar00rootroot00000000000000"""Define package tests.""" bachya-aiopurpleair-f1ba236/tests/common.py000066400000000000000000000007301451140715600210510ustar00rootroot00000000000000"""Define common test utilities.""" from __future__ import annotations import os TEST_API_KEY = "abcde12345" def load_fixture(filename: str) -> str: """Load a fixture. Args: filename: The filename of the fixtures/ file to load. Returns: A string containing the contents of the file. """ path = os.path.join(os.path.dirname(__file__), "fixtures", filename) with open(path, encoding="utf-8") as fptr: return fptr.read() bachya-aiopurpleair-f1ba236/tests/conftest.py000066400000000000000000000000371451140715600214060ustar00rootroot00000000000000"""Define dynamic fixtures.""" bachya-aiopurpleair-f1ba236/tests/endpoints/000077500000000000000000000000001451140715600212125ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/tests/endpoints/__init__.py000066400000000000000000000000351451140715600233210ustar00rootroot00000000000000"""Define endpoint tests.""" bachya-aiopurpleair-f1ba236/tests/endpoints/test_sensors.py000066400000000000000000000332311451140715600243210ustar00rootroot00000000000000"""Define tests for sensor endpoints.""" from __future__ import annotations import json from datetime import datetime import aiohttp import pytest from aresponses import ResponsesMockServer from aiopurpleair import API from aiopurpleair.const import ChannelFlag, ChannelState, LocationType from aiopurpleair.endpoints.sensors import NearbySensorResult from aiopurpleair.errors import InvalidRequestError from aiopurpleair.models.sensors import SensorModel from tests.common import TEST_API_KEY, load_fixture @pytest.mark.asyncio @pytest.mark.parametrize( "limit_results,output", [ ( None, [ NearbySensorResult( sensor=SensorModel( sensor_index=131077, name="BEE Patio", latitude=37.93273, longitude=-122.03972, ), distance=2.2331696896024913, ), NearbySensorResult( sensor=SensorModel( sensor_index=131079, name="BRSKBV-outside", latitude=37.75315, longitude=-122.44364, ), distance=41.766579532099314, ), NearbySensorResult( sensor=SensorModel( sensor_index=131083, name="Test Sensor", latitude=38.287594, longitude=-122.46281, ), distance=56.35082086588817, ), NearbySensorResult( sensor=SensorModel( sensor_index=131075, name="Mariners Bluff", latitude=33.51511, longitude=-117.67972, ), distance=627.8171580436522, ), ], ), ( 1, [ NearbySensorResult( sensor=SensorModel( sensor_index=131077, name="BEE Patio", latitude=37.93273, longitude=-122.03972, ), distance=2.2331696896024913, ), ], ), ], ) async def test_get_nearby_sensors( aresponses: ResponsesMockServer, limit_results: int | None, output: list[NearbySensorResult], ) -> None: """Test getting sensor indices within a bounding box around a latitude/longitude. Args: aresponses: An aresponses server. limit_results: An optional limit. output: The expected output. """ aresponses.add( "api.purpleair.com", "/v1/sensors", "get", response=aiohttp.web_response.json_response( json.loads(load_fixture("get_sensors_response.json")), status=200 ), ) async with aiohttp.ClientSession() as session: api = API(TEST_API_KEY, session=session) sensors = await api.sensors.async_get_nearby_sensors( ["name", "latitude", "longitude"], 37.92122, -122.01889, 10, limit_results=limit_results, ) assert sensors == output aresponses.assert_plan_strictly_followed() @pytest.mark.asyncio async def test_get_sensor( # pylint: disable=too-many-statements aresponses: ResponsesMockServer, ) -> None: """Test the GET /sensors/:sensor_index endpoint. Args: aresponses: An aresponses server. """ aresponses.add( "api.purpleair.com", "/v1/sensors/12345", "get", response=aiohttp.web_response.json_response( json.loads(load_fixture("get_sensor_response.json")), status=200 ), ) async with aiohttp.ClientSession() as session: api = API(TEST_API_KEY, session=session) response = await api.sensors.async_get_sensor(12345) assert response.api_version == "V1.0.11-0.0.41" assert response.timestamp_utc == datetime(2022, 11, 5, 16, 37, 3) assert response.data_timestamp_utc == datetime(2022, 11, 5, 16, 36, 21) assert response.sensor.sensor_index == 131075 assert response.sensor.altitude == 569 assert response.sensor.analog_input == 0.03 assert response.sensor.channel_flags == ChannelFlag.NORMAL assert response.sensor.channel_flags_auto == ChannelFlag.NORMAL assert response.sensor.channel_flags_manual == ChannelFlag.NORMAL assert response.sensor.channel_state == ChannelState.PM_A_PM_B assert response.sensor.confidence == 100 assert response.sensor.confidence_auto == 100 assert response.sensor.confidence_manual == 100 assert response.sensor.date_created_utc == datetime(2021, 9, 29, 22, 46, 14) assert response.sensor.firmware_version == "7.02" assert response.sensor.hardware == "2.0+BME280+PMSX003-B+PMSX003-A" assert response.sensor.humidity == 33 assert response.sensor.humidity_a == 33 assert response.sensor.icon == 0 assert response.sensor.is_owner is False assert response.sensor.last_modified_utc == datetime(2021, 10, 30, 22, 27, 9) assert response.sensor.last_seen_utc == datetime(2022, 11, 5, 16, 36, 2) assert response.sensor.latitude == 33.51511 assert response.sensor.led_brightness == 35 assert response.sensor.location_type == LocationType.OUTSIDE assert response.sensor.longitude == -117.67972 assert response.sensor.memory == 16008 assert response.sensor.model == "PA-II" assert response.sensor.name == "Mariners Bluff" assert response.sensor.pa_latency == 992 assert response.sensor.pm0_3_um_count == 75 assert response.sensor.pm0_3_um_count_a == 65 assert response.sensor.pm0_3_um_count_b == 86 assert response.sensor.pm0_5_um_count == 65 assert response.sensor.pm0_5_um_count_a == 58 assert response.sensor.pm0_5_um_count_b == 73 assert response.sensor.pm10_0_cf_1_a == 0.0 assert response.sensor.pm10_0_cf_1_b == 0.0 assert response.sensor.pm10_0 == 0.0 assert response.sensor.pm10_0_a == 0.0 assert response.sensor.pm10_0_atm == 0.0 assert response.sensor.pm10_0_atm_a == 0.0 assert response.sensor.pm10_0_atm_b == 0.0 assert response.sensor.pm10_0_b == 0.0 assert response.sensor.pm10_0_cf_1 == 0.0 assert response.sensor.pm10_0_um_count == 0 assert response.sensor.pm10_0_um_count_a == 0 assert response.sensor.pm10_0_um_count_b == 0 assert response.sensor.pm1_0 == 0.0 assert response.sensor.pm1_0_a == 0.0 assert response.sensor.pm1_0_atm == 0.0 assert response.sensor.pm1_0_atm_a == 0.0 assert response.sensor.pm1_0_atm_b == 0.0 assert response.sensor.pm1_0_b == 0.0 assert response.sensor.pm1_0_cf_1 == 0.0 assert response.sensor.pm1_0_cf_1_a == 0.0 assert response.sensor.pm1_0_cf_1_b == 0.0 assert response.sensor.pm1_0_um_count == 0 assert response.sensor.pm1_0_um_count_a == 0 assert response.sensor.pm1_0_um_count_b == 0 assert response.sensor.pm2_5 == 0.0 assert response.sensor.pm2_5_a == 0.0 assert response.sensor.pm2_5_alt == 0.4 assert response.sensor.pm2_5_alt_a == 0.3 assert response.sensor.pm2_5_alt_b == 0.4 assert response.sensor.pm2_5_atm == 0.0 assert response.sensor.pm2_5_atm_a == 0.0 assert response.sensor.pm2_5_atm_b == 0.0 assert response.sensor.pm2_5_b == 0.0 assert response.sensor.pm2_5_cf_1 == 0.0 assert response.sensor.pm2_5_cf_1_a == 0.0 assert response.sensor.pm2_5_cf_1_b == 0.0 assert response.sensor.pm2_5_um_count == 0 assert response.sensor.pm2_5_um_count_a == 0 assert response.sensor.pm2_5_um_count_b == 0 assert response.sensor.pm5_0_um_count == 0 assert response.sensor.pm5_0_um_count_a == 0 assert response.sensor.pm5_0_um_count_b == 0 assert response.sensor.position_rating == 5 assert response.sensor.pressure == 1001.66 assert response.sensor.pressure_a == 1001.66 assert response.sensor.primary_id_a == 1522282 assert response.sensor.primary_id_b == 1522284 assert response.sensor.primary_key_a == "FVXH9TQTQGG2CHEY" assert response.sensor.primary_key_b == "31ZHIMYRBK62KPY1" assert response.sensor.private is False assert response.sensor.rssi == -67 assert response.sensor.secondary_id_a == 1522283 assert response.sensor.secondary_id_b == 1522285 assert response.sensor.secondary_key_a == "UVKQCKBKJATTQGCX" assert response.sensor.secondary_key_b == "DT8UOXHFJS1JDONG" assert response.sensor.temperature == 69 assert response.sensor.temperature_a == 69 assert response.sensor.uptime == 15682 assert response.sensor.stats assert response.sensor.stats.pm2_5 == 0.0 assert response.sensor.stats.pm2_5_10minute == 0.2 assert response.sensor.stats.pm2_5_30minute == 1.0 assert response.sensor.stats.pm2_5_60minute == 1.2 assert response.sensor.stats.pm2_5_6hour == 1.2 assert response.sensor.stats.pm2_5_24hour == 1.8 assert response.sensor.stats.pm2_5_1week == 5.8 assert response.sensor.stats.timestamp_utc == datetime(2022, 11, 5, 16, 36, 2) assert response.sensor.stats_a assert response.sensor.stats_a.pm2_5 == 0.0 assert response.sensor.stats_a.pm2_5_10minute == 0.1 assert response.sensor.stats_a.pm2_5_30minute == 0.9 assert response.sensor.stats_a.pm2_5_60minute == 1.0 assert response.sensor.stats_a.pm2_5_6hour == 1.0 assert response.sensor.stats_a.pm2_5_24hour == 1.4 assert response.sensor.stats_a.pm2_5_1week == 4.8 assert response.sensor.stats_a.timestamp_utc == datetime(2022, 11, 5, 16, 36, 2) assert response.sensor.stats_b assert response.sensor.stats_b.pm2_5 == 0.0 assert response.sensor.stats_b.pm2_5_10minute == 0.2 assert response.sensor.stats_b.pm2_5_30minute == 1.2 assert response.sensor.stats_b.pm2_5_60minute == 1.3 assert response.sensor.stats_b.pm2_5_6hour == 1.5 assert response.sensor.stats_b.pm2_5_24hour == 2.2 assert response.sensor.stats_b.pm2_5_1week == 6.7 assert response.sensor.stats_b.timestamp_utc == datetime(2022, 11, 5, 16, 36, 2) aresponses.assert_plan_strictly_followed() @pytest.mark.asyncio async def test_get_sensor_validation_error(aresponses: ResponsesMockServer) -> None: """Test the GET /sensors/:sensor_index endpoint, returning a validation error. Args: aresponses: An aresponses server. """ async with aiohttp.ClientSession() as session: api = API(TEST_API_KEY, session=session) with pytest.raises(InvalidRequestError) as err: _ = await api.sensors.async_get_sensor(12345, fields=["foobar"]) assert "foobar is an unknown field" in str(err.value) aresponses.assert_plan_strictly_followed() @pytest.mark.asyncio async def test_get_sensors(aresponses: ResponsesMockServer) -> None: """Test the GET /sensors endpoint. Args: aresponses: An aresponses server. """ aresponses.add( "api.purpleair.com", "/v1/sensors", "get", response=aiohttp.web_response.json_response( json.loads(load_fixture("get_sensors_response.json")), status=200 ), ) async with aiohttp.ClientSession() as session: api = API(TEST_API_KEY, session=session) response = await api.sensors.async_get_sensors( fields=["name"], location_type=LocationType.OUTSIDE ) assert response.api_version == "V1.0.11-0.0.41" assert response.timestamp_utc == datetime(2022, 11, 3, 19, 26, 29) assert response.data_timestamp_utc == datetime(2022, 11, 3, 19, 25, 31) assert response.firmware_default_version == "7.02" assert response.max_age == 604800 assert response.fields == ["sensor_index", "name", "latitude", "longitude"] assert response.data == { 131075: SensorModel( sensor_index=131075, name="Mariners Bluff", latitude=33.51511, longitude=-117.67972, ), 131079: SensorModel( sensor_index=131079, name="BRSKBV-outside", latitude=37.75315, longitude=-122.44364, ), 131077: SensorModel( sensor_index=131077, name="BEE Patio", latitude=37.93273, longitude=-122.03972, ), 131083: SensorModel( sensor_index=131083, name="Test Sensor", latitude=38.287594, longitude=-122.46281, ), } aresponses.assert_plan_strictly_followed() @pytest.mark.asyncio async def test_get_sensors_validation_error(aresponses: ResponsesMockServer) -> None: """Test the GET /sensors endpoint, returning a validation error. Args: aresponses: An aresponses server. """ async with aiohttp.ClientSession() as session: api = API(TEST_API_KEY, session=session) with pytest.raises(InvalidRequestError) as err: _ = await api.sensors.async_get_sensors(["foobar"]) assert "foobar is an unknown field" in str(err.value) aresponses.assert_plan_strictly_followed() bachya-aiopurpleair-f1ba236/tests/fixtures/000077500000000000000000000000001451140715600210605ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/tests/fixtures/.gitignore000066400000000000000000000000001451140715600230360ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/tests/fixtures/error_invalid_api_key_response.json000066400000000000000000000002331451140715600302270ustar00rootroot00000000000000{ "api_version": "V1.0.11-0.0.41", "time_stamp": 1666903245, "error": "ApiKeyInvalidError", "description": "The provided api_key was not valid." } bachya-aiopurpleair-f1ba236/tests/fixtures/error_missing_api_key_response.json000066400000000000000000000002341451140715600302530ustar00rootroot00000000000000{ "api_version": "V1.0.11-0.0.41", "time_stamp": 1667594193, "error": "ApiKeyMissingError", "description": "No API key was found in the request." } bachya-aiopurpleair-f1ba236/tests/fixtures/error_not_found_response.json000066400000000000000000000002451451140715600270760ustar00rootroot00000000000000{ "api_version": "V1.0.11-0.0.41", "time_stamp": 1669085063, "error": "NotFoundError", "description": "Cannot find a sensor with the provided parameters." } bachya-aiopurpleair-f1ba236/tests/fixtures/error_unknown_response.json000066400000000000000000000002361451140715600266020ustar00rootroot00000000000000{ "api_version": "V1.0.11-0.0.41", "time_stamp": 1666903245, "error": "SomeUnknownError", "description": "We don't know anything about this error." } bachya-aiopurpleair-f1ba236/tests/fixtures/get_keys_response.json000066400000000000000000000001341451140715600255010ustar00rootroot00000000000000{ "api_version": "V1.0.11-0.0.41", "time_stamp": 1666895141, "api_key_type": "READ" } bachya-aiopurpleair-f1ba236/tests/fixtures/get_sensor_response.json000066400000000000000000000061221451140715600260420ustar00rootroot00000000000000{ "api_version": "V1.0.11-0.0.41", "time_stamp": 1667666223, "data_time_stamp": 1667666181, "sensor": { "sensor_index": 131075, "last_modified": 1635632829, "date_created": 1632955574, "last_seen": 1667666162, "private": 0, "is_owner": 0, "name": "Mariners Bluff", "icon": 0, "location_type": 0, "model": "PA-II", "hardware": "2.0+BME280+PMSX003-B+PMSX003-A", "led_brightness": 35, "firmware_version": "7.02", "rssi": -67, "uptime": 15682, "pa_latency": 992, "memory": 16008, "position_rating": 5, "latitude": 33.51511, "longitude": -117.67972, "altitude": 569, "channel_state": 3, "channel_flags": 0, "channel_flags_manual": 0, "channel_flags_auto": 0, "confidence": 100, "confidence_auto": 100, "confidence_manual": 100, "humidity": 33, "humidity_a": 33, "temperature": 69, "temperature_a": 69, "pressure": 1001.66, "pressure_a": 1001.66, "analog_input": 0.03, "pm1.0": 0.0, "pm1.0_a": 0.0, "pm1.0_b": 0.0, "pm2.5": 0.0, "pm2.5_a": 0.0, "pm2.5_b": 0.0, "pm2.5_alt": 0.4, "pm2.5_alt_a": 0.3, "pm2.5_alt_b": 0.4, "pm10.0": 0.0, "pm10.0_a": 0.0, "pm10.0_b": 0.0, "0.3_um_count": 75, "0.3_um_count_a": 65, "0.3_um_count_b": 86, "0.5_um_count": 65, "0.5_um_count_a": 58, "0.5_um_count_b": 73, "1.0_um_count": 0, "1.0_um_count_a": 0, "1.0_um_count_b": 0, "2.5_um_count": 0, "2.5_um_count_a": 0, "2.5_um_count_b": 0, "5.0_um_count": 0, "5.0_um_count_a": 0, "5.0_um_count_b": 0, "10.0_um_count": 0, "10.0_um_count_a": 0, "10.0_um_count_b": 0, "pm1.0_cf_1": 0.0, "pm1.0_cf_1_a": 0.0, "pm1.0_cf_1_b": 0.0, "pm1.0_atm": 0.0, "pm1.0_atm_a": 0.0, "pm1.0_atm_b": 0.0, "pm2.5_atm": 0.0, "pm2.5_atm_a": 0.0, "pm2.5_atm_b": 0.0, "pm2.5_cf_1": 0.0, "pm2.5_cf_1_a": 0.0, "pm2.5_cf_1_b": 0.0, "pm10.0_atm": 0.0, "pm10.0_atm_a": 0.0, "pm10.0_atm_b": 0.0, "pm10.0_cf_1": 0.0, "pm10.0_cf_1_a": 0.0, "pm10.0_cf_1_b": 0.0, "primary_id_a": 1522282, "primary_key_a": "FVXH9TQTQGG2CHEY", "primary_id_b": 1522284, "primary_key_b": "31ZHIMYRBK62KPY1", "secondary_id_a": 1522283, "secondary_key_a": "UVKQCKBKJATTQGCX", "secondary_id_b": 1522285, "secondary_key_b": "DT8UOXHFJS1JDONG", "stats": { "pm2.5": 0.0, "pm2.5_10minute": 0.2, "pm2.5_30minute": 1.0, "pm2.5_60minute": 1.2, "pm2.5_6hour": 1.2, "pm2.5_24hour": 1.8, "pm2.5_1week": 5.8, "time_stamp": 1667666162 }, "stats_a": { "pm2.5": 0.0, "pm2.5_10minute": 0.1, "pm2.5_30minute": 0.9, "pm2.5_60minute": 1.0, "pm2.5_6hour": 1.0, "pm2.5_24hour": 1.4, "pm2.5_1week": 4.8, "time_stamp": 1667666162 }, "stats_b": { "pm2.5": 0.0, "pm2.5_10minute": 0.2, "pm2.5_30minute": 1.2, "pm2.5_60minute": 1.3, "pm2.5_6hour": 1.5, "pm2.5_24hour": 2.2, "pm2.5_1week": 6.7, "time_stamp": 1667666162 } } } bachya-aiopurpleair-f1ba236/tests/fixtures/get_sensors_response.json000066400000000000000000000007241451140715600262270ustar00rootroot00000000000000{ "api_version": "V1.0.11-0.0.41", "time_stamp": 1667503589, "data_time_stamp": 1667503531, "location_type": 0, "max_age": 604800, "firmware_default_version": "7.02", "fields": ["sensor_index", "name", "latitude", "longitude"], "data": [ [131075, "Mariners Bluff", 33.51511, -117.67972], [131079, "BRSKBV-outside", 37.75315, -122.44364], [131077, "BEE Patio", 37.93273, -122.03972], [131083, "Test Sensor", 38.287594, -122.46281] ] } bachya-aiopurpleair-f1ba236/tests/models/000077500000000000000000000000001451140715600204725ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/tests/models/__init__.py000066400000000000000000000000321451140715600225760ustar00rootroot00000000000000"""Define model tests.""" bachya-aiopurpleair-f1ba236/tests/models/conftest.py000066400000000000000000000022171451140715600226730ustar00rootroot00000000000000"""Define dynamic fixtures.""" import json from typing import Any, cast import pytest from tests.common import load_fixture @pytest.fixture(name="error_invalid_api_key_response", scope="session") def error_invalid_api_key_response_fixture() -> dict[str, Any]: """Define a fixture for error response data from GET /v1/keys. Args: Define an API response payload. """ return cast( dict[str, Any], json.loads(load_fixture("error_invalid_api_key_response.json")) ) @pytest.fixture(name="get_keys_response", scope="session") def get_keys_response_fixture() -> dict[str, Any]: """Define a fixture for successful response data from GET /v1/keys. Args: Define an API response payload. """ return cast(dict[str, Any], json.loads(load_fixture("get_keys_response.json"))) @pytest.fixture(name="get_sensors_response", scope="session") def get_sensors_response_fixture() -> dict[str, Any]: """Define a fixture for successful response data from GET /v1/sensors. Args: Define an API response payload. """ return cast(dict[str, Any], json.loads(load_fixture("get_sensors_response.json"))) bachya-aiopurpleair-f1ba236/tests/models/test_keys.py000066400000000000000000000023761451140715600230660ustar00rootroot00000000000000"""Define tests for key models.""" from __future__ import annotations from datetime import datetime from typing import Any import pytest from pydantic import ValidationError from aiopurpleair.models.keys import ApiKeyType, GetKeysResponse def test_get_keys_response(get_keys_response: dict[str, Any]) -> None: """Test the GetKeysResponse model. Args: get_keys_response: A dict of response data from GET /v1/keys. """ response = GetKeysResponse.model_validate(get_keys_response) assert response.model_dump() == { "api_key_type": ApiKeyType.READ, "api_version": "V1.0.11-0.0.41", "timestamp_utc": datetime(2022, 10, 27, 18, 25, 41), } @pytest.mark.parametrize( "get_keys_response", [ { "Foo": "Bar", }, { "api_key_type": "FAKE_TYPE", "api_version": "V1.0.11-0.0.41", "timestamp_utc": 1666895141, }, ], ) def test_get_keys_response_errors(get_keys_response: dict[str, Any]) -> None: """Test errors for the GetKeysResponse model. Args: get_keys_response: A dict of response data from GET /v1/keys. """ with pytest.raises(ValidationError): _ = GetKeysResponse.model_validate(get_keys_response) bachya-aiopurpleair-f1ba236/tests/models/test_sensors.py000066400000000000000000000165361451140715600236120ustar00rootroot00000000000000"""Define tests for sensor models.""" from __future__ import annotations from datetime import datetime from typing import Any import pytest from pydantic import ValidationError from aiopurpleair.models.sensors import ( GetSensorsRequest, GetSensorsResponse, LocationType, ) @pytest.mark.parametrize( "input_payload,output_payload", [ ( { "fields": ["name", "icon"], "location_type": LocationType.INSIDE, "read_keys": ["abc", "def"], "show_only": [123, 456], "modified_since_utc": datetime(2022, 11, 3, 15, 46, 21), "max_age": 1200, "nwlng": -0.2416796, "nwlat": 51.5285582, "selng": -0.8876124, "selat": 54.7818162, }, { "fields": "name,icon", "location_type": 1, "read_keys": "abc,def", "show_only": "123,456", "modified_since": 1667490381, "max_age": 1200, "nwlng": -0.2416796, "nwlat": 51.5285582, "selng": -0.8876124, "selat": 54.7818162, }, ), ], ) def test_get_sensors_request( input_payload: dict[str, Any], output_payload: dict[str, Any] ) -> None: """Test the GetSensorsRequest model. Args: input_payload: input_payload parameters for the model. output_payload: A parsed model dictionary output_payload. """ request = GetSensorsRequest.model_validate(input_payload) assert dict(request) == output_payload @pytest.mark.parametrize( "payload,error_string", [ ( { "fields": ["name", "foobar"], }, "foobar is an unknown field", ), ( { "fields": ["name"], "nwlng": -0.2416796, "nwlat": -100.2416796, "selng": -0.8876124, "selat": 54.7818162, }, "-100.2416796 is an invalid latitude", ), ( { "fields": ["name"], "nwlng": -200.2416796, "nwlat": 51.5285582, "selng": -0.8876124, "selat": 54.7818162, }, "-200.2416796 is an invalid longitude", ), ( { "fields": ["name"], "nwlng": -0.2416796, }, "must pass none or all of the bounding box coordinates", ), ], ) def test_get_sensors_request_errors(error_string: str, payload: dict[str, Any]) -> None: """Test that an invalid GetSensorsRequest payload raises an error. Args: error_string: The error string that gets raised. payload: The payload to test. """ with pytest.raises(ValidationError) as err: _ = GetSensorsRequest.model_validate(payload) assert error_string in str(err.value) @pytest.mark.parametrize( "input_payload,output_payload", [ ( { "api_version": "V1.0.11-0.0.41", "time_stamp": 1667533097, "data_time_stamp": 1667533091, "location_type": 0, "max_age": 604800, "firmware_default_version": "7.02", "fields": ["sensor_index", "name", "icon"], "data": [[131075, "Mariners Bluff", 0], [131079, "BRSKBV-outside", 0]], }, { "api_version": "V1.0.11-0.0.41", "timestamp_utc": datetime(2022, 11, 4, 3, 38, 17), "data_timestamp_utc": datetime(2022, 11, 4, 3, 38, 11), "max_age": 604800, "firmware_default_version": "7.02", "fields": ["sensor_index", "name", "icon"], "data": { 131075: { "sensor_index": 131075, "name": "Mariners Bluff", "icon": 0, }, 131079: { "sensor_index": 131079, "name": "BRSKBV-outside", "icon": 0, }, }, }, ), ], ) def test_get_sensors_response( input_payload: dict[str, Any], output_payload: dict[str, Any] ) -> None: """Test the GetSensorsResponse model. Args: input_payload: input_payload parameters for the model. output_payload: A parsed model dictionary output_payload. """ request = GetSensorsResponse.model_validate(input_payload) assert request.model_dump(exclude_none=True) == output_payload @pytest.mark.parametrize( "payload,error_string", [ ( { "api_version": "V1.0.11-0.0.41", "time_stamp": 1667533097, "data_time_stamp": 1667533091, "location_type": 0, "max_age": 604800, "firmware_default_version": "7.02", "fields": ["sensor_index", "name", "foobar"], "data": [ [131075, "Mariners Bluff", 0], [131079, "BRSKBV-outside", 0], ], }, "foobar is an unknown field", ), ( { "api_version": "V1.0.11-0.0.41", "time_stamp": 1667533097, "data_time_stamp": 1667533091, "max_age": 604800, "firmware_default_version": "7.02", "fields": ["sensor_index", "name", "icon", "location_type"], "data": [ [131075, "Mariners Bluff", 0, 0], [131079, "BRSKBV-outside", 0, 2], ], }, "2 is an unknown location type", ), ( { "api_version": "V1.0.11-0.0.41", "time_stamp": 1667533097, "data_time_stamp": 1667533091, "max_age": 604800, "firmware_default_version": "7.02", "fields": ["sensor_index", "name", "icon", "channel_flags"], "data": [ [131075, "Mariners Bluff", 0, 0], [131079, "BRSKBV-outside", 0, 7], ], }, "7 is an unknown channel flag", ), ( { "api_version": "V1.0.11-0.0.41", "time_stamp": 1667533097, "data_time_stamp": 1667533091, "max_age": 604800, "firmware_default_version": "7.02", "fields": ["sensor_index", "name", "icon", "channel_state"], "data": [ [131075, "Mariners Bluff", 0, 0], [131079, "BRSKBV-outside", 0, 7], ], }, "7 is an unknown channel state", ), ], ) def test_get_sensors_response_errors( error_string: str, payload: dict[str, Any] ) -> None: """Test that an invalid GetSensorsResponse payload raises an error. Args: error_string: The error string that gets raised. payload: The payload to test. """ with pytest.raises(ValidationError) as err: _ = GetSensorsResponse.model_validate(payload) assert error_string in str(err.value) bachya-aiopurpleair-f1ba236/tests/test_api.py000066400000000000000000000074541451140715600214030ustar00rootroot00000000000000"""Define tests for the API object.""" from __future__ import annotations import json from datetime import datetime import aiohttp import pytest from aresponses import ResponsesMockServer from aiopurpleair import API from aiopurpleair.errors import InvalidApiKeyError, NotFoundError, RequestError from aiopurpleair.models.keys import ApiKeyType, GetKeysResponse from tests.common import TEST_API_KEY, load_fixture @pytest.mark.asyncio @pytest.mark.parametrize( "error_fixture_filename,err_type,status_code", [ ("error_invalid_api_key_response.json", InvalidApiKeyError, 403), ("error_missing_api_key_response.json", InvalidApiKeyError, 403), ("error_not_found_response.json", NotFoundError, 404), ("error_unknown_response.json", RequestError, 500), ], ) async def test_api_error( aresponses: ResponsesMockServer, err_type: type[RequestError], error_fixture_filename: str, status_code: int, ) -> None: """Test an invalid API call. Args: aresponses: An aresponses server. err_type: A subclass of PurpleAirError. error_fixture_filename: A fixture that contains an error API response. status_code: An HTTP status code. """ aresponses.add( "api.purpleair.com", "/v1/bad_endpoint", "get", response=aiohttp.web_response.json_response( json.loads(load_fixture(error_fixture_filename)), status=status_code ), ) async with aiohttp.ClientSession() as session: api = API(TEST_API_KEY, session=session) with pytest.raises(err_type): await api.async_request("get", "/bad_endpoint", GetKeysResponse) aresponses.assert_plan_strictly_followed() @pytest.mark.asyncio @pytest.mark.parametrize("use_session", [True, False]) async def test_check_api_key( aresponses: ResponsesMockServer, use_session: bool ) -> None: """Test the GET /keys endpoint. Args: aresponses: An aresponses server. use_session: Whether an existing aiohttp ClientSession should be used. """ aresponses.add( "api.purpleair.com", "/v1/keys", "get", response=aiohttp.web_response.json_response( json.loads(load_fixture("get_keys_response.json")), status=200 ), ) if use_session: async with aiohttp.ClientSession() as session: api = API(TEST_API_KEY, session=session) else: api = API(TEST_API_KEY) response = await api.async_check_api_key() assert isinstance(response, GetKeysResponse) assert response.api_key_type == ApiKeyType.READ assert response.api_version == "V1.0.11-0.0.41" assert response.timestamp_utc == datetime(2022, 10, 27, 18, 25, 41) aresponses.assert_plan_strictly_followed() @pytest.mark.asyncio async def test_check_api_key_validation_error(aresponses: ResponsesMockServer) -> None: """Test the GET /keys endpoint, returning a validation error. Args: aresponses: An aresponses server. """ raw_response = json.loads(load_fixture("get_keys_response.json")) raw_response["api_key_type"] = "FAKE" aresponses.add( "api.purpleair.com", "/v1/keys", "get", response=aiohttp.web_response.json_response(raw_response, status=200), ) async with aiohttp.ClientSession() as session: with pytest.raises(RequestError) as err: api = API(TEST_API_KEY, session=session) _ = await api.async_check_api_key() assert "FAKE is an unknown API key type" in str(err.value) aresponses.assert_plan_strictly_followed() def test_get_map_url() -> None: """Test getting the map URL for a sensor index.""" api = API(TEST_API_KEY) map_url = api.get_map_url(12345) assert map_url == "https://map.purpleair.com/1/mAQI/a10/p604800/cC0?select=12345" bachya-aiopurpleair-f1ba236/tests/util/000077500000000000000000000000001451140715600201645ustar00rootroot00000000000000bachya-aiopurpleair-f1ba236/tests/util/__init__.py000066400000000000000000000000311451140715600222670ustar00rootroot00000000000000"""Define util tests.""" bachya-aiopurpleair-f1ba236/tests/util/test_dt.py000066400000000000000000000004621451140715600222060ustar00rootroot00000000000000"""Define datetime util tests.""" from datetime import datetime from aiopurpleair.util.dt import utc_to_timestamp def test_utc_to_timestamp() -> None: """Test the utc_to_timestamp util.""" test_datetime = datetime(2022, 11, 8, 6, 34, 39) assert utc_to_timestamp(test_datetime) == 1667889279 bachya-aiopurpleair-f1ba236/tests/util/test_geo.py000066400000000000000000000074471451140715600223630ustar00rootroot00000000000000"""Define geographical util tests.""" import pytest from aiopurpleair.util.geo import GeoLocation @pytest.mark.parametrize( "latitude,longitude,distance_km,nw_latitude,nw_longitude,se_latitude,se_longitude", [ ( 51.5285582, -0.2416796, 5, 51.57347, -0.31388, 51.48364, -0.16948, ), ( 0.2, 179.99999999999, 500, 4.6916, 175.50837, -4.2916, -175.50837, ), ( 0.2, -179.99999999999, 500, 4.6916, 175.50837, -4.2916, -175.50837, ), ( 89.9, 0.2, 500, 90.0, -180.0, 85.4084, 180.0, ), ], ) def test_geo_location_bounding_box( # pylint: disable=too-many-arguments distance_km: float, latitude: float, longitude: float, nw_latitude: float, nw_longitude: float, se_latitude: float, se_longitude: float, ) -> None: """Test getting a 5km bounding box around London. Args: distance_km: The bounding box distance (in kilometers). latitude: The central latitude. longitude: The central longitude. nw_latitude: The latitude of the NW point. nw_longitude: The longitude of the NW point. se_latitude: The latitude of the SE point. se_longitude: The longitude of the SE point. """ location = GeoLocation.from_degrees(latitude, longitude) nw_coordinate, se_coordinate = location.bounding_box(distance_km) # We round to prevent floating point errors in CI: assert round(nw_coordinate.latitude_degrees, 5) == nw_latitude assert round(nw_coordinate.longitude_degrees, 5) == nw_longitude assert round(se_coordinate.latitude_degrees, 5) == se_latitude assert round(se_coordinate.longitude_degrees, 5) == se_longitude def test_geo_location_bounding_box_invalid_distance() -> None: """Test an error with an invalid bounding box distance.""" location = GeoLocation.from_degrees(51.5285582, -0.2416796) with pytest.raises(ValueError) as err: _, _ = location.bounding_box(-1) assert "Cannot calculate a bounding box with negative distance" in str(err.value) def test_geo_location_distance_to() -> None: """Test getting the distance between two GeoLocation objects.""" london = GeoLocation.from_degrees(51.5285582, -0.2416796) liverpool = GeoLocation.from_degrees(53.4121569, -2.9860979) distance = london.distance_to(liverpool) assert distance == 280.31725082207095 def test_geo_location_from_degrees() -> None: """Test creating a GeoLocation object from a degrees-based latitude/longitude.""" location = GeoLocation.from_degrees(51.5285582, -0.2416796) assert location.latitude_degrees == 51.5285582 assert location.longitude_degrees == -0.2416796 assert location.latitude_radians == 0.8993429993955228 assert location.longitude_radians == -0.004218104754902888 def test_geo_location_from_radians() -> None: """Test creating a GeoLocation object from a radians-based latitude/longitude.""" location = GeoLocation.from_radians(0.8993429993955228, -0.004218104754902888) assert location.latitude_degrees == 51.5285582 assert location.longitude_degrees == -0.24167960000000002 assert location.latitude_radians == 0.8993429993955228 assert location.longitude_radians == -0.004218104754902888 def test_geo_location_invalid_coordinates() -> None: """Test an error with invalid coordinates.""" with pytest.raises(ValueError) as err: _ = GeoLocation.from_degrees(97.0, -0.2416796) assert "Invalid latitude: 1.6929693744344996 radians" in str(err.value) bachya-aiopurpleair-f1ba236/vulture_whitelist.py000066400000000000000000000006311451140715600222210ustar00rootroot00000000000000cls # unused variable (aiopurpleair/model/keys.py:30) cls # unused variable (aiopurpleair/model/sensors.py:163) cls # unused variable (aiopurpleair/model/sensors.py:191) cls # unused variable (aiopurpleair/model/sensors.py:211) cls # unused variable (aiopurpleair/model/sensors.py:224) cls # unused variable (aiopurpleair/model/sensors.py:240) cls # unused variable (aiopurpleair/model/sensors.py:256)