pax_global_header 0000666 0000000 0000000 00000000064 14675551151 0014524 g ustar 00root root 0000000 0000000 52 comment=b25d6f4301322a849c9fb4b58b34cd6e092b0139
firebase-messaging-0.4.4/ 0000775 0000000 0000000 00000000000 14675551151 0015264 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/.github/ 0000775 0000000 0000000 00000000000 14675551151 0016624 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/.github/actions/ 0000775 0000000 0000000 00000000000 14675551151 0020264 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/.github/actions/setup/ 0000775 0000000 0000000 00000000000 14675551151 0021424 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/.github/actions/setup/action.yml 0000664 0000000 0000000 00000006171 14675551151 0023431 0 ustar 00root root 0000000 0000000 ---
name: Setup Environment
description: Install requested pipx dependencies, configure the system python, and install uv and the package dependencies
inputs:
uv-install-options:
default: ""
uv-version:
default: 0.4.5
python-version:
required: true
cache-pre-commit:
default: false
runs:
using: composite
steps:
- uses: "actions/setup-python@v5"
id: setup-python
with:
python-version: "${{ inputs.python-version }}"
allow-prereleases: true
- name: Setup pipx environment Variables
id: pipx-env-setup
# pipx default home and bin dir are not writable by the cache action
# so override them here and add the bin dir to PATH for later steps.
# This also ensures the pipx cache only contains uv
run: |
SEP="${{ !startsWith(runner.os, 'windows') && '/' || '\\' }}"
PIPX_CACHE="${{ github.workspace }}${SEP}pipx_cache"
echo "pipx-cache-path=${PIPX_CACHE}" >> $GITHUB_OUTPUT
echo "pipx-version=$(pipx --version)" >> $GITHUB_OUTPUT
echo "PIPX_HOME=${PIPX_CACHE}${SEP}home" >> $GITHUB_ENV
echo "PIPX_BIN_DIR=${PIPX_CACHE}${SEP}bin" >> $GITHUB_ENV
echo "PIPX_MAN_DIR=${PIPX_CACHE}${SEP}man" >> $GITHUB_ENV
echo "${PIPX_CACHE}${SEP}bin" >> $GITHUB_PATH
shell: bash
- name: Pipx cache
id: pipx-cache
uses: actions/cache@v4
with:
path: ${{ steps.pipx-env-setup.outputs.pipx-cache-path }}
key: cache-${{ inputs.cache-version }}-${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-pipx-${{ steps.pipx-env-setup.outputs.pipx-version }}-uv-${{ inputs.uv-version }}
- name: Install uv
if: steps.pipx-cache.outputs.cache-hit != 'true'
id: install-uv
shell: bash
run: |-
pipx install uv==${{ inputs.uv-version }} --python "${{ steps.setup-python.outputs.python-path }}"
- name: Read uv cache location
id: uv-cache-location
shell: bash
run: |-
echo "uv-venv-location=$(uv cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
name: uv cache
with:
path: |
${{ steps.uv-cache-location.outputs.uv-venv-location }}
key: cache-${{ inputs.cache-version }}-${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('uv.lock') }}-options-${{ inputs.uv-install-options }}
- name: "uv install"
shell: bash
run: |
uv sync --python "${{ steps.setup-python.outputs.python-path }}" ${{ inputs.uv-install-options }}
- name: Read pre-commit version
if: inputs.cache-pre-commit == 'true'
id: pre-commit-version
shell: bash
run: >-
echo "pre-commit-version=$(uv run pre-commit -V | awk '{print $2}')" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
if: inputs.cache-pre-commit == 'true'
name: Pre-commit cache
with:
path: ~/.cache/pre-commit/
key: cache-${{ inputs.cache-version }}-${{ runner.os }}-pre-commit-${{ steps.pre-commit-version.outputs.pre-commit-version }}-python-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}
firebase-messaging-0.4.4/.github/workflows/ 0000775 0000000 0000000 00000000000 14675551151 0020661 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/.github/workflows/ci.yml 0000664 0000000 0000000 00000004600 14675551151 0021777 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
env:
UV_VERSION: 0.4.5
PACKAGE_NAME: firebase_messaging
jobs:
linting:
name: "Perform linting checks"
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
steps:
- uses: "actions/checkout@v4"
- name: Setup environment
uses: ./.github/actions/setup
with:
python-version: ${{ matrix.python-version }}
uv-version: ${{ env.UV_VERSION }}
uv-install-options: ""
cache-pre-commit: true
- name: "Run pre-commit checks"
run: |
uv run pre-commit run --all-files --verbose
docs:
name: "Build docs"
needs: linting
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
steps:
- uses: "actions/checkout@v4"
- name: Setup environment
uses: ./.github/actions/setup
with:
python-version: ${{ matrix.python-version }}
uv-version: ${{ env.UV_VERSION }}
uv-install-options: "--extra docs --no-dev"
- name: Make docs
run: |
uv run make -C docs html
tests:
name: Tests - Python ${{ matrix.python-version}} on ${{ matrix.os }}
needs: linting
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "pypy-3.9", "pypy-3.10"]
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: "actions/checkout@v4"
- name: Setup environment
uses: ./.github/actions/setup
with:
python-version: ${{ matrix.python-version }}
uv-version: ${{ env.UV_VERSION }}
uv-install-options: ""
- name: Run tests
run: >
uv run pytest tests/
--cov=${{ env.PACKAGE_NAME }} --cov-report=xml
--cov-report=term-missing --import-mode importlib
- name: Coveralls GitHub Action
uses: coverallsapp/github-action@v2.2.3
with:
file: coverage.xml
debug: true
parallel: true
if: ${{ success() && matrix.python-version == '3.12' }}
finish:
name: Finish coverage build
needs: tests
runs-on: ubuntu-latest
steps:
- name: Close parallel build
uses: coverallsapp/github-action@v2.2.3
with:
parallel-finished: true
firebase-messaging-0.4.4/.github/workflows/publish.yml 0000664 0000000 0000000 00000007132 14675551151 0023055 0 ustar 00root root 0000000 0000000 name: Publish Python distribution to PyPI and TestPyPI
on:
push:
branches: ["main"]
tags:
- '*'
env:
UV_VERSION: 0.4.5
PYPI_PROJECT: firebase-messaging
# GITHUB_TOKEN must have write access
# https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/signing-the-distribution-packages
jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install uv
run: |-
pipx install uv==${{ env.UV_VERSION }} --python "${{ steps.setup-python.outputs.python-path }}"
- name: Build with uv
run: uv build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
publish-to-pypi:
name: >-
Publish to PyPI
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/${{ env.PYPI_PROJECT }}
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
github-release:
name: >-
Create github release
needs:
- publish-to-pypi
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
id-token: write # IMPORTANT: mandatory for sigstore
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v2.1.1
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Repo clone is required for --notes-from-tag to work
run: |
gh repo clone '${{ github.repository }}'
cd ${{ github.event.repository.name }}
gh release create '${{ github.ref_name }}' --verify-tag --notes-from-tag --title '${{ github.ref_name }}' ${{ contains(github.ref_name, 'dev') && '--prerelease --latest=false' || '--latest=true' }}
cd ..
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}'
publish-to-testpypi:
name: Publish to TestPyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/${{ env.PYPI_PROJECT }}
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish distribution to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
skip-existing: true
firebase-messaging-0.4.4/.github_changelog_generator 0000664 0000000 0000000 00000000732 14675551151 0022626 0 ustar 00root root 0000000 0000000 output=CHANGELOG.md
user=sdb9696
project=firebase-messaging
release-branch=main
base=HISTORY.md
since-tag=0.2.1
usernames-as-github-logins=true
add-sections={"docs":{"prefix":"**Documentation updates:**","labels":["documentation"]},"maintenance":{"prefix":"**Project maintenance:**","labels":["maintenance"]}}
exclude-labels=duplicate,question,invalid,wontfix,release-prep,stale,bug
issues-wo-labels=false
bug-labels=bugfix
breaking-label=**Breaking change pull requests:**
firebase-messaging-0.4.4/.gitignore 0000664 0000000 0000000 00000000066 14675551151 0017256 0 ustar 00root root 0000000 0000000 __pycache__/
.vscode/
htmlcov/
docs/build
dist
.venv
firebase-messaging-0.4.4/.pre-commit-config.yaml 0000664 0000000 0000000 00000002211 14675551151 0021541 0 ustar 00root root 0000000 0000000 repos:
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
rev: 0.4.5
hooks:
# Update the uv lockfile
- id: uv-lock
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-docstring-first
- id: check-yaml
- id: debug-statements
- id: check-ast
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.1
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/PyCQA/doc8
rev: 'v1.1.1'
hooks:
- id: doc8
additional_dependencies: [tomli]
- repo: local
hooks:
# Run mypy in the virtual environment so it uses the installed dependencies
# for more accurate checking than using the pre-commit mypy mirror
- id: mypy
name: mypy
entry: uv run mypy
language: system
types_or: [python, pyi]
require_serial: true
# exclude required because --all-files passes py and pyi
# should match pattern in pyproject.toml
exclude: |
(?x)(
^docs/.*|
^tests/.*|
^firebase_messaging/proto/.*py$
)
firebase-messaging-0.4.4/.readthedocs.yaml 0000664 0000000 0000000 00000002175 14675551151 0020520 0 ustar 00root root 0000000 0000000 # Read the Docs configuration file for Sphinx projects
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the OS, Python version and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.11"
# You can also specify other tool versions:
# nodejs: "20"
# rust: "1.70"
# golang: "1.20"
# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: docs/source/conf.py
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
# builder: "dirhtml"
# Fail on all warnings to avoid broken references
# fail_on_warning: true
# Optionally build your docs in additional formats such as PDF and ePub
# formats:
# - pdf
# - epub
formats: all
# Optional but recommended, declare the Python requirements required
# to build your documentation
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
# python:
# install:
# - requirements: docs/requirements.txt
python:
install:
- method: pip
path: .
extra_requirements:
- docs
firebase-messaging-0.4.4/CHANGELOG.md 0000664 0000000 0000000 00000015564 14675551151 0017110 0 ustar 00root root 0000000 0000000 # Changelog
## [0.4.4](https://github.com/sdb9696/firebase-messaging/tree/0.4.4) (2024-09-27)
[Full Changelog](https://github.com/sdb9696/firebase-messaging/compare/0.4.3...0.4.4)
**Release highlights:**
- Revert changes to compiled protobuf files so they are back to 4.24 to avoid ongoing release management headaches.
**Merged pull requests:**
- Revert protobuf compilation changes [\#19](https://github.com/sdb9696/firebase-messaging/pull/19) (@sdb9696)
## [0.4.3](https://github.com/sdb9696/firebase-messaging/tree/0.4.3) (2024-09-25)
[Full Changelog](https://github.com/sdb9696/firebase-messaging/compare/0.4.2...0.4.3)
**Release highlights:**
- Suppress unnecessary warnings from dependant library.
**Fixed bugs:**
- Catch excessive protobuf warnings [\#16](https://github.com/sdb9696/firebase-messaging/pull/16) (@sdb9696)
## [0.4.2](https://github.com/sdb9696/firebase-messaging/tree/0.4.2) (2024-09-25)
[Full Changelog](https://github.com/sdb9696/firebase-messaging/compare/0.4.1...0.4.2)
**Release highlights:**
Upgrades protobuf dependency to 5.28.
**Project maintenance:**
- Fix publish workflow to testpypi [\#15](https://github.com/sdb9696/firebase-messaging/pull/15) (@sdb9696)
- Update protobuf to 5.28 [\#12](https://github.com/sdb9696/firebase-messaging/pull/12) (@sdb9696)
## [0.4.1](https://github.com/sdb9696/firebase-messaging/tree/0.4.1) (2024-09-06)
[Full Changelog](https://github.com/sdb9696/firebase-messaging/compare/0.4.0...0.4.1)
**Release highlights:**
Migration to uv for project/package management
**Project maintenance:**
- Migrate from poetry to uv and enable testpypi publishing [\#9](https://github.com/sdb9696/firebase-messaging/pull/9) (@sdb9696)
## [0.4.0](https://github.com/sdb9696/firebase-messaging/tree/0.4.0) (2024-08-29)
[Full Changelog](https://github.com/sdb9696/firebase-messaging/compare/0.3.0...0.4.0)
**Release highlights:**
- Support for new FCM HTTP v1 API
- Previous versions of this library will no longer work due firebase [deprecating the legacy APIs](https://firebase.google.com/docs/cloud-messaging/migrate-v1)
- Dropping official python 3.8 support
- **Breaking** - this version of the library only supports being run in an asyncio event loop
- **Breaking** - The api has changed, see the readme for updated details
**Breaking change pull requests:**
- Drop python 3.8 support and update CI [\#5](https://github.com/sdb9696/firebase-messaging/pull/5) (@sdb9696)
**Implemented enhancements:**
- Support FCM HTTP v1 \(async only\) [\#4](https://github.com/sdb9696/firebase-messaging/pull/4) (@sdb9696)
**Project maintenance:**
- Update releasing instructions and add changelog [\#6](https://github.com/sdb9696/firebase-messaging/pull/6) (@sdb9696)
## [0.3.0](https://github.com/sdb9696/firebase-messaging/tree/0.3.0) (2024-03-26)
[Full Changelog](https://github.com/sdb9696/firebase-messaging/compare/0.2.1...0.3.0)
**Merged pull requests:**
- Make checkin async [\#2](https://github.com/sdb9696/firebase-messaging/pull/2) (@sdb9696)
## [0.2.1](https://github.com/sdb9696/firebase-messaging/releases/tag/0.2.1) - 2024-03-19
[Compare with 0.2.0](https://github.com/sdb9696/firebase-messaging/compare/0.2.0...0.2.1)
### Added
- Add typing ([ae3bc88](https://github.com/sdb9696/firebase-messaging/commit/ae3bc8821c1ca16fc6da00af0f0655851f6f848f) by sdb9696).
- Add ruff pre-commit hook ([bd98a4e](https://github.com/sdb9696/firebase-messaging/commit/bd98a4eea43ab0d63112f15f2ea3e2aa6c12f7c7) by sdb9696).
- Publisher verbose ([98cd5c4](https://github.com/sdb9696/firebase-messaging/commit/98cd5c4a40b12a42fc234d61076560a21bf46666) by sdb9696).
### Fixed
- Fix publisher ([6347dc2](https://github.com/sdb9696/firebase-messaging/commit/6347dc262df7f409099807df18db3e4550316106) by sdb9696).
- Fix cryptography warning in key generation ([5d4685b](https://github.com/sdb9696/firebase-messaging/commit/5d4685b9be3b66c3bff38ae6b4049094ba116ffb) by sdb9696).
- Fix broken proto file ([1bf3625](https://github.com/sdb9696/firebase-messaging/commit/1bf36259cd508bf6a58dc9f16138294aef235068) by sdb9696).
### Merged
- Merge pull request #1 from sdb9696/add_typing ([919eb97](https://github.com/sdb9696/firebase-messaging/commit/919eb97750dc3481130056ed6a4b9f4773b8da15) by Steven B).
## [0.2.0](https://github.com/sdb9696/firebase-messaging/releases/tag/0.2.0) - 2023-10-31
[Compare with 0.1.4](https://github.com/sdb9696/firebase-messaging/compare/0.1.4...0.2.0)
- Bump to 0.2.0, rename entry points and add run state for stability ([e3cbfda](https://github.com/sdb9696/firebase-messaging/commit/e3cbfda2f753e11029c437ec66720d836ccc0595) by sdb9696).
### Removed
- Remove need to be created in an event loop ([87daa6b](https://github.com/sdb9696/firebase-messaging/commit/87daa6b0078ef17131c3e64519b3042c559e3630) by sdb9696).
## [0.1.4](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.4) - 2023-10-25
[Compare with 0.1.3](https://github.com/sdb9696/firebase-messaging/compare/0.1.3...0.1.4)
- Relax protobuf dependency for HA ([9ac0bc6](https://github.com/sdb9696/firebase-messaging/commit/9ac0bc6d8212ea9a4fb4aa6cc412e7e760414dae) by sdb9696).
## [0.1.3](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.3) - 2023-10-25
[Compare with 0.1.2](https://github.com/sdb9696/firebase-messaging/compare/0.1.2...0.1.3)
- Bugfix python 3.9 async lock ([c2ee681](https://github.com/sdb9696/firebase-messaging/commit/c2ee68123ee4b8d5d62060b80ed746b2ec639b29) by sdb9696).
## [0.1.2](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.2) - 2023-10-25
[Compare with 0.1.1](https://github.com/sdb9696/firebase-messaging/compare/0.1.1...0.1.2)
### Fixed
- Update handling of no event loop and bump version ([29f3841](https://github.com/sdb9696/firebase-messaging/commit/29f38414eba0ed5893578c382eae558a826475de) by sdb9696).
## [0.1.1](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.1) - 2023-10-23
[Compare with 0.1.0](https://github.com/sdb9696/firebase-messaging/compare/0.1.0...0.1.1)
### Fixed
- Fix gcm checkin with credentials ([4c51098](https://github.com/sdb9696/firebase-messaging/commit/4c5109816b0d3fa266329bb36ec6fdfb02598ca3) by sdb9696).
## [0.1.0](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.0) - 2023-10-23
[Compare with first commit](https://github.com/sdb9696/firebase-messaging/compare/acf9b784788d68026d64d2f6d39a23274dbd663e...0.1.0)
### Added
- Add tests and docs and refactor ([77c225c](https://github.com/sdb9696/firebase-messaging/commit/77c225c142f1173ca2746c7a07de250b7d46e610) by sdb9696).
### Fixed
- Fix publish workflow ([447d379](https://github.com/sdb9696/firebase-messaging/commit/447d37922aa2589e79b3952036ef10b02debb01a) by sdb9696).
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
firebase-messaging-0.4.4/HISTORY.md 0000664 0000000 0000000 00000007361 14675551151 0016756 0 ustar 00root root 0000000 0000000 ## [0.2.1](https://github.com/sdb9696/firebase-messaging/releases/tag/0.2.1) - 2024-03-19
[Compare with 0.2.0](https://github.com/sdb9696/firebase-messaging/compare/0.2.0...0.2.1)
### Added
- Add typing ([ae3bc88](https://github.com/sdb9696/firebase-messaging/commit/ae3bc8821c1ca16fc6da00af0f0655851f6f848f) by sdb9696).
- Add ruff pre-commit hook ([bd98a4e](https://github.com/sdb9696/firebase-messaging/commit/bd98a4eea43ab0d63112f15f2ea3e2aa6c12f7c7) by sdb9696).
- Publisher verbose ([98cd5c4](https://github.com/sdb9696/firebase-messaging/commit/98cd5c4a40b12a42fc234d61076560a21bf46666) by sdb9696).
### Fixed
- Fix publisher ([6347dc2](https://github.com/sdb9696/firebase-messaging/commit/6347dc262df7f409099807df18db3e4550316106) by sdb9696).
- Fix cryptography warning in key generation ([5d4685b](https://github.com/sdb9696/firebase-messaging/commit/5d4685b9be3b66c3bff38ae6b4049094ba116ffb) by sdb9696).
- Fix broken proto file ([1bf3625](https://github.com/sdb9696/firebase-messaging/commit/1bf36259cd508bf6a58dc9f16138294aef235068) by sdb9696).
### Merged
- Merge pull request #1 from sdb9696/add_typing ([919eb97](https://github.com/sdb9696/firebase-messaging/commit/919eb97750dc3481130056ed6a4b9f4773b8da15) by Steven B).
## [0.2.0](https://github.com/sdb9696/firebase-messaging/releases/tag/0.2.0) - 2023-10-31
[Compare with 0.1.4](https://github.com/sdb9696/firebase-messaging/compare/0.1.4...0.2.0)
- Bump to 0.2.0, rename entry points and add run state for stability ([e3cbfda](https://github.com/sdb9696/firebase-messaging/commit/e3cbfda2f753e11029c437ec66720d836ccc0595) by sdb9696).
### Removed
- Remove need to be created in an event loop ([87daa6b](https://github.com/sdb9696/firebase-messaging/commit/87daa6b0078ef17131c3e64519b3042c559e3630) by sdb9696).
## [0.1.4](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.4) - 2023-10-25
[Compare with 0.1.3](https://github.com/sdb9696/firebase-messaging/compare/0.1.3...0.1.4)
- Relax protobuf dependency for HA ([9ac0bc6](https://github.com/sdb9696/firebase-messaging/commit/9ac0bc6d8212ea9a4fb4aa6cc412e7e760414dae) by sdb9696).
## [0.1.3](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.3) - 2023-10-25
[Compare with 0.1.2](https://github.com/sdb9696/firebase-messaging/compare/0.1.2...0.1.3)
- Bugfix python 3.9 async lock ([c2ee681](https://github.com/sdb9696/firebase-messaging/commit/c2ee68123ee4b8d5d62060b80ed746b2ec639b29) by sdb9696).
## [0.1.2](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.2) - 2023-10-25
[Compare with 0.1.1](https://github.com/sdb9696/firebase-messaging/compare/0.1.1...0.1.2)
### Fixed
- Update handling of no event loop and bump version ([29f3841](https://github.com/sdb9696/firebase-messaging/commit/29f38414eba0ed5893578c382eae558a826475de) by sdb9696).
## [0.1.1](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.1) - 2023-10-23
[Compare with 0.1.0](https://github.com/sdb9696/firebase-messaging/compare/0.1.0...0.1.1)
### Fixed
- Fix gcm checkin with credentials ([4c51098](https://github.com/sdb9696/firebase-messaging/commit/4c5109816b0d3fa266329bb36ec6fdfb02598ca3) by sdb9696).
## [0.1.0](https://github.com/sdb9696/firebase-messaging/releases/tag/0.1.0) - 2023-10-23
[Compare with first commit](https://github.com/sdb9696/firebase-messaging/compare/acf9b784788d68026d64d2f6d39a23274dbd663e...0.1.0)
### Added
- Add tests and docs and refactor ([77c225c](https://github.com/sdb9696/firebase-messaging/commit/77c225c142f1173ca2746c7a07de250b7d46e610) by sdb9696).
### Fixed
- Fix publish workflow ([447d379](https://github.com/sdb9696/firebase-messaging/commit/447d37922aa2589e79b3952036ef10b02debb01a) by sdb9696).
firebase-messaging-0.4.4/LICENSE 0000664 0000000 0000000 00000002120 14675551151 0016264 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2017 Matthieu Lemoine
Copyright (c) 2023 Steven Beth
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.
firebase-messaging-0.4.4/README.rst 0000664 0000000 0000000 00000005560 14675551151 0016761 0 ustar 00root root 0000000 0000000 ==================
Firebase Messaging
==================
.. image:: https://badge.fury.io/py/firebase-messaging.svg
:alt: PyPI Version
:target: https://badge.fury.io/py/firebase-messaging
.. image:: https://github.com/sdb9696/firebase-messaging/actions/workflows/ci.yml/badge.svg?branch=main
:alt: Build Status
:target: https://github.com/sdb9696/firebase-messaging/actions/workflows/ci.yml?branch=main
.. image:: https://coveralls.io/repos/github/sdb9696/firebase-messaging/badge.svg?branch=main
:alt: Coverage
:target: https://coveralls.io/github/sdb9696/firebase-messaging?branch=main
.. image:: https://readthedocs.org/projects/firebase-messaging/badge/?version=latest
:alt: Documentation Status
:target: https://firebase-messaging.readthedocs.io/?badge=latest
.. image:: https://img.shields.io/pypi/pyversions/firebase-messaging.svg
:alt: Py Versions
:target: https://pypi.python.org/pypi/firebase-messaging#
A library to subscribe to GCM/FCM and receive notifications within a python application.
When should I use `firebase-messaging` ?
----------------------------------------
- I want to **receive** push notifications sent using Firebase Cloud Messaging in a python application.
When should I not use `firebase-messaging` ?
--------------------------------------------
- I want to **send** push notifications (use the firebase SDK instead)
- My application is running on a FCM supported platform (Android, iOS, Web).
Install
-------
PyPi::
$ pip install firebase-messaging
Requirements
------------
- Firebase configuration to receive notifications
Usage
-----
Must be run inside an asyncio event loop.
python::
from firebase_messaging import FcmPushClient, FcmRegisterConfig
def on_notification(obj, notification, data_message):
# Do something with the notification
pass
credentials = None # Start off with none or load from previous save
def on_credentials_updated(creds):
# save the credentials to a file here for future use
fcm_config = FcmRegisterConfig(fcm-project-id, fcm-app-id, fcm-api-key, fcm-message-sender-id)
pc = FcmPushClient(on_notification, fcm_config, credentials, on_credentials_updated)
fcm_token = await pc.checkin_or_register()
await pc.start()
# Adapt the following for your usage
while some_condition_to_keep_listening:
asyncio.sleep(2)
Attribution
-----------
Code originally based on typescript/node implementation by
`Matthieu Lemoine `_.
See `this blog post `_ for more details.
Converted to python by
`lolisamurai `_
http decryption logic in decrypt.py by
`Martin Thomson `_
firebase-messaging-0.4.4/RELEASING.md 0000664 0000000 0000000 00000011533 14675551151 0017122 0 ustar 00root root 0000000 0000000 # Releasing
## Requirements
* [github client](https://github.com/cli/cli#installation)
* [gitchub_changelog_generator](https://github.com/github-changelog-generator)
* [github access token](https://github.com/github-changelog-generator/github-changelog-generator#github-token)
## Export changelog token
```bash
export CHANGELOG_GITHUB_TOKEN=token
```
## Set release information
```bash
export NEW_RELEASE=x.x.x
```
## Normal releases from main
### Create a branch for the release
```bash
git checkout main
git fetch origin main
git rebase origin/main
git checkout -b release/$NEW_RELEASE
```
### Update the version number
```bash
sed -i "0,/version = /{s/version = .*/version = \"${NEW_RELEASE}\"/}" pyproject.toml
```
### Update dependencies
```bash
uv sync --all-extras
uv lock --upgrade
```
### Run pre-commit and tests
```bash
uv run pre-commit run --all-files
uv run pytest
```
### Create release summary (skip for dev releases)
Write a short and understandable summary for the release. Can include images.
#### Create $NEW_RELEASE milestone in github
If not already created
#### Create new issue linked to the milestone
```bash
gh issue create --label "release-summary" --milestone $NEW_RELEASE --title "$NEW_RELEASE Release Summary" --body "**Release highlights:**"
```
You can exclude the --body option to get an interactive editor or go into the issue on github and edit there.
#### Close the issue
Either via github or:
```bash
gh issue close ISSUE_NUMBER
```
### Generate changelog
Configuration settings are in `.github_changelog_generator`
#### For pre-release
EXCLUDE_TAGS will exclude all dev tags except for the current release dev tags.
Regex should be something like this `^((?!0\.9\.0)(.*dev\d))+`. The first match group negative matches on the current release and the second matches on releases ending with dev.
```bash
EXCLUDE_TAGS=${NEW_RELEASE%.dev*}; EXCLUDE_TAGS=${EXCLUDE_TAGS//"."/"\."}; EXCLUDE_TAGS="^((?!"$EXCLUDE_TAGS")(.*dev\d))+"
echo "$EXCLUDE_TAGS"
github_changelog_generator --future-release $NEW_RELEASE --exclude-tags-regex "$EXCLUDE_TAGS"
```
#### For production
```bash
github_changelog_generator --future-release $NEW_RELEASE --exclude-tags-regex 'dev\d$'
```
You can ignore warnings about missing PR commits like below as these relate to PRs to branches other than main:
```
Warning: PR 111 merge commit was not found in the release branch or tagged git history and no rebased SHA comment was found
```
### Export new release notes to variable
```bash
export RELEASE_NOTES=$(grep -Poz '(?<=\# Changelog\n\n)(.|\n)+?(?=\#\#)' CHANGELOG.md | tr '\0' '\n' )
echo "$RELEASE_NOTES" # Check the output and copy paste if neccessary
```
### Commit and push the changed files
```bash
git commit --all --verbose -m "Prepare $NEW_RELEASE"
git push origin release/$NEW_RELEASE -u
```
### Create a PR for the release, merge it, and re-fetch the main
#### Create the PR
```
gh pr create --title "Prepare $NEW_RELEASE" --body "$RELEASE_NOTES" --label release-prep --base main
```
#### Merge the PR once the CI passes
Create a squash commit and add the markdown from the PR description to the commit description.
```bash
gh pr merge --squash --body "$RELEASE_NOTES"
```
### Rebase local main
```bash
git checkout main
git fetch origin main
git rebase origin/main
```
### Create a release tag
Note, add changelog release notes as the tag commit message so `gh release create --notes-from-tag` can be used to create a release draft.
```bash
git tag --annotate $NEW_RELEASE -m "$RELEASE_NOTES" # to create a signed tag replace --annotate with --sign
git push origin $NEW_RELEASE
```
### Approve the release workflow
This will automatically deploy to pypi
## Updating protobuf version
Skip this part under normal circumstances.
Only required if protobuf minimum dependency is updated.
### Update protobuf version
```bash
export PROTOBUF_VERSION=x.x.0
uv add "protobuf>$PROTOBUF_VERSION"
uv add --dev "types-protobuf>$PROTOBUF_VERSION"
```
### Download and unzip latest protoc compiler
Replace download url with correct version/platform
```bash
cd /var/tmp
wget https://github.com/protocolbuffers/protobuf/releases/download/v28.0/protoc-28.0-linux-x86_64.zip
unzip protoc-28.0-linux-x86_64.zip
sudo cp bin/protoc /usr/local/bin/
cd
protoc --version # check version as expected
```
### Update generated python files
```bash
export PROTO_DIR="firebase_messaging/proto"
protoc --proto_path=$PROTO_DIR --python_out=$PROTO_DIR $PROTO_DIR/android_checkin.proto $PROTO_DIR/checkin.proto $PROTO_DIR/mcs.proto
protoc --proto_path=$PROTO_DIR --pyi_out=$PROTO_DIR $PROTO_DIR/android_checkin.proto $PROTO_DIR/checkin.proto $PROTO_DIR/mcs.proto
```
### Fix relative import
`protoc` doesn't do relative imports https://github.com/protocolbuffers/protobuf/issues/1491
In `checkin_pb2.py` and `checkin_pb2.pyi` put `from . ` in front of `import android_checkin_pb2 ...`
firebase-messaging-0.4.4/docs/ 0000775 0000000 0000000 00000000000 14675551151 0016214 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/docs/Makefile 0000664 0000000 0000000 00000001176 14675551151 0017661 0 ustar 00root root 0000000 0000000 # Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
firebase-messaging-0.4.4/docs/make.bat 0000664 0000000 0000000 00000001401 14675551151 0017615 0 ustar 00root root 0000000 0000000 @ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
firebase-messaging-0.4.4/docs/source/ 0000775 0000000 0000000 00000000000 14675551151 0017514 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/docs/source/api.rst 0000664 0000000 0000000 00000000323 14675551151 0021015 0 ustar 00root root 0000000 0000000 .. py:module:: firebase_messaging
API Documentation
*****************
.. autoclass:: FcmPushClientConfig
:members:
:undoc-members: false
.. autoclass:: FcmPushClient
:members:
:undoc-members:
firebase-messaging-0.4.4/docs/source/changelog.md 0000664 0000000 0000000 00000000040 14675551151 0021757 0 ustar 00root root 0000000 0000000 :::{include} ../../CHANGELOG.md
firebase-messaging-0.4.4/docs/source/conf.py 0000664 0000000 0000000 00000002421 14675551151 0021012 0 ustar 00root root 0000000 0000000 # Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
from importlib.metadata import version as _version
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = "firebase-messaging"
copyright = "2023, Steven Beth"
author = "Steven Beth"
release = _version("firebase_messaging")
version = _version("firebase_messaging")
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
"sphinx.ext.autodoc",
"sphinx_autodoc_typehints",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
"sphinx.ext.todo",
"myst_parser",
]
myst_enable_extensions = [
"colon_fence",
]
templates_path = ["_templates"]
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = "sphinx_rtd_theme"
autodoc_member_order = "bysource"
# html_static_path = ["_static"]
firebase-messaging-0.4.4/docs/source/index.rst 0000664 0000000 0000000 00000000706 14675551151 0021360 0 ustar 00root root 0000000 0000000 .. python-ring-doorbell documentation master file, created by
sphinx-quickstart on Fri Sep 22 17:28:31 2023.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to python-ring-doorbell's documentation!
================================================
.. include:: ../../README.rst
.. toctree::
:hidden:
:titlesonly:
:maxdepth: 0
Home
api
changelog
firebase-messaging-0.4.4/firebase_messaging/ 0000775 0000000 0000000 00000000000 14675551151 0021101 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/firebase_messaging/__init__.py 0000664 0000000 0000000 00000000365 14675551151 0023216 0 ustar 00root root 0000000 0000000 from .fcmpushclient import FcmPushClient, FcmPushClientConfig, FcmPushClientRunState
from .fcmregister import FcmRegisterConfig
__all__ = [
"FcmPushClientConfig",
"FcmPushClient",
"FcmPushClientRunState",
"FcmRegisterConfig",
]
firebase-messaging-0.4.4/firebase_messaging/const.py 0000664 0000000 0000000 00000002247 14675551151 0022606 0 ustar 00root root 0000000 0000000 """Constants module."""
GCM_REGISTER_URL = "https://android.clients.google.com/c2dm/register3"
GCM_CHECKIN_URL = "https://android.clients.google.com/checkin"
GCM_SERVER_KEY_BIN = (
b"\x04\x33\x94\xf7\xdf\xa1\xeb\xb1\xdc\x03\xa2\x5e\x15\x71\xdb\x48\xd3"
+ b"\x2e\xed\xed\xb2\x34\xdb\xb7\x47\x3a\x0c\x8f\xc4\xcc\xe1\x6f\x3c"
+ b"\x8c\x84\xdf\xab\xb6\x66\x3e\xf2\x0c\xd4\x8b\xfe\xe3\xf9\x76\x2f"
+ b"\x14\x1c\x63\x08\x6a\x6f\x2d\xb1\x1a\x95\xb0\xce\x37\xc0\x9c\x6e"
)
# urlsafe b64 encoding of the binary key with = padding removed
GCM_SERVER_KEY_B64 = (
"BDOU99-h67HcA6JeFXHbSNMu7e2yNNu3RzoM"
+ "j8TM4W88jITfq7ZmPvIM1Iv-4_l2LxQcYwhqby2xGpWwzjfAnG4"
)
FCM_SUBSCRIBE_URL = "https://fcm.googleapis.com/fcm/connect/subscribe/"
FCM_SEND_URL = "https://fcm.googleapis.com/fcm/send/"
FCM_API = "https://fcm.googleapis.com/v1/"
FCM_REGISTRATION = "https://fcmregistrations.googleapis.com/v1/"
FCM_INSTALLATION = "https://firebaseinstallations.googleapis.com/v1/"
AUTH_VERSION = "FIS_v2"
SDK_VERSION = "w:0.6.6"
DOORBELLS_ENDPOINT = "/clients_api/doorbots/{0}"
MCS_VERSION = 41
MCS_HOST = "mtalk.google.com"
MCS_PORT = 5228
MCS_SELECTIVE_ACK_ID = 12
MCS_STREAM_ACK_ID = 13
firebase-messaging-0.4.4/firebase_messaging/fcmpushclient.py 0000664 0000000 0000000 00000071105 14675551151 0024323 0 ustar 00root root 0000000 0000000 from __future__ import annotations
import asyncio
import contextlib
import json
import logging
import ssl
import struct
import time
import traceback
from base64 import urlsafe_b64decode
from contextlib import suppress as contextlib_suppress
from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING, Any, Callable
from aiohttp import ClientSession
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_der_private_key
from google.protobuf.json_format import MessageToJson
from google.protobuf.message import Message
from http_ece import decrypt as http_decrypt # type: ignore[import-untyped]
from .const import (
MCS_HOST,
MCS_PORT,
MCS_SELECTIVE_ACK_ID,
MCS_VERSION,
)
from .fcmregister import FcmRegister, FcmRegisterConfig
from .proto.mcs_pb2 import ( # pylint: disable=no-name-in-module
Close,
DataMessageStanza,
HeartbeatAck,
HeartbeatPing,
IqStanza,
LoginRequest,
LoginResponse,
SelectiveAck,
StreamErrorStanza,
)
_logger = logging.getLogger(__name__)
OnNotificationCallable = Callable[[dict[str, Any], str, Any], None]
CredentialsUpdatedCallable = Callable[[dict[str, Any]], None]
# MCS Message Types and Tags
MCS_MESSAGE_TAG = {
HeartbeatPing: 0,
HeartbeatAck: 1,
LoginRequest: 2,
LoginResponse: 3,
Close: 4,
"MessageStanza": 5,
"PresenceStanza": 6,
IqStanza: 7,
DataMessageStanza: 8,
"BatchPresenceStanza": 9,
StreamErrorStanza: 10,
"HttpRequest": 11,
"HttpResponse": 12,
"BindAccountRequest": 13,
"BindAccountResponse": 14,
"TalkMetadata": 15,
}
class ErrorType(Enum):
CONNECTION = 1
READ = 2
LOGIN = 3
NOTIFY = 4
class FcmPushClientRunState(Enum):
CREATED = (1,)
STARTING_TASKS = (2,)
STARTING_CONNECTION = (3,)
STARTING_LOGIN = (4,)
STARTED = (5,)
RESETTING = (6,)
STOPPING = (7,)
STOPPED = (8,)
@dataclass
class FcmPushClientConfig: # pylint:disable=too-many-instance-attributes
"""Class to provide configuration to
:class:`firebase_messaging.FcmPushClientConfig`.FcmPushClient."""
server_heartbeat_interval: int | None = 10
"""Time in seconds to request the server to send heartbeats"""
client_heartbeat_interval: int | None = 20
"""Time in seconds to send heartbeats to the server"""
send_selective_acknowledgements: bool = True
"""True to send selective acknowledgements for each message received.
Currently if false the client does not send any acknowlegements."""
connection_retry_count: int = 5
"""Number of times to retry the connection before giving up."""
start_seconds_before_retry_connect: float = 3
"""Time in seconds to wait before attempting to retry
the connection after failure."""
reset_interval: float = 3
"""Time in seconds to wait between resets after errors or disconnection."""
heartbeat_ack_timeout: float = 5
"""Time in seconds to wait for a heartbeat ack before resetting."""
abort_on_sequential_error_count: int | None = 3
"""Number of sequential errors of the same time to wait before aborting.
If set to None the client will not abort."""
monitor_interval: float = 1
"""Time in seconds for the monitor task to fire and check for heartbeats,
stale connections and shut down of the main event loop."""
log_warn_limit: int | None = 5
"""Number of times to log specific warning messages before going silent for
a specific warning type."""
log_debug_verbose: bool = False
"""Set to True to log all message info including tokens."""
class FcmPushClient: # pylint:disable=too-many-instance-attributes
"""Client that connects to Firebase Cloud Messaging and receives messages.
:param credentials: credentials object returned by register()
:param credentials_updated_callback: callback when new credentials are
created to allow client to store them
:param received_persistent_ids: any persistent id's you already received.
:param config: configuration class of
:class:`firebase_messaging.FcmPushClientConfig`
"""
def __init__(
self,
callback: Callable[[dict, str, Any | None], None],
fcm_config: FcmRegisterConfig,
credentials: dict | None = None,
credentials_updated_callback: CredentialsUpdatedCallable | None = None,
*,
callback_context: object | None = None,
received_persistent_ids: list[str] | None = None,
config: FcmPushClientConfig | None = None,
http_client_session: ClientSession | None = None,
):
"""Initializes the receiver."""
self.callback = callback
self.callback_context = callback_context
self.fcm_config = fcm_config
self.credentials = credentials
self.credentials_updated_callback = credentials_updated_callback
self.persistent_ids = received_persistent_ids if received_persistent_ids else []
self.config = config if config else FcmPushClientConfig()
if self.config.log_debug_verbose:
_logger.setLevel(logging.DEBUG)
self._http_client_session = http_client_session
self.reader: asyncio.StreamReader | None = None
self.writer: asyncio.StreamWriter | None = None
self.do_listen = False
self.sequential_error_counters: dict[ErrorType, int] = {}
self.log_warn_counters: dict[str, int] = {}
# reset variables
self.input_stream_id = 0
self.last_input_stream_id_reported = -1
self.first_message = True
self.last_login_time: float | None = None
self.last_message_time: float | None = None
self.run_state: FcmPushClientRunState = FcmPushClientRunState.CREATED
self.tasks: list[asyncio.Task] = []
self.reset_lock: asyncio.Lock | None = None
self.stopping_lock: asyncio.Lock | None = None
def _msg_str(self, msg: Message) -> str:
if self.config.log_debug_verbose:
return type(msg).__name__ + "\n" + MessageToJson(msg, indent=4)
return type(msg).__name__
def _log_verbose(self, msg: str, *args: object) -> None:
if self.config.log_debug_verbose:
_logger.debug(msg, *args)
def _log_warn_with_limit(self, msg: str, *args: object) -> None:
if msg not in self.log_warn_counters:
self.log_warn_counters[msg] = 0
if (
self.config.log_warn_limit
and self.config.log_warn_limit > self.log_warn_counters[msg]
):
self.log_warn_counters[msg] += 1
_logger.warning(msg, *args)
async def _do_writer_close(self) -> None:
writer = self.writer
self.writer = None
if writer:
writer.close()
with contextlib.suppress(Exception):
await writer.wait_closed()
async def _reset(self) -> None:
if (
(self.reset_lock and self.reset_lock.locked())
or (self.stopping_lock and self.stopping_lock.locked())
or not self.do_listen
):
return
async with self.reset_lock: # type: ignore[union-attr]
_logger.debug("Resetting connection")
self.run_state = FcmPushClientRunState.RESETTING
await self._do_writer_close()
now = time.time()
time_since_last_login = now - self.last_login_time # type: ignore[operator]
if time_since_last_login < self.config.reset_interval:
_logger.debug("%ss since last reset attempt.", time_since_last_login)
await asyncio.sleep(self.config.reset_interval - time_since_last_login)
_logger.debug("Reestablishing connection")
if not await self._connect_with_retry():
_logger.error(
"Unable to connect to MCS endpoint "
+ "after %s tries, shutting down",
self.config.connection_retry_count,
)
self._terminate()
return
_logger.debug("Re-connected to ssl socket")
await self._login()
# protobuf variable length integers are encoded in base 128
# each byte contains 7 bits of the integer and the msb is set if there's
# more. pretty simple to implement
async def _read_varint32(self) -> int:
res = 0
shift = 0
while True:
r = await self.reader.readexactly(1) # type: ignore[union-attr]
(b,) = struct.unpack("B", r)
res |= (b & 0x7F) << shift
if (b & 0x80) == 0:
break
shift += 7
return res
@staticmethod
def _encode_varint32(x: int) -> bytes:
if x == 0:
return bytes(bytearray([0]))
res = bytearray([])
while x != 0:
b = x & 0x7F
x >>= 7
if x != 0:
b |= 0x80
res.append(b)
return bytes(res)
@staticmethod
def _make_packet(msg: Message, include_version: bool) -> bytes:
tag = MCS_MESSAGE_TAG[type(msg)]
header = bytearray([MCS_VERSION, tag]) if include_version else bytearray([tag])
payload = msg.SerializeToString()
buf = bytes(header) + FcmPushClient._encode_varint32(len(payload)) + payload
return buf
async def _send_msg(self, msg: Message) -> None:
self._log_verbose("Sending packet to server: %s", self._msg_str(msg))
buf = FcmPushClient._make_packet(msg, self.first_message)
self.writer.write(buf) # type: ignore[union-attr]
await self.writer.drain() # type: ignore[union-attr]
async def _receive_msg(self) -> Message | None:
if self.first_message:
r = await self.reader.readexactly(2) # type: ignore[union-attr]
version, tag = struct.unpack("BB", r)
if version < MCS_VERSION and version != 38:
raise RuntimeError(f"protocol version {version} unsupported")
self.first_message = False
else:
r = await self.reader.readexactly(1) # type: ignore[union-attr]
(tag,) = struct.unpack("B", r)
size = await self._read_varint32()
self._log_verbose(
"Received message with tag %s and size %s",
tag,
size,
)
if not size >= 0:
self._log_warn_with_limit("Unexpected message size %s", size)
return None
buf = await self.reader.readexactly(size) # type: ignore[union-attr]
msg_class = next(iter([c for c, t in MCS_MESSAGE_TAG.items() if t == tag]))
if not msg_class:
self._log_warn_with_limit("Unexpected message tag %s", tag)
return None
if isinstance(msg_class, str):
self._log_warn_with_limit("Unconfigured message class %s", msg_class)
return None
payload = msg_class() # type: ignore[operator]
payload.ParseFromString(buf)
self._log_verbose("Received payload: %s", self._msg_str(payload))
return payload
async def _login(self) -> None:
self.run_state = FcmPushClientRunState.STARTING_LOGIN
now = time.time()
self.input_stream_id = 0
self.last_input_stream_id_reported = -1
self.first_message = True
self.last_login_time = now
try:
android_id = self.credentials["gcm"]["android_id"] # type: ignore[index]
req = LoginRequest()
req.adaptive_heartbeat = False
req.auth_service = LoginRequest.ANDROID_ID # 2
req.auth_token = self.credentials["gcm"]["security_token"] # type: ignore[index]
req.id = self.fcm_config.chrome_version
req.domain = "mcs.android.com"
req.device_id = f"android-{int(android_id):x}"
req.network_type = 1
req.resource = android_id
req.user = android_id
req.use_rmq2 = True
req.setting.add(name="new_vc", value="1")
req.received_persistent_id.extend(self.persistent_ids)
if (
self.config.server_heartbeat_interval
and self.config.server_heartbeat_interval > 0
):
req.heartbeat_stat.ip = ""
req.heartbeat_stat.timeout = True
req.heartbeat_stat.interval_ms = (
1000 * self.config.server_heartbeat_interval
)
await self._send_msg(req)
_logger.debug("Sent login request")
except Exception as ex:
_logger.error("Received an exception logging in: %s", ex)
if self._try_increment_error_count(ErrorType.LOGIN):
await self._reset()
@staticmethod
def _decrypt_raw_data(
credentials: dict[str, dict[str, str]],
crypto_key_str: str,
salt_str: str,
raw_data: bytes,
) -> bytes:
crypto_key = urlsafe_b64decode(crypto_key_str.encode("ascii"))
salt = urlsafe_b64decode(salt_str.encode("ascii"))
der_data_str = credentials["keys"]["private"]
der_data = urlsafe_b64decode(der_data_str.encode("ascii") + b"========")
secret_str = credentials["keys"]["secret"]
secret = urlsafe_b64decode(secret_str.encode("ascii") + b"========")
privkey = load_der_private_key(
der_data, password=None, backend=default_backend()
)
decrypted = http_decrypt(
raw_data,
salt=salt,
private_key=privkey,
dh=crypto_key,
version="aesgcm",
auth_secret=secret,
)
return decrypted
def _app_data_by_key(
self, p: DataMessageStanza, key: str, do_not_raise: bool = False
) -> str:
for x in p.app_data:
if x.key == key:
return x.value
if do_not_raise:
return ""
raise RuntimeError(f"couldn't find in app_data {key}")
def _handle_data_message(
self,
msg: DataMessageStanza,
) -> None:
_logger.debug(
"Received data message Stream ID: %s, Last: %s, Status: %s",
msg.stream_id,
msg.last_stream_id_received,
msg.status,
)
if (
self._app_data_by_key(msg, "message_type", do_not_raise=True)
== "deleted_messages"
):
# The deleted_messages message does not contain data.
return
crypto_key = self._app_data_by_key(msg, "crypto-key")[3:] # strip dh=
salt = self._app_data_by_key(msg, "encryption")[5:] # strip salt=
subtype = self._app_data_by_key(msg, "subtype")
if TYPE_CHECKING:
assert self.credentials
if subtype != self.credentials["gcm"]["app_id"]:
self._log_warn_with_limit(
"Subtype %s in data message does not match"
+ "app id client was registered with %s",
subtype,
self.credentials["gcm"]["app_id"],
)
if not self.credentials:
return
decrypted = self._decrypt_raw_data(
self.credentials, crypto_key, salt, msg.raw_data
)
with contextlib_suppress(json.JSONDecodeError, ValueError):
decrypted_json = json.loads(decrypted.decode("utf-8"))
ret_val = decrypted_json if decrypted_json else decrypted
self._log_verbose(
"Decrypted data for message %s is: %s", msg.persistent_id, ret_val
)
try:
self.callback(ret_val, msg.persistent_id, self.callback_context)
self._reset_error_count(ErrorType.NOTIFY)
except Exception:
_logger.exception("Unexpected exception calling notification callback\n")
self._try_increment_error_count(ErrorType.NOTIFY)
def _new_input_stream_id_available(self) -> bool:
return self.last_input_stream_id_reported != self.input_stream_id
def _get_input_stream_id(self) -> int:
self.last_input_stream_id_reported = self.input_stream_id
return self.input_stream_id
async def _handle_ping(self, p: HeartbeatPing) -> None:
_logger.debug(
"Received heartbeat ping, sending ack: Stream ID: %s, Last: %s, Status: %s",
p.stream_id,
p.last_stream_id_received,
p.status,
)
req = HeartbeatAck()
if self._new_input_stream_id_available():
req.last_stream_id_received = self._get_input_stream_id()
await self._send_msg(req)
async def _handle_iq(self, p: IqStanza) -> None:
if not p.extension:
self._log_warn_with_limit(
"Unexpected IqStanza id received with no extension", str(p)
)
return
if p.extension.id not in (12, 13):
self._log_warn_with_limit(
"Unexpected extension id received: %s", p.extension.id
)
return
async def _send_selective_ack(self, persistent_id: str) -> None:
iqs = IqStanza()
iqs.type = IqStanza.IqType.SET
iqs.id = ""
iqs.extension.id = MCS_SELECTIVE_ACK_ID
sa = SelectiveAck()
sa.id.extend([persistent_id])
iqs.extension.data = sa.SerializeToString()
_logger.debug("Sending selective ack for message id %s", persistent_id)
await self._send_msg(iqs)
async def _send_heartbeat(self) -> None:
req = HeartbeatPing()
if self._new_input_stream_id_available():
req.last_stream_id_received = self._get_input_stream_id()
await self._send_msg(req)
_logger.debug("Sent heartbeat ping")
def _terminate(self) -> None:
self.run_state = FcmPushClientRunState.STOPPING
self.do_listen = False
current_task = asyncio.current_task()
for task in self.tasks:
if (
current_task != task and not task.done()
): # cancel return if task is done so no need to check
task.cancel()
async def _do_monitor(self) -> None:
while self.do_listen:
await asyncio.sleep(self.config.monitor_interval)
if self.run_state == FcmPushClientRunState.STARTED:
# if server_heartbeat_interval is set and less than
# client_heartbeat_interval then the last_message_time
# will be within the client window if connected
if self.config.client_heartbeat_interval:
now = time.time()
if (
self.last_message_time + self.config.client_heartbeat_interval # type: ignore[operator]
< now
):
await self._send_heartbeat()
await asyncio.sleep(self.config.heartbeat_ack_timeout)
now = time.time()
if ( # Check state hasn't changed during sleep
self.last_message_time # type: ignore[operator]
+ self.config.client_heartbeat_interval
< now
and self.do_listen
and self.run_state == FcmPushClientRunState.STARTED
):
await self._reset()
elif self.config.server_heartbeat_interval:
now = time.time()
if ( # We give the server 2 extra seconds
self.last_message_time + self.config.server_heartbeat_interval # type: ignore[operator]
< now - 2
):
await self._reset()
def _reset_error_count(self, error_type: ErrorType) -> None:
self.sequential_error_counters[error_type] = 0
def _try_increment_error_count(self, error_type: ErrorType) -> bool:
if error_type not in self.sequential_error_counters:
self.sequential_error_counters[error_type] = 0
self.sequential_error_counters[error_type] += 1
if (
self.config.abort_on_sequential_error_count
and self.sequential_error_counters[error_type]
>= self.config.abort_on_sequential_error_count
):
_logger.error(
"Shutting down push receiver due to "
+ f"{self.sequential_error_counters[error_type]} sequential"
+ f" errors of type {error_type}"
)
self._terminate()
return False
return True
async def _handle_message(self, msg: Message) -> None:
self.last_message_time = time.time()
self.input_stream_id += 1
if isinstance(msg, Close):
self._log_warn_with_limit("Server sent Close message, resetting")
if self._try_increment_error_count(ErrorType.CONNECTION):
await self._reset()
return
if isinstance(msg, LoginResponse):
if str(msg.error):
_logger.error("Received login error response: %s", msg)
if self._try_increment_error_count(ErrorType.LOGIN):
await self._reset()
else:
_logger.info("Succesfully logged in to MCS endpoint")
self._reset_error_count(ErrorType.LOGIN)
self.run_state = FcmPushClientRunState.STARTED
self.persistent_ids = []
return
if isinstance(msg, DataMessageStanza):
self._handle_data_message(msg)
self.persistent_ids.append(msg.persistent_id)
if self.config.send_selective_acknowledgements:
await self._send_selective_ack(msg.persistent_id)
elif isinstance(msg, HeartbeatPing):
await self._handle_ping(msg)
elif isinstance(msg, HeartbeatAck):
_logger.debug("Received heartbeat ack: %s", msg)
elif isinstance(msg, IqStanza):
pass
else:
self._log_warn_with_limit("Unexpected message type %s.", type(msg).__name__)
# Reset error count if a read has been succesful
self._reset_error_count(ErrorType.READ)
self._reset_error_count(ErrorType.CONNECTION)
@staticmethod
async def _open_connection(
host: str, port: int, ssl_context: ssl.SSLContext
) -> tuple[asyncio.StreamReader, asyncio.StreamWriter]:
return await asyncio.open_connection(host=host, port=port, ssl=ssl_context)
async def _connect(self) -> bool:
try:
loop = asyncio.get_running_loop()
# create_default_context() blocks the event loop
ssl_context = await loop.run_in_executor(None, ssl.create_default_context)
self.reader, self.writer = await self._open_connection(
host=MCS_HOST, port=MCS_PORT, ssl_context=ssl_context
)
_logger.debug("Connected to MCS endpoint (%s,%s)", MCS_HOST, MCS_PORT)
return True
except OSError as oex:
_logger.error(
"Could not connected to MCS endpoint (%s,%s): %s",
MCS_HOST,
MCS_PORT,
oex,
)
return False
async def _connect_with_retry(self) -> bool:
self.run_state = FcmPushClientRunState.STARTING_CONNECTION
trycount = 0
connected = False
while (
trycount < self.config.connection_retry_count
and not connected
and self.do_listen
):
trycount += 1
connected = await self._connect()
if not connected:
sleep_time = (
self.config.start_seconds_before_retry_connect * trycount * trycount
)
_logger.info(
"Could not connect to MCS Endpoint on "
+ "try %s, sleeping for %s seconds",
trycount,
sleep_time,
)
await asyncio.sleep(sleep_time)
if not connected:
_logger.error(
"Unable to connect to MCS endpoint after %s tries, aborting", trycount
)
return connected
async def _listen(self) -> None:
"""listens for push notifications."""
if not await self._connect_with_retry():
return
try:
await self._login()
while self.do_listen:
try:
if self.run_state == FcmPushClientRunState.RESETTING:
await asyncio.sleep(1)
elif msg := await self._receive_msg():
await self._handle_message(msg)
except (OSError, EOFError) as osex:
if (
isinstance(
osex,
(
ConnectionResetError,
TimeoutError,
asyncio.IncompleteReadError,
ssl.SSLError,
),
)
and self.run_state == FcmPushClientRunState.RESETTING
):
if (
isinstance(osex, ssl.SSLError) # pylint: disable=no-member
and osex.reason != "APPLICATION_DATA_AFTER_CLOSE_NOTIFY"
):
self._log_warn_with_limit(
"Unexpected SSLError reason during reset of %s",
osex.reason,
)
else:
self._log_verbose(
"Expected read error during reset: %s",
type(osex).__name__,
)
else:
_logger.exception("Unexpected exception during read\n")
if self._try_increment_error_count(ErrorType.CONNECTION):
await self._reset()
except Exception as ex:
_logger.error(
"Unknown error: %s, shutting down FcmPushClient.\n%s",
ex,
traceback.format_exc(),
)
self._terminate()
finally:
await self._do_writer_close()
async def checkin_or_register(self) -> str:
"""Check in if you have credentials otherwise register as a new client.
:param sender_id: sender id identifying push service you are connecting to.
:param app_id: identifier for your application.
:return: The FCM token which is used to identify you with the push end
point application.
"""
self.register = FcmRegister(
self.fcm_config,
self.credentials,
self.credentials_updated_callback,
http_client_session=self._http_client_session,
)
self.credentials = await self.register.checkin_or_register()
# await self.register.fcm_refresh_install()
await self.register.close()
return self.credentials["fcm"]["registration"]["token"]
async def start(self) -> None:
"""Connect to FCM and start listening for push notifications."""
self.reset_lock = asyncio.Lock()
self.stopping_lock = asyncio.Lock()
self.do_listen = True
self.run_state = FcmPushClientRunState.STARTING_TASKS
try:
self.tasks = [
asyncio.create_task(self._listen()),
asyncio.create_task(self._do_monitor()),
]
except Exception as ex:
_logger.error("Unexpected error running FcmPushClient: %s", ex)
async def stop(self) -> None:
if (
self.stopping_lock
and self.stopping_lock.locked()
or self.run_state
in (
FcmPushClientRunState.STOPPING,
FcmPushClientRunState.STOPPED,
)
):
return
async with self.stopping_lock: # type: ignore[union-attr]
try:
self.run_state = FcmPushClientRunState.STOPPING
self.do_listen = False
for task in self.tasks:
if not task.done():
task.cancel()
finally:
self.run_state = FcmPushClientRunState.STOPPED
self.fcm_thread = None
self.listen_event_loop = None
def is_started(self) -> bool:
return self.run_state == FcmPushClientRunState.STARTED
async def send_message(self, raw_data: bytes, persistent_id: str) -> None:
"""Not implemented, does nothing atm."""
dms = DataMessageStanza()
dms.persistent_id = persistent_id
# Not supported yet
firebase-messaging-0.4.4/firebase_messaging/fcmregister.py 0000664 0000000 0000000 00000043201 14675551151 0023765 0 ustar 00root root 0000000 0000000 from __future__ import annotations
import asyncio
import json
import logging
import os
import secrets
import time
import uuid
from base64 import b64encode, urlsafe_b64encode
from dataclasses import dataclass
from typing import Any, Callable
from aiohttp import ClientSession, ClientTimeout
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from google.protobuf.json_format import MessageToDict, MessageToJson
from .const import (
AUTH_VERSION,
FCM_INSTALLATION,
FCM_REGISTRATION,
FCM_SEND_URL,
GCM_CHECKIN_URL,
GCM_REGISTER_URL,
GCM_SERVER_KEY_B64,
SDK_VERSION,
)
from .proto.android_checkin_pb2 import (
DEVICE_CHROME_BROWSER,
AndroidCheckinProto,
ChromeBuildProto,
)
from .proto.checkin_pb2 import (
AndroidCheckinRequest,
AndroidCheckinResponse,
)
_logger = logging.getLogger(__name__)
@dataclass
class FcmRegisterConfig:
project_id: str
app_id: str
api_key: str
messaging_sender_id: str
bundle_id: str = "receiver.push.com"
chrome_id: str = "org.chromium.linux"
chrome_version: str = "94.0.4606.51"
vapid_key: str | None = GCM_SERVER_KEY_B64
persistend_ids: list[str] | None = None
heartbeat_interval_ms: int = 5 * 60 * 1000 # 5 mins
def __postinit__(self) -> None:
if self.persistend_ids is None:
self.persistend_ids = []
class FcmRegister:
CLIENT_TIMEOUT = ClientTimeout(total=3)
def __init__(
self,
config: FcmRegisterConfig,
credentials: dict | None = None,
credentials_updated_callback: Callable[[dict[str, Any]], None] | None = None,
*,
http_client_session: ClientSession | None = None,
log_debug_verbose: bool = False,
):
self.config = config
self.credentials = credentials
self.credentials_updated_callback = credentials_updated_callback
self._log_debug_verbose = log_debug_verbose
self._http_client_session = http_client_session
self._local_session: ClientSession | None = None
def _get_checkin_payload(
self, android_id: int | None = None, security_token: int | None = None
) -> AndroidCheckinRequest:
chrome = ChromeBuildProto()
chrome.platform = ChromeBuildProto.Platform.PLATFORM_LINUX # 3
chrome.chrome_version = self.config.chrome_version
chrome.channel = ChromeBuildProto.Channel.CHANNEL_STABLE # 1
checkin = AndroidCheckinProto()
checkin.type = DEVICE_CHROME_BROWSER # 3
checkin.chrome_build.CopyFrom(chrome)
payload = AndroidCheckinRequest()
payload.user_serial_number = 0
payload.checkin.CopyFrom(checkin)
payload.version = 3
if android_id and security_token:
payload.id = int(android_id)
payload.security_token = int(security_token)
return payload
async def gcm_check_in_and_register(
self,
) -> dict[str, Any] | None:
options = await self.gcm_check_in()
if not options:
raise RuntimeError("Unable to register and check in to gcm")
gcm_credentials = await self.gcm_register(options)
return gcm_credentials
async def gcm_check_in(
self,
android_id: int | None = None,
security_token: int | None = None,
) -> dict[str, Any] | None:
"""
perform check-in request
android_id, security_token can be provided if we already did the initial
check-in
returns dict with android_id, security_token and more
"""
payload = self._get_checkin_payload(android_id, security_token)
if self._log_debug_verbose:
_logger.debug("GCM check in payload:\n%s", payload)
retries = 3
acir = None
content = None
for try_num in range(retries):
try:
async with self._session.post(
url=GCM_CHECKIN_URL,
headers={"Content-Type": "application/x-protobuf"},
data=payload.SerializeToString(),
timeout=self.CLIENT_TIMEOUT,
) as resp:
if resp.status == 200:
acir = AndroidCheckinResponse()
content = await resp.read()
break
else:
text = await resp.text()
if acir and content:
break
else:
_logger.warning(
"GCM checkin failed on attempt %s out "
+ "of %s with status: %s, %s",
try_num + 1,
retries,
resp.status,
text,
)
# retry without android id and security_token
payload = self._get_checkin_payload()
await asyncio.sleep(1)
except Exception as e:
_logger.warning(
"Error during gcm checkin post attempt %s out of %s",
try_num + 1,
retries,
exc_info=e,
)
await asyncio.sleep(1)
if not acir or not content:
_logger.error("Unable to checkin to gcm after %s retries", retries)
return None
acir.ParseFromString(content)
if self._log_debug_verbose:
msg = MessageToJson(acir, indent=4)
_logger.debug("GCM check in response (raw):\n%s", msg)
return MessageToDict(acir)
async def gcm_register(
self,
options: dict[str, Any],
retries: int = 2,
) -> dict[str, str] | None:
"""
obtains a gcm token
app_id: app id as an integer
retries: number of failed requests before giving up
returns {"token": "...", "gcm_app_id": 123123, "androidId":123123,
"securityToken": 123123}
"""
# contains android_id, security_token and more
gcm_app_id = f"wp:{self.config.bundle_id}#{uuid.uuid4()}"
android_id = options["androidId"]
security_token = options["securityToken"]
headers = {
"Authorization": f"AidLogin {android_id}:{security_token}",
"Content-Type": "application/x-www-form-urlencoded",
}
body = {
"app": "org.chromium.linux",
"X-subtype": gcm_app_id,
"device": android_id,
"sender": GCM_SERVER_KEY_B64,
}
if self._log_debug_verbose:
_logger.debug("GCM Registration request: %s", body)
last_error: str | Exception | None = None
for try_num in range(retries):
try:
async with self._session.post(
url=GCM_REGISTER_URL,
headers=headers,
data=body,
timeout=self.CLIENT_TIMEOUT,
) as resp:
response_text = await resp.text()
if "Error" in response_text:
_logger.warning(
"GCM register request attempt %s out of %s has failed with %s",
try_num + 1,
retries,
response_text,
)
last_error = response_text
await asyncio.sleep(1)
continue
token = response_text.split("=")[1]
return {
"token": token,
"app_id": gcm_app_id,
"android_id": android_id,
"security_token": security_token,
}
except Exception as e:
last_error = e
_logger.warning(
"Error during gcm auth request attempt %s out of %s",
try_num + 1,
retries,
exc_info=e,
)
await asyncio.sleep(1)
errorstr = f"Unable to complete gcm auth request after {retries} tries"
if isinstance(last_error, Exception):
_logger.error(errorstr, exc_info=last_error)
else:
errorstr += f", last error was {last_error}"
_logger.error(errorstr)
return None
async def fcm_install_and_register(
self, gcm_data: dict[str, Any], keys: dict[str, Any]
) -> dict[str, Any] | None:
if installation := await self.fcm_install():
registration = await self.fcm_register(gcm_data, installation, keys)
return {
"registration": registration,
"installation": installation,
}
return None
async def fcm_install(self) -> dict | None:
fid = bytearray(secrets.token_bytes(17))
# Replace the first 4 bits with the FID header 0b0111.
fid[0] = 0b01110000 + (fid[0] % 0b00010000)
fid64 = b64encode(fid).decode()
hb_header = b64encode(
json.dumps({"heartbeats": [], "version": 2}).encode()
).decode()
headers = {
"x-firebase-client": hb_header,
"x-goog-api-key": self.config.api_key,
}
payload = {
"appId": self.config.app_id,
"authVersion": AUTH_VERSION,
"fid": fid64,
"sdkVersion": SDK_VERSION,
}
url = FCM_INSTALLATION + f"projects/{self.config.project_id}/installations"
async with self._session.post(
url=url,
headers=headers,
data=json.dumps(payload),
timeout=self.CLIENT_TIMEOUT,
) as resp:
if resp.status == 200:
fcm_install = await resp.json()
return {
"token": fcm_install["authToken"]["token"],
"expires_in": int(fcm_install["authToken"]["expiresIn"][:-1:]),
"refresh_token": fcm_install["refreshToken"],
"fid": fcm_install["fid"],
"created_at": time.monotonic(),
}
else:
text = await resp.text()
_logger.error(
"Error during fcm_install: %s ",
text,
)
return None
async def fcm_refresh_install_token(self) -> dict | None:
hb_header = b64encode(
json.dumps({"heartbeats": [], "version": 2}).encode()
).decode()
if not self.credentials:
raise RuntimeError("Credentials must be set to refresh install token")
fcm_refresh_token = self.credentials["fcm"]["installation"]["refresh_token"]
headers = {
"Authorization": f"{AUTH_VERSION} {fcm_refresh_token}",
"x-firebase-client": hb_header,
"x-goog-api-key": self.config.api_key,
}
payload = {
"installation": {
"sdkVersion": SDK_VERSION,
"appId": self.config.app_id,
}
}
url = (
FCM_INSTALLATION + f"projects/{self.config.project_id}/"
"installations/{fid}/authTokens:generate"
)
async with self._session.post(
url=url,
headers=headers,
data=json.dumps(payload),
timeout=self.CLIENT_TIMEOUT,
) as resp:
if resp.status == 200:
fcm_refresh = await resp.json()
return {
"token": fcm_refresh["token"],
"expires_in": int(fcm_refresh["expiresIn"][:-1:]),
"created_at": time.monotonic(),
}
else:
text = await resp.text()
_logger.error(
"Error during fcm_refresh_install_token: %s ",
text,
)
return None
def generate_keys(self) -> dict:
private_key = ec.generate_private_key(ec.SECP256R1())
public_key = private_key.public_key()
serialized_private = private_key.private_bytes(
encoding=serialization.Encoding.DER, # asn1
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
serialized_public = public_key.public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
return {
"public": urlsafe_b64encode(serialized_public[26:]).decode(
"ascii"
), # urlsafe_base64(serialized_public[26:]),
"private": urlsafe_b64encode(serialized_private).decode("ascii"),
"secret": urlsafe_b64encode(os.urandom(16)).decode("ascii"),
}
async def fcm_register(
self,
gcm_data: dict,
installation: dict,
keys: dict,
retries: int = 2,
) -> dict[str, Any] | None:
headers = {
"x-goog-api-key": self.config.api_key,
"x-goog-firebase-installations-auth": installation["token"],
}
# If vapid_key is the default do not send it here or it will error
vapid_key = (
self.config.vapid_key
if self.config.vapid_key != GCM_SERVER_KEY_B64
else None
)
payload = {
"web": {
"applicationPubKey": vapid_key,
"auth": keys["secret"],
"endpoint": FCM_SEND_URL + gcm_data["token"],
"p256dh": keys["public"],
}
}
url = FCM_REGISTRATION + f"projects/{self.config.project_id}/registrations"
if self._log_debug_verbose:
_logger.debug("FCM registration data: %s", payload)
for try_num in range(retries):
try:
async with self._session.post(
url=url,
headers=headers,
data=json.dumps(payload),
timeout=self.CLIENT_TIMEOUT,
) as resp:
if resp.status == 200:
fcm = await resp.json()
return fcm
else:
text = await resp.text()
_logger.error( # pylint: disable=duplicate-code
"Error during fmc register request "
"attempt %s out of %s: %s",
try_num + 1,
retries,
text,
)
except Exception as e:
_logger.error( # pylint: disable=duplicate-code
"Error during fmc register request attempt %s out of %s",
try_num + 1,
retries,
exc_info=e,
)
await asyncio.sleep(1)
return None
async def checkin_or_register(self) -> dict[str, Any]:
"""Check in if you have credentials otherwise register as a new client.
:param sender_id: sender id identifying push service you are connecting to.
:param app_id: identifier for your application.
:return: The FCM token which is used to identify you with the push end
point application.
"""
if self.credentials:
gcm_response = await self.gcm_check_in(
self.credentials["gcm"]["android_id"],
self.credentials["gcm"]["security_token"],
)
if gcm_response:
return self.credentials
self.credentials = await self.register()
if self.credentials_updated_callback:
self.credentials_updated_callback(self.credentials)
return self.credentials
async def register(self) -> dict:
"""Register gcm and fcm tokens for sender_id.
Typically you would
call checkin instead of register which does not do a full registration
if credentials are present
:param sender_id: sender id identifying push service you are connecting to.
:param app_id: identifier for your application.
:return: The dict containing all credentials.
"""
keys = self.generate_keys()
gcm_data = await self.gcm_check_in_and_register()
if gcm_data is None:
raise RuntimeError(
"Unable to establish subscription with Google Cloud Messaging."
)
self._log_verbose("GCM subscription: %s", gcm_data)
fcm_data = await self.fcm_install_and_register(gcm_data, keys)
if not fcm_data:
raise RuntimeError("Unable to register with fcm")
self._log_verbose("FCM registration: %s", fcm_data)
res: dict[str, Any] = {
"keys": keys,
"gcm": gcm_data,
"fcm": fcm_data,
"config": {
"bundle_id": self.config.bundle_id,
"project_id": self.config.project_id,
"vapid_key": self.config.vapid_key,
},
}
self._log_verbose("Credential: %s", res)
_logger.info("Registered with FCM")
return res
def _log_verbose(self, msg: str, *args: object) -> None:
if self._log_debug_verbose:
_logger.debug(msg, *args)
@property
def _session(self) -> ClientSession:
if self._http_client_session:
return self._http_client_session
if self._local_session is None:
self._local_session = ClientSession()
return self._local_session
async def close(self) -> None:
"""Close aiohttp session."""
session = self._local_session
self._local_session = None
if session:
await session.close()
firebase-messaging-0.4.4/firebase_messaging/proto/ 0000775 0000000 0000000 00000000000 14675551151 0022244 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/firebase_messaging/proto/android_checkin.proto 0000664 0000000 0000000 00000006045 14675551151 0026442 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Logging information for Android "checkin" events (automatic, periodic
// requests made by Android devices to the server).
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package checkin_proto;
// Build characteristics unique to the Chrome browser, and Chrome OS
message ChromeBuildProto {
enum Platform {
PLATFORM_WIN = 1;
PLATFORM_MAC = 2;
PLATFORM_LINUX = 3;
PLATFORM_CROS = 4;
PLATFORM_IOS = 5;
// Just a placeholder. Likely don't need it due to the presence of the
// Android GCM on phone/tablet devices.
PLATFORM_ANDROID = 6;
}
enum Channel {
CHANNEL_STABLE = 1;
CHANNEL_BETA = 2;
CHANNEL_DEV = 3;
CHANNEL_CANARY = 4;
CHANNEL_UNKNOWN = 5; // for tip of tree or custom builds
}
// The platform of the device.
optional Platform platform = 1;
// The Chrome instance's version.
optional string chrome_version = 2;
// The Channel (build type) of Chrome.
optional Channel channel = 3;
}
// Information sent by the device in a "checkin" request.
message AndroidCheckinProto {
// Miliseconds since the Unix epoch of the device's last successful checkin.
optional int64 last_checkin_msec = 2;
// The current MCC+MNC of the mobile device's current cell.
optional string cell_operator = 6;
// The MCC+MNC of the SIM card (different from operator if the
// device is roaming, for instance).
optional string sim_operator = 7;
// The device's current roaming state (reported starting in eclair builds).
// Currently one of "{,not}mobile-{,not}roaming", if it is present at all.
optional string roaming = 8;
// For devices supporting multiple user profiles (which may be
// supported starting in jellybean), the ordinal number of the
// profile that is checking in. This is 0 for the primary profile
// (which can't be changed without wiping the device), and 1,2,3,...
// for additional profiles (which can be added and deleted freely).
optional int32 user_number = 9;
// Class of device. Indicates the type of build proto
// (IosBuildProto/ChromeBuildProto/AndroidBuildProto)
// That is included in this proto
optional DeviceType type = 12 [default = DEVICE_ANDROID_OS];
// For devices running MCS on Chrome, build-specific characteristics
// of the browser. There are no hardware aspects (except for ChromeOS).
// This will only be populated for Chrome builds/ChromeOS devices
optional checkin_proto.ChromeBuildProto chrome_build = 13;
// Note: Some of the Android specific optional fields were skipped to limit
// the protobuf definition.
// Next 14
}
// enum values correspond to the type of device.
// Used in the AndroidCheckinProto and Device proto.
enum DeviceType {
// Android Device
DEVICE_ANDROID_OS = 1;
// Apple IOS device
DEVICE_IOS_OS = 2;
// Chrome browser - Not Chrome OS. No hardware records.
DEVICE_CHROME_BROWSER = 3;
// Chrome OS
DEVICE_CHROME_OS = 4;
}
firebase-messaging-0.4.4/firebase_messaging/proto/android_checkin_pb2.py 0000664 0000000 0000000 00000005441 14675551151 0026471 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: android_checkin.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x15\x61ndroid_checkin.proto\x12\rcheckin_proto"\x8a\x03\n\x10\x43hromeBuildProto\x12:\n\x08platform\x18\x01 \x01(\x0e\x32(.checkin_proto.ChromeBuildProto.Platform\x12\x16\n\x0e\x63hrome_version\x18\x02 \x01(\t\x12\x38\n\x07\x63hannel\x18\x03 \x01(\x0e\x32\'.checkin_proto.ChromeBuildProto.Channel"}\n\x08Platform\x12\x10\n\x0cPLATFORM_WIN\x10\x01\x12\x10\n\x0cPLATFORM_MAC\x10\x02\x12\x12\n\x0ePLATFORM_LINUX\x10\x03\x12\x11\n\rPLATFORM_CROS\x10\x04\x12\x10\n\x0cPLATFORM_IOS\x10\x05\x12\x14\n\x10PLATFORM_ANDROID\x10\x06"i\n\x07\x43hannel\x12\x12\n\x0e\x43HANNEL_STABLE\x10\x01\x12\x10\n\x0c\x43HANNEL_BETA\x10\x02\x12\x0f\n\x0b\x43HANNEL_DEV\x10\x03\x12\x12\n\x0e\x43HANNEL_CANARY\x10\x04\x12\x13\n\x0f\x43HANNEL_UNKNOWN\x10\x05"\xf6\x01\n\x13\x41ndroidCheckinProto\x12\x19\n\x11last_checkin_msec\x18\x02 \x01(\x03\x12\x15\n\rcell_operator\x18\x06 \x01(\t\x12\x14\n\x0csim_operator\x18\x07 \x01(\t\x12\x0f\n\x07roaming\x18\x08 \x01(\t\x12\x13\n\x0buser_number\x18\t \x01(\x05\x12:\n\x04type\x18\x0c \x01(\x0e\x32\x19.checkin_proto.DeviceType:\x11\x44\x45VICE_ANDROID_OS\x12\x35\n\x0c\x63hrome_build\x18\r \x01(\x0b\x32\x1f.checkin_proto.ChromeBuildProto*g\n\nDeviceType\x12\x15\n\x11\x44\x45VICE_ANDROID_OS\x10\x01\x12\x11\n\rDEVICE_IOS_OS\x10\x02\x12\x19\n\x15\x44\x45VICE_CHROME_BROWSER\x10\x03\x12\x14\n\x10\x44\x45VICE_CHROME_OS\x10\x04\x42\x02H\x03'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "android_checkin_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals["DESCRIPTOR"]._serialized_options = b"H\003"
_globals["_DEVICETYPE"]._serialized_start = 686
_globals["_DEVICETYPE"]._serialized_end = 789
_globals["_CHROMEBUILDPROTO"]._serialized_start = 41
_globals["_CHROMEBUILDPROTO"]._serialized_end = 435
_globals["_CHROMEBUILDPROTO_PLATFORM"]._serialized_start = 203
_globals["_CHROMEBUILDPROTO_PLATFORM"]._serialized_end = 328
_globals["_CHROMEBUILDPROTO_CHANNEL"]._serialized_start = 330
_globals["_CHROMEBUILDPROTO_CHANNEL"]._serialized_end = 435
_globals["_ANDROIDCHECKINPROTO"]._serialized_start = 438
_globals["_ANDROIDCHECKINPROTO"]._serialized_end = 684
# @@protoc_insertion_point(module_scope)
firebase-messaging-0.4.4/firebase_messaging/proto/android_checkin_pb2.pyi 0000664 0000000 0000000 00000022243 14675551151 0026641 0 ustar 00root root 0000000 0000000 """
@generated by mypy-protobuf. Do not edit manually!
isort:skip_file
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
Logging information for Android "checkin" events (automatic, periodic
requests made by Android devices to the server).
"""
import builtins
import google.protobuf.descriptor
import google.protobuf.internal.enum_type_wrapper
import google.protobuf.message
import sys
import typing
if sys.version_info >= (3, 10):
import typing as typing_extensions
else:
import typing_extensions
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
class _DeviceType:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _DeviceTypeEnumTypeWrapper(
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DeviceType.ValueType],
builtins.type,
):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
DEVICE_ANDROID_OS: _DeviceType.ValueType # 1
"""Android Device"""
DEVICE_IOS_OS: _DeviceType.ValueType # 2
"""Apple IOS device"""
DEVICE_CHROME_BROWSER: _DeviceType.ValueType # 3
"""Chrome browser - Not Chrome OS. No hardware records."""
DEVICE_CHROME_OS: _DeviceType.ValueType # 4
"""Chrome OS"""
class DeviceType(_DeviceType, metaclass=_DeviceTypeEnumTypeWrapper):
"""enum values correspond to the type of device.
Used in the AndroidCheckinProto and Device proto.
"""
DEVICE_ANDROID_OS: DeviceType.ValueType # 1
"""Android Device"""
DEVICE_IOS_OS: DeviceType.ValueType # 2
"""Apple IOS device"""
DEVICE_CHROME_BROWSER: DeviceType.ValueType # 3
"""Chrome browser - Not Chrome OS. No hardware records."""
DEVICE_CHROME_OS: DeviceType.ValueType # 4
"""Chrome OS"""
global___DeviceType = DeviceType
@typing_extensions.final
class ChromeBuildProto(google.protobuf.message.Message):
"""Build characteristics unique to the Chrome browser, and Chrome OS"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _Platform:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _PlatformEnumTypeWrapper(
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
ChromeBuildProto._Platform.ValueType
],
builtins.type,
):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
PLATFORM_WIN: ChromeBuildProto._Platform.ValueType # 1
PLATFORM_MAC: ChromeBuildProto._Platform.ValueType # 2
PLATFORM_LINUX: ChromeBuildProto._Platform.ValueType # 3
PLATFORM_CROS: ChromeBuildProto._Platform.ValueType # 4
PLATFORM_IOS: ChromeBuildProto._Platform.ValueType # 5
PLATFORM_ANDROID: ChromeBuildProto._Platform.ValueType # 6
"""Just a placeholder. Likely don't need it due to the presence of the
Android GCM on phone/tablet devices.
"""
class Platform(_Platform, metaclass=_PlatformEnumTypeWrapper): ...
PLATFORM_WIN: ChromeBuildProto.Platform.ValueType # 1
PLATFORM_MAC: ChromeBuildProto.Platform.ValueType # 2
PLATFORM_LINUX: ChromeBuildProto.Platform.ValueType # 3
PLATFORM_CROS: ChromeBuildProto.Platform.ValueType # 4
PLATFORM_IOS: ChromeBuildProto.Platform.ValueType # 5
PLATFORM_ANDROID: ChromeBuildProto.Platform.ValueType # 6
"""Just a placeholder. Likely don't need it due to the presence of the
Android GCM on phone/tablet devices.
"""
class _Channel:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _ChannelEnumTypeWrapper(
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
ChromeBuildProto._Channel.ValueType
],
builtins.type,
):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
CHANNEL_STABLE: ChromeBuildProto._Channel.ValueType # 1
CHANNEL_BETA: ChromeBuildProto._Channel.ValueType # 2
CHANNEL_DEV: ChromeBuildProto._Channel.ValueType # 3
CHANNEL_CANARY: ChromeBuildProto._Channel.ValueType # 4
CHANNEL_UNKNOWN: ChromeBuildProto._Channel.ValueType # 5
"""for tip of tree or custom builds"""
class Channel(_Channel, metaclass=_ChannelEnumTypeWrapper): ...
CHANNEL_STABLE: ChromeBuildProto.Channel.ValueType # 1
CHANNEL_BETA: ChromeBuildProto.Channel.ValueType # 2
CHANNEL_DEV: ChromeBuildProto.Channel.ValueType # 3
CHANNEL_CANARY: ChromeBuildProto.Channel.ValueType # 4
CHANNEL_UNKNOWN: ChromeBuildProto.Channel.ValueType # 5
"""for tip of tree or custom builds"""
PLATFORM_FIELD_NUMBER: builtins.int
CHROME_VERSION_FIELD_NUMBER: builtins.int
CHANNEL_FIELD_NUMBER: builtins.int
platform: global___ChromeBuildProto.Platform.ValueType
"""The platform of the device."""
chrome_version: builtins.str
"""The Chrome instance's version."""
channel: global___ChromeBuildProto.Channel.ValueType
"""The Channel (build type) of Chrome."""
def __init__(
self,
*,
platform: global___ChromeBuildProto.Platform.ValueType | None = ...,
chrome_version: builtins.str | None = ...,
channel: global___ChromeBuildProto.Channel.ValueType | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"channel",
b"channel",
"chrome_version",
b"chrome_version",
"platform",
b"platform",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"channel",
b"channel",
"chrome_version",
b"chrome_version",
"platform",
b"platform",
],
) -> None: ...
global___ChromeBuildProto = ChromeBuildProto
@typing_extensions.final
class AndroidCheckinProto(google.protobuf.message.Message):
"""Information sent by the device in a "checkin" request."""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
LAST_CHECKIN_MSEC_FIELD_NUMBER: builtins.int
CELL_OPERATOR_FIELD_NUMBER: builtins.int
SIM_OPERATOR_FIELD_NUMBER: builtins.int
ROAMING_FIELD_NUMBER: builtins.int
USER_NUMBER_FIELD_NUMBER: builtins.int
TYPE_FIELD_NUMBER: builtins.int
CHROME_BUILD_FIELD_NUMBER: builtins.int
last_checkin_msec: builtins.int
"""Miliseconds since the Unix epoch of the device's last successful checkin."""
cell_operator: builtins.str
"""The current MCC+MNC of the mobile device's current cell."""
sim_operator: builtins.str
"""The MCC+MNC of the SIM card (different from operator if the
device is roaming, for instance).
"""
roaming: builtins.str
"""The device's current roaming state (reported starting in eclair builds).
Currently one of "{,not}mobile-{,not}roaming", if it is present at all.
"""
user_number: builtins.int
"""For devices supporting multiple user profiles (which may be
supported starting in jellybean), the ordinal number of the
profile that is checking in. This is 0 for the primary profile
(which can't be changed without wiping the device), and 1,2,3,...
for additional profiles (which can be added and deleted freely).
"""
type: global___DeviceType.ValueType
"""Class of device. Indicates the type of build proto
(IosBuildProto/ChromeBuildProto/AndroidBuildProto)
That is included in this proto
"""
@property
def chrome_build(self) -> global___ChromeBuildProto:
"""For devices running MCS on Chrome, build-specific characteristics
of the browser. There are no hardware aspects (except for ChromeOS).
This will only be populated for Chrome builds/ChromeOS devices
"""
def __init__(
self,
*,
last_checkin_msec: builtins.int | None = ...,
cell_operator: builtins.str | None = ...,
sim_operator: builtins.str | None = ...,
roaming: builtins.str | None = ...,
user_number: builtins.int | None = ...,
type: global___DeviceType.ValueType | None = ...,
chrome_build: global___ChromeBuildProto | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"cell_operator",
b"cell_operator",
"chrome_build",
b"chrome_build",
"last_checkin_msec",
b"last_checkin_msec",
"roaming",
b"roaming",
"sim_operator",
b"sim_operator",
"type",
b"type",
"user_number",
b"user_number",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"cell_operator",
b"cell_operator",
"chrome_build",
b"chrome_build",
"last_checkin_msec",
b"last_checkin_msec",
"roaming",
b"roaming",
"sim_operator",
b"sim_operator",
"type",
b"type",
"user_number",
b"user_number",
],
) -> None: ...
global___AndroidCheckinProto = AndroidCheckinProto
firebase-messaging-0.4.4/firebase_messaging/proto/checkin.proto 0000664 0000000 0000000 00000014171 14675551151 0024741 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Request and reply to the "checkin server" devices poll every few hours.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package checkin_proto;
import "android_checkin.proto";
// A concrete name/value pair sent to the device's Gservices database.
message GservicesSetting {
required bytes name = 1;
required bytes value = 2;
}
// Devices send this every few hours to tell us how they're doing.
message AndroidCheckinRequest {
// IMEI (used by GSM phones) is sent and stored as 15 decimal
// digits; the 15th is a check digit.
optional string imei = 1; // IMEI, reported but not logged.
// MEID (used by CDMA phones) is sent and stored as 14 hexadecimal
// digits (no check digit).
optional string meid = 10; // MEID, reported but not logged.
// MAC address (used by non-phone devices). 12 hexadecimal digits;
// no separators (eg "0016E6513AC2", not "00:16:E6:51:3A:C2").
repeated string mac_addr = 9; // MAC address, reported but not logged.
// An array parallel to mac_addr, describing the type of interface.
// Currently accepted values: "wifi", "ethernet", "bluetooth". If
// not present, "wifi" is assumed.
repeated string mac_addr_type = 19;
// Serial number (a manufacturer-defined unique hardware
// identifier). Alphanumeric, case-insensitive.
optional string serial_number = 16;
// Older CDMA networks use an ESN (8 hex digits) instead of an MEID.
optional string esn = 17; // ESN, reported but not logged
optional int64 id = 2; // Android device ID, not logged
optional int64 logging_id = 7; // Pseudonymous logging ID for Sawmill
optional string digest = 3; // Digest of device provisioning, not logged.
optional string locale = 6; // Current locale in standard (xx_XX) format
required AndroidCheckinProto checkin = 4;
// DEPRECATED, see AndroidCheckinProto.requested_group
optional string desired_build = 5;
// Blob of data from the Market app to be passed to Market API server
optional string market_checkin = 8;
// SID cookies of any google accounts stored on the phone. Not logged.
repeated string account_cookie = 11;
// Time zone. Not currently logged.
optional string time_zone = 12;
// Security token used to validate the checkin request.
// Required for android IDs issued to Froyo+ devices, not for legacy IDs.
optional fixed64 security_token = 13;
// Version of checkin protocol.
//
// There are currently two versions:
//
// - version field missing: android IDs are assigned based on
// hardware identifiers. unsecured in the sense that you can
// "unregister" someone's phone by sending a registration request
// with their IMEI/MEID/MAC.
//
// - version=2: android IDs are assigned randomly. The device is
// sent a security token that must be included in all future
// checkins for that android id.
//
// - version=3: same as version 2, but the 'fragment' field is
// provided, and the device understands incremental updates to the
// gservices table (ie, only returning the keys whose values have
// changed.)
//
// (version=1 was skipped to avoid confusion with the "missing"
// version field that is effectively version 1.)
optional int32 version = 14;
// OTA certs accepted by device (base-64 SHA-1 of cert files). Not
// logged.
repeated string ota_cert = 15;
// Honeycomb and newer devices send configuration data with their checkin.
// optional DeviceConfigurationProto device_configuration = 18;
// A single CheckinTask on the device may lead to multiple checkin
// requests if there is too much log data to upload in a single
// request. For version 3 and up, this field will be filled in with
// the number of the request, starting with 0.
optional int32 fragment = 20;
// For devices supporting multiple users, the name of the current
// profile (they all check in independently, just as if they were
// multiple physical devices). This may not be set, even if the
// device is using multiuser. (checkin.user_number should be set to
// the ordinal of the user.)
optional string user_name = 21;
// For devices supporting multiple user profiles, the serial number
// for the user checking in. Not logged. May not be set, even if
// the device supportes multiuser. checkin.user_number is the
// ordinal of the user (0, 1, 2, ...), which may be reused if users
// are deleted and re-created. user_serial_number is never reused
// (unless the device is wiped).
optional int32 user_serial_number = 22;
// NEXT TAG: 23
}
// The response to the device.
message AndroidCheckinResponse {
required bool stats_ok = 1; // Whether statistics were recorded properly.
optional int64 time_msec = 3; // Time of day from server (Java epoch).
// repeated AndroidIntentProto intent = 2;
// Provisioning is sent if the request included an obsolete digest.
//
// For version <= 2, 'digest' contains the digest that should be
// sent back to the server on the next checkin, and 'setting'
// contains the entire gservices table (which replaces the entire
// current table on the device).
//
// for version >= 3, 'digest' will be absent. If 'settings_diff'
// is false, then 'setting' contains the entire table, as in version
// 2. If 'settings_diff' is true, then 'delete_setting' contains
// the keys to delete, and 'setting' contains only keys to be added
// or for which the value has changed. All other keys in the
// current table should be left untouched. If 'settings_diff' is
// absent, don't touch the existing gservices table.
//
optional string digest = 4;
optional bool settings_diff = 9;
repeated string delete_setting = 10;
repeated GservicesSetting setting = 5;
optional bool market_ok = 6; // If Market got the market_checkin data OK.
optional fixed64 android_id = 7; // From the request, or newly assigned
optional fixed64 security_token = 8; // The associated security token
optional string version_info = 11;
// NEXT TAG: 12
}
firebase-messaging-0.4.4/firebase_messaging/proto/checkin_pb2.py 0000664 0000000 0000000 00000005353 14675551151 0024773 0 ustar 00root root 0000000 0000000 # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: checkin.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\rcheckin.proto\x12\rcheckin_proto\x1a\x15\x61ndroid_checkin.proto"/\n\x10GservicesSetting\x12\x0c\n\x04name\x18\x01 \x02(\x0c\x12\r\n\x05value\x18\x02 \x02(\x0c"\xcb\x03\n\x15\x41ndroidCheckinRequest\x12\x0c\n\x04imei\x18\x01 \x01(\t\x12\x0c\n\x04meid\x18\n \x01(\t\x12\x10\n\x08mac_addr\x18\t \x03(\t\x12\x15\n\rmac_addr_type\x18\x13 \x03(\t\x12\x15\n\rserial_number\x18\x10 \x01(\t\x12\x0b\n\x03\x65sn\x18\x11 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x03\x12\x12\n\nlogging_id\x18\x07 \x01(\x03\x12\x0e\n\x06\x64igest\x18\x03 \x01(\t\x12\x0e\n\x06locale\x18\x06 \x01(\t\x12\x33\n\x07\x63heckin\x18\x04 \x02(\x0b\x32".checkin_proto.AndroidCheckinProto\x12\x15\n\rdesired_build\x18\x05 \x01(\t\x12\x16\n\x0emarket_checkin\x18\x08 \x01(\t\x12\x16\n\x0e\x61\x63\x63ount_cookie\x18\x0b \x03(\t\x12\x11\n\ttime_zone\x18\x0c \x01(\t\x12\x16\n\x0esecurity_token\x18\r \x01(\x06\x12\x0f\n\x07version\x18\x0e \x01(\x05\x12\x10\n\x08ota_cert\x18\x0f \x03(\t\x12\x10\n\x08\x66ragment\x18\x14 \x01(\x05\x12\x11\n\tuser_name\x18\x15 \x01(\t\x12\x1a\n\x12user_serial_number\x18\x16 \x01(\x05"\x83\x02\n\x16\x41ndroidCheckinResponse\x12\x10\n\x08stats_ok\x18\x01 \x02(\x08\x12\x11\n\ttime_msec\x18\x03 \x01(\x03\x12\x0e\n\x06\x64igest\x18\x04 \x01(\t\x12\x15\n\rsettings_diff\x18\t \x01(\x08\x12\x16\n\x0e\x64\x65lete_setting\x18\n \x03(\t\x12\x30\n\x07setting\x18\x05 \x03(\x0b\x32\x1f.checkin_proto.GservicesSetting\x12\x11\n\tmarket_ok\x18\x06 \x01(\x08\x12\x12\n\nandroid_id\x18\x07 \x01(\x06\x12\x16\n\x0esecurity_token\x18\x08 \x01(\x06\x12\x14\n\x0cversion_info\x18\x0b \x01(\tB\x02H\x03'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "checkin_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals["DESCRIPTOR"]._serialized_options = b"H\003"
_globals["_GSERVICESSETTING"]._serialized_start = 55
_globals["_GSERVICESSETTING"]._serialized_end = 102
_globals["_ANDROIDCHECKINREQUEST"]._serialized_start = 105
_globals["_ANDROIDCHECKINREQUEST"]._serialized_end = 564
_globals["_ANDROIDCHECKINRESPONSE"]._serialized_start = 567
_globals["_ANDROIDCHECKINRESPONSE"]._serialized_end = 826
# @@protoc_insertion_point(module_scope)
firebase-messaging-0.4.4/firebase_messaging/proto/checkin_pb2.pyi 0000664 0000000 0000000 00000034442 14675551151 0025145 0 ustar 00root root 0000000 0000000 """
@generated by mypy-protobuf. Do not edit manually!
isort:skip_file
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
Request and reply to the "checkin server" devices poll every few hours.
"""
from . import android_checkin_pb2
import builtins
import collections.abc
import google.protobuf.descriptor
import google.protobuf.internal.containers
import google.protobuf.message
import sys
if sys.version_info >= (3, 8):
import typing as typing_extensions
else:
import typing_extensions
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
@typing_extensions.final
class GservicesSetting(google.protobuf.message.Message):
"""A concrete name/value pair sent to the device's Gservices database."""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
NAME_FIELD_NUMBER: builtins.int
VALUE_FIELD_NUMBER: builtins.int
name: builtins.bytes
value: builtins.bytes
def __init__(
self,
*,
name: builtins.bytes | None = ...,
value: builtins.bytes | None = ...,
) -> None: ...
def HasField(
self, field_name: typing_extensions.Literal["name", b"name", "value", b"value"]
) -> builtins.bool: ...
def ClearField(
self, field_name: typing_extensions.Literal["name", b"name", "value", b"value"]
) -> None: ...
global___GservicesSetting = GservicesSetting
@typing_extensions.final
class AndroidCheckinRequest(google.protobuf.message.Message):
"""Devices send this every few hours to tell us how they're doing."""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
IMEI_FIELD_NUMBER: builtins.int
MEID_FIELD_NUMBER: builtins.int
MAC_ADDR_FIELD_NUMBER: builtins.int
MAC_ADDR_TYPE_FIELD_NUMBER: builtins.int
SERIAL_NUMBER_FIELD_NUMBER: builtins.int
ESN_FIELD_NUMBER: builtins.int
ID_FIELD_NUMBER: builtins.int
LOGGING_ID_FIELD_NUMBER: builtins.int
DIGEST_FIELD_NUMBER: builtins.int
LOCALE_FIELD_NUMBER: builtins.int
CHECKIN_FIELD_NUMBER: builtins.int
DESIRED_BUILD_FIELD_NUMBER: builtins.int
MARKET_CHECKIN_FIELD_NUMBER: builtins.int
ACCOUNT_COOKIE_FIELD_NUMBER: builtins.int
TIME_ZONE_FIELD_NUMBER: builtins.int
SECURITY_TOKEN_FIELD_NUMBER: builtins.int
VERSION_FIELD_NUMBER: builtins.int
OTA_CERT_FIELD_NUMBER: builtins.int
FRAGMENT_FIELD_NUMBER: builtins.int
USER_NAME_FIELD_NUMBER: builtins.int
USER_SERIAL_NUMBER_FIELD_NUMBER: builtins.int
imei: builtins.str
"""IMEI (used by GSM phones) is sent and stored as 15 decimal
digits; the 15th is a check digit.
IMEI, reported but not logged.
"""
meid: builtins.str
"""MEID (used by CDMA phones) is sent and stored as 14 hexadecimal
digits (no check digit).
MEID, reported but not logged.
"""
@property
def mac_addr(
self,
) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
"""MAC address (used by non-phone devices). 12 hexadecimal digits;
no separators (eg "0016E6513AC2", not "00:16:E6:51:3A:C2").
MAC address, reported but not logged.
"""
@property
def mac_addr_type(
self,
) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
"""An array parallel to mac_addr, describing the type of interface.
Currently accepted values: "wifi", "ethernet", "bluetooth". If
not present, "wifi" is assumed.
"""
serial_number: builtins.str
"""Serial number (a manufacturer-defined unique hardware
identifier). Alphanumeric, case-insensitive.
"""
esn: builtins.str
"""Older CDMA networks use an ESN (8 hex digits) instead of an MEID.
ESN, reported but not logged
"""
id: builtins.int
"""Android device ID, not logged"""
logging_id: builtins.int
"""Pseudonymous logging ID for Sawmill"""
digest: builtins.str
"""Digest of device provisioning, not logged."""
locale: builtins.str
"""Current locale in standard (xx_XX) format"""
@property
def checkin(self) -> android_checkin_pb2.AndroidCheckinProto: ...
desired_build: builtins.str
"""DEPRECATED, see AndroidCheckinProto.requested_group"""
market_checkin: builtins.str
"""Blob of data from the Market app to be passed to Market API server"""
@property
def account_cookie(
self,
) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
"""SID cookies of any google accounts stored on the phone. Not logged."""
time_zone: builtins.str
"""Time zone. Not currently logged."""
security_token: builtins.int
"""Security token used to validate the checkin request.
Required for android IDs issued to Froyo+ devices, not for legacy IDs.
"""
version: builtins.int
"""Version of checkin protocol.
There are currently two versions:
- version field missing: android IDs are assigned based on
hardware identifiers. unsecured in the sense that you can
"unregister" someone's phone by sending a registration request
with their IMEI/MEID/MAC.
- version=2: android IDs are assigned randomly. The device is
sent a security token that must be included in all future
checkins for that android id.
- version=3: same as version 2, but the 'fragment' field is
provided, and the device understands incremental updates to the
gservices table (ie, only returning the keys whose values have
changed.)
(version=1 was skipped to avoid confusion with the "missing"
version field that is effectively version 1.)
"""
@property
def ota_cert(
self,
) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
"""OTA certs accepted by device (base-64 SHA-1 of cert files). Not
logged.
"""
fragment: builtins.int
"""Honeycomb and newer devices send configuration data with their checkin.
optional DeviceConfigurationProto device_configuration = 18;
A single CheckinTask on the device may lead to multiple checkin
requests if there is too much log data to upload in a single
request. For version 3 and up, this field will be filled in with
the number of the request, starting with 0.
"""
user_name: builtins.str
"""For devices supporting multiple users, the name of the current
profile (they all check in independently, just as if they were
multiple physical devices). This may not be set, even if the
device is using multiuser. (checkin.user_number should be set to
the ordinal of the user.)
"""
user_serial_number: builtins.int
"""For devices supporting multiple user profiles, the serial number
for the user checking in. Not logged. May not be set, even if
the device supportes multiuser. checkin.user_number is the
ordinal of the user (0, 1, 2, ...), which may be reused if users
are deleted and re-created. user_serial_number is never reused
(unless the device is wiped).
"""
def __init__(
self,
*,
imei: builtins.str | None = ...,
meid: builtins.str | None = ...,
mac_addr: collections.abc.Iterable[builtins.str] | None = ...,
mac_addr_type: collections.abc.Iterable[builtins.str] | None = ...,
serial_number: builtins.str | None = ...,
esn: builtins.str | None = ...,
id: builtins.int | None = ...,
logging_id: builtins.int | None = ...,
digest: builtins.str | None = ...,
locale: builtins.str | None = ...,
checkin: android_checkin_pb2.AndroidCheckinProto | None = ...,
desired_build: builtins.str | None = ...,
market_checkin: builtins.str | None = ...,
account_cookie: collections.abc.Iterable[builtins.str] | None = ...,
time_zone: builtins.str | None = ...,
security_token: builtins.int | None = ...,
version: builtins.int | None = ...,
ota_cert: collections.abc.Iterable[builtins.str] | None = ...,
fragment: builtins.int | None = ...,
user_name: builtins.str | None = ...,
user_serial_number: builtins.int | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"checkin",
b"checkin",
"desired_build",
b"desired_build",
"digest",
b"digest",
"esn",
b"esn",
"fragment",
b"fragment",
"id",
b"id",
"imei",
b"imei",
"locale",
b"locale",
"logging_id",
b"logging_id",
"market_checkin",
b"market_checkin",
"meid",
b"meid",
"security_token",
b"security_token",
"serial_number",
b"serial_number",
"time_zone",
b"time_zone",
"user_name",
b"user_name",
"user_serial_number",
b"user_serial_number",
"version",
b"version",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"account_cookie",
b"account_cookie",
"checkin",
b"checkin",
"desired_build",
b"desired_build",
"digest",
b"digest",
"esn",
b"esn",
"fragment",
b"fragment",
"id",
b"id",
"imei",
b"imei",
"locale",
b"locale",
"logging_id",
b"logging_id",
"mac_addr",
b"mac_addr",
"mac_addr_type",
b"mac_addr_type",
"market_checkin",
b"market_checkin",
"meid",
b"meid",
"ota_cert",
b"ota_cert",
"security_token",
b"security_token",
"serial_number",
b"serial_number",
"time_zone",
b"time_zone",
"user_name",
b"user_name",
"user_serial_number",
b"user_serial_number",
"version",
b"version",
],
) -> None: ...
global___AndroidCheckinRequest = AndroidCheckinRequest
@typing_extensions.final
class AndroidCheckinResponse(google.protobuf.message.Message):
"""The response to the device."""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
STATS_OK_FIELD_NUMBER: builtins.int
TIME_MSEC_FIELD_NUMBER: builtins.int
DIGEST_FIELD_NUMBER: builtins.int
SETTINGS_DIFF_FIELD_NUMBER: builtins.int
DELETE_SETTING_FIELD_NUMBER: builtins.int
SETTING_FIELD_NUMBER: builtins.int
MARKET_OK_FIELD_NUMBER: builtins.int
ANDROID_ID_FIELD_NUMBER: builtins.int
SECURITY_TOKEN_FIELD_NUMBER: builtins.int
VERSION_INFO_FIELD_NUMBER: builtins.int
stats_ok: builtins.bool
"""Whether statistics were recorded properly."""
time_msec: builtins.int
"""Time of day from server (Java epoch)."""
digest: builtins.str
"""repeated AndroidIntentProto intent = 2;
Provisioning is sent if the request included an obsolete digest.
For version <= 2, 'digest' contains the digest that should be
sent back to the server on the next checkin, and 'setting'
contains the entire gservices table (which replaces the entire
current table on the device).
for version >= 3, 'digest' will be absent. If 'settings_diff'
is false, then 'setting' contains the entire table, as in version
2. If 'settings_diff' is true, then 'delete_setting' contains
the keys to delete, and 'setting' contains only keys to be added
or for which the value has changed. All other keys in the
current table should be left untouched. If 'settings_diff' is
absent, don't touch the existing gservices table.
"""
settings_diff: builtins.bool
@property
def delete_setting(
self,
) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[
builtins.str
]: ...
@property
def setting(
self,
) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[
global___GservicesSetting
]: ...
market_ok: builtins.bool
"""If Market got the market_checkin data OK."""
android_id: builtins.int
"""From the request, or newly assigned"""
security_token: builtins.int
"""The associated security token"""
version_info: builtins.str
"""NEXT TAG: 12"""
def __init__(
self,
*,
stats_ok: builtins.bool | None = ...,
time_msec: builtins.int | None = ...,
digest: builtins.str | None = ...,
settings_diff: builtins.bool | None = ...,
delete_setting: collections.abc.Iterable[builtins.str] | None = ...,
setting: collections.abc.Iterable[global___GservicesSetting] | None = ...,
market_ok: builtins.bool | None = ...,
android_id: builtins.int | None = ...,
security_token: builtins.int | None = ...,
version_info: builtins.str | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"android_id",
b"android_id",
"digest",
b"digest",
"market_ok",
b"market_ok",
"security_token",
b"security_token",
"settings_diff",
b"settings_diff",
"stats_ok",
b"stats_ok",
"time_msec",
b"time_msec",
"version_info",
b"version_info",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"android_id",
b"android_id",
"delete_setting",
b"delete_setting",
"digest",
b"digest",
"market_ok",
b"market_ok",
"security_token",
b"security_token",
"setting",
b"setting",
"settings_diff",
b"settings_diff",
"stats_ok",
b"stats_ok",
"time_msec",
b"time_msec",
"version_info",
b"version_info",
],
) -> None: ...
global___AndroidCheckinResponse = AndroidCheckinResponse
firebase-messaging-0.4.4/firebase_messaging/proto/mcs.proto 0000664 0000000 0000000 00000020111 14675551151 0024106 0 ustar 00root root 0000000 0000000 // Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// MCS protocol for communication between Chrome client and Mobile Connection
// Server .
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package mcs_proto;
/*
Common fields/comments:
stream_id: no longer sent by server, each side keeps a counter
last_stream_id_received: sent only if a packet was received since last time
a last_stream was sent
status: new bitmask including the 'idle' as bit 0.
*/
/**
TAG: 0
*/
message HeartbeatPing {
optional int32 stream_id = 1;
optional int32 last_stream_id_received = 2;
optional int64 status = 3;
}
/**
TAG: 1
*/
message HeartbeatAck {
optional int32 stream_id = 1;
optional int32 last_stream_id_received = 2;
optional int64 status = 3;
}
message ErrorInfo {
required int32 code = 1;
optional string message = 2;
optional string type = 3;
optional Extension extension = 4;
}
// MobileSettings class.
// "u:f", "u:b", "u:s" - multi user devices reporting foreground, background
// and stopped users.
// hbping: heatbeat ping interval
// rmq2v: include explicit stream IDs
message Setting {
required string name = 1;
required string value = 2;
}
message HeartbeatStat {
required string ip = 1;
required bool timeout = 2;
required int32 interval_ms = 3;
}
message HeartbeatConfig {
optional bool upload_stat = 1;
optional string ip = 2;
optional int32 interval_ms = 3;
}
// ClientEvents are used to inform the server of failed and successful
// connections.
message ClientEvent {
enum Type {
UNKNOWN = 0;
// Count of discarded events if the buffer filled up and was trimmed.
DISCARDED_EVENTS = 1;
// Failed connection event: the connection failed to be established or we
// had a login error.
FAILED_CONNECTION = 2;
// Successful connection event: information about the last successful
// connection, including the time at which it was established.
SUCCESSFUL_CONNECTION = 3;
}
// Common fields [1-99]
optional Type type = 1;
// Fields for DISCARDED_EVENTS messages [100-199]
optional uint32 number_discarded_events = 100;
// Fields for FAILED_CONNECTION and SUCCESSFUL_CONNECTION messages [200-299]
// Network type is a value in net::NetworkChangeNotifier::ConnectionType.
optional int32 network_type = 200;
// Reserved for network_port.
reserved 201;
optional uint64 time_connection_started_ms = 202;
optional uint64 time_connection_ended_ms = 203;
// Error code should be a net::Error value.
optional int32 error_code = 204;
// Fields for SUCCESSFUL_CONNECTION messages [300-399]
optional uint64 time_connection_established_ms = 300;
}
/**
TAG: 2
*/
message LoginRequest {
enum AuthService {
ANDROID_ID = 2;
}
required string id = 1; // Must be present ( proto required ), may be empty
// string.
// mcs.android.com.
required string domain = 2;
// Decimal android ID
required string user = 3;
required string resource = 4;
// Secret
required string auth_token = 5;
// Format is: android-HEX_DEVICE_ID
// The user is the decimal value.
optional string device_id = 6;
// RMQ1 - no longer used
optional int64 last_rmq_id = 7;
repeated Setting setting = 8;
//optional int32 compress = 9;
repeated string received_persistent_id = 10;
// Replaced by "rmq2v" setting
// optional bool include_stream_ids = 11;
optional bool adaptive_heartbeat = 12;
optional HeartbeatStat heartbeat_stat = 13;
// Must be true.
optional bool use_rmq2 = 14;
optional int64 account_id = 15;
// ANDROID_ID = 2
optional AuthService auth_service = 16;
optional int32 network_type = 17;
optional int64 status = 18;
// 19, 20, and 21 are not currently populated by Chrome.
reserved 19, 20, 21;
// Events recorded on the client after the last successful connection.
repeated ClientEvent client_event = 22;
}
/**
* TAG: 3
*/
message LoginResponse {
required string id = 1;
// Not used.
optional string jid = 2;
// Null if login was ok.
optional ErrorInfo error = 3;
repeated Setting setting = 4;
optional int32 stream_id = 5;
// Should be "1"
optional int32 last_stream_id_received = 6;
optional HeartbeatConfig heartbeat_config = 7;
// used by the client to synchronize with the server timestamp.
optional int64 server_timestamp = 8;
}
message StreamErrorStanza {
required string type = 1;
optional string text = 2;
}
/**
* TAG: 4
*/
message Close {
}
message Extension {
// 12: SelectiveAck
// 13: StreamAck
required int32 id = 1;
required bytes data = 2;
}
/**
* TAG: 7
* IqRequest must contain a single extension. IqResponse may contain 0 or 1
* extensions.
*/
message IqStanza {
enum IqType {
GET = 0;
SET = 1;
RESULT = 2;
IQ_ERROR = 3;
}
optional int64 rmq_id = 1;
required IqType type = 2;
required string id = 3;
optional string from = 4;
optional string to = 5;
optional ErrorInfo error = 6;
// Only field used in the 38+ protocol (besides common last_stream_id_received, status, rmq_id)
optional Extension extension = 7;
optional string persistent_id = 8;
optional int32 stream_id = 9;
optional int32 last_stream_id_received = 10;
optional int64 account_id = 11;
optional int64 status = 12;
}
message AppData {
required string key = 1;
required string value = 2;
}
/**
* TAG: 8
*/
message DataMessageStanza {
// Not used.
// optional int64 rmq_id = 1;
// This is the message ID, set by client, DMP.9 (message_id)
optional string id = 2;
// Project ID of the sender, DMP.1
required string from = 3;
// Part of DMRequest - also the key in DataMessageProto.
optional string to = 4;
// Package name. DMP.2
required string category = 5;
// The collapsed key, DMP.3
optional string token = 6;
// User data + GOOGLE. prefixed special entries, DMP.4
repeated AppData app_data = 7;
// Not used.
optional bool from_trusted_server = 8;
// Part of the ACK protocol, returned in DataMessageResponse on server side.
// It's part of the key of DMP.
optional string persistent_id = 9;
// In-stream ack. Increments on each message sent - a bit redundant
// Not used in DMP/DMR.
optional int32 stream_id = 10;
optional int32 last_stream_id_received = 11;
// Not used.
// optional string permission = 12;
// Sent by the device shortly after registration.
optional string reg_id = 13;
// Not used.
// optional string pkg_signature = 14;
// Not used.
// optional string client_id = 15;
// serial number of the target user, DMP.8
// It is the 'serial number' according to user manager.
optional int64 device_user_id = 16;
// Time to live, in seconds.
optional int32 ttl = 17;
// Timestamp ( according to client ) when message was sent by app, in seconds
optional int64 sent = 18;
// How long has the message been queued before the flush, in seconds.
// This is needed to account for the time difference between server and
// client: server should adjust 'sent' based on its 'receive' time.
optional int32 queued = 19;
optional int64 status = 20;
// Optional field containing the binary payload of the message.
optional bytes raw_data = 21;
// Not used.
// The maximum delay of the message, in seconds.
// optional int32 max_delay = 22;
// Not used.
// How long the message was delayed before it was sent, in seconds.
// optional int32 actual_delay = 23;
// If set the server requests immediate ack. Used for important messages and
// for testing.
optional bool immediate_ack = 24;
// Not used.
// Enables message receipts from MCS/GCM back to CCS clients
// optional bool delivery_receipt_requested = 25;
}
/**
Included in IQ with ID 13, sent from client or server after 10 unconfirmed
messages.
*/
message StreamAck {
// No last_streamid_received required. This is included within an IqStanza,
// which includes the last_stream_id_received.
}
/**
Included in IQ sent after LoginResponse from server with ID 12.
*/
message SelectiveAck {
repeated string id = 1;
}
firebase-messaging-0.4.4/firebase_messaging/proto/mcs_pb2.py 0000664 0000000 0000000 00000017065 14675551151 0024154 0 ustar 00root root 0000000 0000000 # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: mcs.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\tmcs.proto\x12\tmcs_proto"S\n\rHeartbeatPing\x12\x11\n\tstream_id\x18\x01 \x01(\x05\x12\x1f\n\x17last_stream_id_received\x18\x02 \x01(\x05\x12\x0e\n\x06status\x18\x03 \x01(\x03"R\n\x0cHeartbeatAck\x12\x11\n\tstream_id\x18\x01 \x01(\x05\x12\x1f\n\x17last_stream_id_received\x18\x02 \x01(\x05\x12\x0e\n\x06status\x18\x03 \x01(\x03"a\n\tErrorInfo\x12\x0c\n\x04\x63ode\x18\x01 \x02(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\'\n\textension\x18\x04 \x01(\x0b\x32\x14.mcs_proto.Extension"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\t"A\n\rHeartbeatStat\x12\n\n\x02ip\x18\x01 \x02(\t\x12\x0f\n\x07timeout\x18\x02 \x02(\x08\x12\x13\n\x0binterval_ms\x18\x03 \x02(\x05"G\n\x0fHeartbeatConfig\x12\x13\n\x0bupload_stat\x18\x01 \x01(\x08\x12\n\n\x02ip\x18\x02 \x01(\t\x12\x13\n\x0binterval_ms\x18\x03 \x01(\x05"\xdb\x02\n\x0b\x43lientEvent\x12)\n\x04type\x18\x01 \x01(\x0e\x32\x1b.mcs_proto.ClientEvent.Type\x12\x1f\n\x17number_discarded_events\x18\x64 \x01(\r\x12\x15\n\x0cnetwork_type\x18\xc8\x01 \x01(\x05\x12#\n\x1atime_connection_started_ms\x18\xca\x01 \x01(\x04\x12!\n\x18time_connection_ended_ms\x18\xcb\x01 \x01(\x04\x12\x13\n\nerror_code\x18\xcc\x01 \x01(\x05\x12\'\n\x1etime_connection_established_ms\x18\xac\x02 \x01(\x04"[\n\x04Type\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x14\n\x10\x44ISCARDED_EVENTS\x10\x01\x12\x15\n\x11\x46\x41ILED_CONNECTION\x10\x02\x12\x19\n\x15SUCCESSFUL_CONNECTION\x10\x03J\x06\x08\xc9\x01\x10\xca\x01"\xff\x03\n\x0cLoginRequest\x12\n\n\x02id\x18\x01 \x02(\t\x12\x0e\n\x06\x64omain\x18\x02 \x02(\t\x12\x0c\n\x04user\x18\x03 \x02(\t\x12\x10\n\x08resource\x18\x04 \x02(\t\x12\x12\n\nauth_token\x18\x05 \x02(\t\x12\x11\n\tdevice_id\x18\x06 \x01(\t\x12\x13\n\x0blast_rmq_id\x18\x07 \x01(\x03\x12#\n\x07setting\x18\x08 \x03(\x0b\x32\x12.mcs_proto.Setting\x12\x1e\n\x16received_persistent_id\x18\n \x03(\t\x12\x1a\n\x12\x61\x64\x61ptive_heartbeat\x18\x0c \x01(\x08\x12\x30\n\x0eheartbeat_stat\x18\r \x01(\x0b\x32\x18.mcs_proto.HeartbeatStat\x12\x10\n\x08use_rmq2\x18\x0e \x01(\x08\x12\x12\n\naccount_id\x18\x0f \x01(\x03\x12\x39\n\x0c\x61uth_service\x18\x10 \x01(\x0e\x32#.mcs_proto.LoginRequest.AuthService\x12\x14\n\x0cnetwork_type\x18\x11 \x01(\x05\x12\x0e\n\x06status\x18\x12 \x01(\x03\x12,\n\x0c\x63lient_event\x18\x16 \x03(\x0b\x32\x16.mcs_proto.ClientEvent"\x1d\n\x0b\x41uthService\x12\x0e\n\nANDROID_ID\x10\x02J\x04\x08\x13\x10\x14J\x04\x08\x14\x10\x15J\x04\x08\x15\x10\x16"\xf6\x01\n\rLoginResponse\x12\n\n\x02id\x18\x01 \x02(\t\x12\x0b\n\x03jid\x18\x02 \x01(\t\x12#\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x14.mcs_proto.ErrorInfo\x12#\n\x07setting\x18\x04 \x03(\x0b\x32\x12.mcs_proto.Setting\x12\x11\n\tstream_id\x18\x05 \x01(\x05\x12\x1f\n\x17last_stream_id_received\x18\x06 \x01(\x05\x12\x34\n\x10heartbeat_config\x18\x07 \x01(\x0b\x32\x1a.mcs_proto.HeartbeatConfig\x12\x18\n\x10server_timestamp\x18\x08 \x01(\x03"/\n\x11StreamErrorStanza\x12\x0c\n\x04type\x18\x01 \x02(\t\x12\x0c\n\x04text\x18\x02 \x01(\t"\x07\n\x05\x43lose"%\n\tExtension\x12\n\n\x02id\x18\x01 \x02(\x05\x12\x0c\n\x04\x64\x61ta\x18\x02 \x02(\x0c"\xdd\x02\n\x08IqStanza\x12\x0e\n\x06rmq_id\x18\x01 \x01(\x03\x12(\n\x04type\x18\x02 \x02(\x0e\x32\x1a.mcs_proto.IqStanza.IqType\x12\n\n\x02id\x18\x03 \x02(\t\x12\x0c\n\x04\x66rom\x18\x04 \x01(\t\x12\n\n\x02to\x18\x05 \x01(\t\x12#\n\x05\x65rror\x18\x06 \x01(\x0b\x32\x14.mcs_proto.ErrorInfo\x12\'\n\textension\x18\x07 \x01(\x0b\x32\x14.mcs_proto.Extension\x12\x15\n\rpersistent_id\x18\x08 \x01(\t\x12\x11\n\tstream_id\x18\t \x01(\x05\x12\x1f\n\x17last_stream_id_received\x18\n \x01(\x05\x12\x12\n\naccount_id\x18\x0b \x01(\x03\x12\x0e\n\x06status\x18\x0c \x01(\x03"4\n\x06IqType\x12\x07\n\x03GET\x10\x00\x12\x07\n\x03SET\x10\x01\x12\n\n\x06RESULT\x10\x02\x12\x0c\n\x08IQ_ERROR\x10\x03"%\n\x07\x41ppData\x12\x0b\n\x03key\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\t"\xf4\x02\n\x11\x44\x61taMessageStanza\x12\n\n\x02id\x18\x02 \x01(\t\x12\x0c\n\x04\x66rom\x18\x03 \x02(\t\x12\n\n\x02to\x18\x04 \x01(\t\x12\x10\n\x08\x63\x61tegory\x18\x05 \x02(\t\x12\r\n\x05token\x18\x06 \x01(\t\x12$\n\x08\x61pp_data\x18\x07 \x03(\x0b\x32\x12.mcs_proto.AppData\x12\x1b\n\x13\x66rom_trusted_server\x18\x08 \x01(\x08\x12\x15\n\rpersistent_id\x18\t \x01(\t\x12\x11\n\tstream_id\x18\n \x01(\x05\x12\x1f\n\x17last_stream_id_received\x18\x0b \x01(\x05\x12\x0e\n\x06reg_id\x18\r \x01(\t\x12\x16\n\x0e\x64\x65vice_user_id\x18\x10 \x01(\x03\x12\x0b\n\x03ttl\x18\x11 \x01(\x05\x12\x0c\n\x04sent\x18\x12 \x01(\x03\x12\x0e\n\x06queued\x18\x13 \x01(\x05\x12\x0e\n\x06status\x18\x14 \x01(\x03\x12\x10\n\x08raw_data\x18\x15 \x01(\x0c\x12\x15\n\rimmediate_ack\x18\x18 \x01(\x08"\x0b\n\tStreamAck"\x1a\n\x0cSelectiveAck\x12\n\n\x02id\x18\x01 \x03(\tB\x02H\x03'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "mcs_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals["DESCRIPTOR"]._serialized_options = b"H\003"
_globals["_HEARTBEATPING"]._serialized_start = 24
_globals["_HEARTBEATPING"]._serialized_end = 107
_globals["_HEARTBEATACK"]._serialized_start = 109
_globals["_HEARTBEATACK"]._serialized_end = 191
_globals["_ERRORINFO"]._serialized_start = 193
_globals["_ERRORINFO"]._serialized_end = 290
_globals["_SETTING"]._serialized_start = 292
_globals["_SETTING"]._serialized_end = 330
_globals["_HEARTBEATSTAT"]._serialized_start = 332
_globals["_HEARTBEATSTAT"]._serialized_end = 397
_globals["_HEARTBEATCONFIG"]._serialized_start = 399
_globals["_HEARTBEATCONFIG"]._serialized_end = 470
_globals["_CLIENTEVENT"]._serialized_start = 473
_globals["_CLIENTEVENT"]._serialized_end = 820
_globals["_CLIENTEVENT_TYPE"]._serialized_start = 721
_globals["_CLIENTEVENT_TYPE"]._serialized_end = 812
_globals["_LOGINREQUEST"]._serialized_start = 823
_globals["_LOGINREQUEST"]._serialized_end = 1334
_globals["_LOGINREQUEST_AUTHSERVICE"]._serialized_start = 1287
_globals["_LOGINREQUEST_AUTHSERVICE"]._serialized_end = 1316
_globals["_LOGINRESPONSE"]._serialized_start = 1337
_globals["_LOGINRESPONSE"]._serialized_end = 1583
_globals["_STREAMERRORSTANZA"]._serialized_start = 1585
_globals["_STREAMERRORSTANZA"]._serialized_end = 1632
_globals["_CLOSE"]._serialized_start = 1634
_globals["_CLOSE"]._serialized_end = 1641
_globals["_EXTENSION"]._serialized_start = 1643
_globals["_EXTENSION"]._serialized_end = 1680
_globals["_IQSTANZA"]._serialized_start = 1683
_globals["_IQSTANZA"]._serialized_end = 2032
_globals["_IQSTANZA_IQTYPE"]._serialized_start = 1980
_globals["_IQSTANZA_IQTYPE"]._serialized_end = 2032
_globals["_APPDATA"]._serialized_start = 2034
_globals["_APPDATA"]._serialized_end = 2071
_globals["_DATAMESSAGESTANZA"]._serialized_start = 2074
_globals["_DATAMESSAGESTANZA"]._serialized_end = 2446
_globals["_STREAMACK"]._serialized_start = 2448
_globals["_STREAMACK"]._serialized_end = 2459
_globals["_SELECTIVEACK"]._serialized_start = 2461
_globals["_SELECTIVEACK"]._serialized_end = 2487
# @@protoc_insertion_point(module_scope)
firebase-messaging-0.4.4/firebase_messaging/proto/mcs_pb2.pyi 0000664 0000000 0000000 00000102247 14675551151 0024322 0 ustar 00root root 0000000 0000000 """
@generated by mypy-protobuf. Do not edit manually!
isort:skip_file
Copyright 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
MCS protocol for communication between Chrome client and Mobile Connection
Server .
"""
import builtins
import collections.abc
import google.protobuf.descriptor
import google.protobuf.internal.containers
import google.protobuf.internal.enum_type_wrapper
import google.protobuf.message
import sys
import typing
if sys.version_info >= (3, 10):
import typing as typing_extensions
else:
import typing_extensions
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
@typing_extensions.final
class HeartbeatPing(google.protobuf.message.Message):
"""
Common fields/comments:
stream_id: no longer sent by server, each side keeps a counter
last_stream_id_received: sent only if a packet was received since last time
a last_stream was sent
status: new bitmask including the 'idle' as bit 0.
*
TAG: 0
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
STREAM_ID_FIELD_NUMBER: builtins.int
LAST_STREAM_ID_RECEIVED_FIELD_NUMBER: builtins.int
STATUS_FIELD_NUMBER: builtins.int
stream_id: builtins.int
last_stream_id_received: builtins.int
status: builtins.int
def __init__(
self,
*,
stream_id: builtins.int | None = ...,
last_stream_id_received: builtins.int | None = ...,
status: builtins.int | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"last_stream_id_received",
b"last_stream_id_received",
"status",
b"status",
"stream_id",
b"stream_id",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"last_stream_id_received",
b"last_stream_id_received",
"status",
b"status",
"stream_id",
b"stream_id",
],
) -> None: ...
global___HeartbeatPing = HeartbeatPing
@typing_extensions.final
class HeartbeatAck(google.protobuf.message.Message):
"""*
TAG: 1
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
STREAM_ID_FIELD_NUMBER: builtins.int
LAST_STREAM_ID_RECEIVED_FIELD_NUMBER: builtins.int
STATUS_FIELD_NUMBER: builtins.int
stream_id: builtins.int
last_stream_id_received: builtins.int
status: builtins.int
def __init__(
self,
*,
stream_id: builtins.int | None = ...,
last_stream_id_received: builtins.int | None = ...,
status: builtins.int | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"last_stream_id_received",
b"last_stream_id_received",
"status",
b"status",
"stream_id",
b"stream_id",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"last_stream_id_received",
b"last_stream_id_received",
"status",
b"status",
"stream_id",
b"stream_id",
],
) -> None: ...
global___HeartbeatAck = HeartbeatAck
@typing_extensions.final
class ErrorInfo(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
CODE_FIELD_NUMBER: builtins.int
MESSAGE_FIELD_NUMBER: builtins.int
TYPE_FIELD_NUMBER: builtins.int
EXTENSION_FIELD_NUMBER: builtins.int
code: builtins.int
message: builtins.str
type: builtins.str
@property
def extension(self) -> global___Extension: ...
def __init__(
self,
*,
code: builtins.int | None = ...,
message: builtins.str | None = ...,
type: builtins.str | None = ...,
extension: global___Extension | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"code",
b"code",
"extension",
b"extension",
"message",
b"message",
"type",
b"type",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"code",
b"code",
"extension",
b"extension",
"message",
b"message",
"type",
b"type",
],
) -> None: ...
global___ErrorInfo = ErrorInfo
@typing_extensions.final
class Setting(google.protobuf.message.Message):
"""MobileSettings class.
"u:f", "u:b", "u:s" - multi user devices reporting foreground, background
and stopped users.
hbping: heatbeat ping interval
rmq2v: include explicit stream IDs
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
NAME_FIELD_NUMBER: builtins.int
VALUE_FIELD_NUMBER: builtins.int
name: builtins.str
value: builtins.str
def __init__(
self,
*,
name: builtins.str | None = ...,
value: builtins.str | None = ...,
) -> None: ...
def HasField(
self, field_name: typing_extensions.Literal["name", b"name", "value", b"value"]
) -> builtins.bool: ...
def ClearField(
self, field_name: typing_extensions.Literal["name", b"name", "value", b"value"]
) -> None: ...
global___Setting = Setting
@typing_extensions.final
class HeartbeatStat(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
IP_FIELD_NUMBER: builtins.int
TIMEOUT_FIELD_NUMBER: builtins.int
INTERVAL_MS_FIELD_NUMBER: builtins.int
ip: builtins.str
timeout: builtins.bool
interval_ms: builtins.int
def __init__(
self,
*,
ip: builtins.str | None = ...,
timeout: builtins.bool | None = ...,
interval_ms: builtins.int | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"interval_ms", b"interval_ms", "ip", b"ip", "timeout", b"timeout"
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"interval_ms", b"interval_ms", "ip", b"ip", "timeout", b"timeout"
],
) -> None: ...
global___HeartbeatStat = HeartbeatStat
@typing_extensions.final
class HeartbeatConfig(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
UPLOAD_STAT_FIELD_NUMBER: builtins.int
IP_FIELD_NUMBER: builtins.int
INTERVAL_MS_FIELD_NUMBER: builtins.int
upload_stat: builtins.bool
ip: builtins.str
interval_ms: builtins.int
def __init__(
self,
*,
upload_stat: builtins.bool | None = ...,
ip: builtins.str | None = ...,
interval_ms: builtins.int | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"interval_ms", b"interval_ms", "ip", b"ip", "upload_stat", b"upload_stat"
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"interval_ms", b"interval_ms", "ip", b"ip", "upload_stat", b"upload_stat"
],
) -> None: ...
global___HeartbeatConfig = HeartbeatConfig
@typing_extensions.final
class ClientEvent(google.protobuf.message.Message):
"""ClientEvents are used to inform the server of failed and successful
connections.
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _Type:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _TypeEnumTypeWrapper(
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
ClientEvent._Type.ValueType
],
builtins.type,
):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
UNKNOWN: ClientEvent._Type.ValueType # 0
DISCARDED_EVENTS: ClientEvent._Type.ValueType # 1
"""Count of discarded events if the buffer filled up and was trimmed."""
FAILED_CONNECTION: ClientEvent._Type.ValueType # 2
"""Failed connection event: the connection failed to be established or we
had a login error.
"""
SUCCESSFUL_CONNECTION: ClientEvent._Type.ValueType # 3
"""Successful connection event: information about the last successful
connection, including the time at which it was established.
"""
class Type(_Type, metaclass=_TypeEnumTypeWrapper): ...
UNKNOWN: ClientEvent.Type.ValueType # 0
DISCARDED_EVENTS: ClientEvent.Type.ValueType # 1
"""Count of discarded events if the buffer filled up and was trimmed."""
FAILED_CONNECTION: ClientEvent.Type.ValueType # 2
"""Failed connection event: the connection failed to be established or we
had a login error.
"""
SUCCESSFUL_CONNECTION: ClientEvent.Type.ValueType # 3
"""Successful connection event: information about the last successful
connection, including the time at which it was established.
"""
TYPE_FIELD_NUMBER: builtins.int
NUMBER_DISCARDED_EVENTS_FIELD_NUMBER: builtins.int
NETWORK_TYPE_FIELD_NUMBER: builtins.int
TIME_CONNECTION_STARTED_MS_FIELD_NUMBER: builtins.int
TIME_CONNECTION_ENDED_MS_FIELD_NUMBER: builtins.int
ERROR_CODE_FIELD_NUMBER: builtins.int
TIME_CONNECTION_ESTABLISHED_MS_FIELD_NUMBER: builtins.int
type: global___ClientEvent.Type.ValueType
"""Common fields [1-99]"""
number_discarded_events: builtins.int
"""Fields for DISCARDED_EVENTS messages [100-199]"""
network_type: builtins.int
"""Fields for FAILED_CONNECTION and SUCCESSFUL_CONNECTION messages [200-299]
Network type is a value in net::NetworkChangeNotifier::ConnectionType.
"""
time_connection_started_ms: builtins.int
time_connection_ended_ms: builtins.int
error_code: builtins.int
"""Error code should be a net::Error value."""
time_connection_established_ms: builtins.int
"""Fields for SUCCESSFUL_CONNECTION messages [300-399]"""
def __init__(
self,
*,
type: global___ClientEvent.Type.ValueType | None = ...,
number_discarded_events: builtins.int | None = ...,
network_type: builtins.int | None = ...,
time_connection_started_ms: builtins.int | None = ...,
time_connection_ended_ms: builtins.int | None = ...,
error_code: builtins.int | None = ...,
time_connection_established_ms: builtins.int | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"error_code",
b"error_code",
"network_type",
b"network_type",
"number_discarded_events",
b"number_discarded_events",
"time_connection_ended_ms",
b"time_connection_ended_ms",
"time_connection_established_ms",
b"time_connection_established_ms",
"time_connection_started_ms",
b"time_connection_started_ms",
"type",
b"type",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"error_code",
b"error_code",
"network_type",
b"network_type",
"number_discarded_events",
b"number_discarded_events",
"time_connection_ended_ms",
b"time_connection_ended_ms",
"time_connection_established_ms",
b"time_connection_established_ms",
"time_connection_started_ms",
b"time_connection_started_ms",
"type",
b"type",
],
) -> None: ...
global___ClientEvent = ClientEvent
@typing_extensions.final
class LoginRequest(google.protobuf.message.Message):
"""*
TAG: 2
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _AuthService:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _AuthServiceEnumTypeWrapper(
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
LoginRequest._AuthService.ValueType
],
builtins.type,
):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
ANDROID_ID: LoginRequest._AuthService.ValueType # 2
class AuthService(_AuthService, metaclass=_AuthServiceEnumTypeWrapper): ...
ANDROID_ID: LoginRequest.AuthService.ValueType # 2
ID_FIELD_NUMBER: builtins.int
DOMAIN_FIELD_NUMBER: builtins.int
USER_FIELD_NUMBER: builtins.int
RESOURCE_FIELD_NUMBER: builtins.int
AUTH_TOKEN_FIELD_NUMBER: builtins.int
DEVICE_ID_FIELD_NUMBER: builtins.int
LAST_RMQ_ID_FIELD_NUMBER: builtins.int
SETTING_FIELD_NUMBER: builtins.int
RECEIVED_PERSISTENT_ID_FIELD_NUMBER: builtins.int
ADAPTIVE_HEARTBEAT_FIELD_NUMBER: builtins.int
HEARTBEAT_STAT_FIELD_NUMBER: builtins.int
USE_RMQ2_FIELD_NUMBER: builtins.int
ACCOUNT_ID_FIELD_NUMBER: builtins.int
AUTH_SERVICE_FIELD_NUMBER: builtins.int
NETWORK_TYPE_FIELD_NUMBER: builtins.int
STATUS_FIELD_NUMBER: builtins.int
CLIENT_EVENT_FIELD_NUMBER: builtins.int
id: builtins.str
"""Must be present ( proto required ), may be empty"""
domain: builtins.str
"""string.
mcs.android.com.
"""
user: builtins.str
"""Decimal android ID"""
resource: builtins.str
auth_token: builtins.str
"""Secret"""
device_id: builtins.str
"""Format is: android-HEX_DEVICE_ID
The user is the decimal value.
"""
last_rmq_id: builtins.int
"""RMQ1 - no longer used"""
@property
def setting(
self,
) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[
global___Setting
]: ...
@property
def received_persistent_id(
self,
) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
"""optional int32 compress = 9;"""
adaptive_heartbeat: builtins.bool
"""Replaced by "rmq2v" setting
optional bool include_stream_ids = 11;
"""
@property
def heartbeat_stat(self) -> global___HeartbeatStat: ...
use_rmq2: builtins.bool
"""Must be true."""
account_id: builtins.int
auth_service: global___LoginRequest.AuthService.ValueType
"""ANDROID_ID = 2"""
network_type: builtins.int
status: builtins.int
@property
def client_event(
self,
) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[
global___ClientEvent
]:
"""Events recorded on the client after the last successful connection."""
def __init__(
self,
*,
id: builtins.str | None = ...,
domain: builtins.str | None = ...,
user: builtins.str | None = ...,
resource: builtins.str | None = ...,
auth_token: builtins.str | None = ...,
device_id: builtins.str | None = ...,
last_rmq_id: builtins.int | None = ...,
setting: collections.abc.Iterable[global___Setting] | None = ...,
received_persistent_id: collections.abc.Iterable[builtins.str] | None = ...,
adaptive_heartbeat: builtins.bool | None = ...,
heartbeat_stat: global___HeartbeatStat | None = ...,
use_rmq2: builtins.bool | None = ...,
account_id: builtins.int | None = ...,
auth_service: global___LoginRequest.AuthService.ValueType | None = ...,
network_type: builtins.int | None = ...,
status: builtins.int | None = ...,
client_event: collections.abc.Iterable[global___ClientEvent] | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"account_id",
b"account_id",
"adaptive_heartbeat",
b"adaptive_heartbeat",
"auth_service",
b"auth_service",
"auth_token",
b"auth_token",
"device_id",
b"device_id",
"domain",
b"domain",
"heartbeat_stat",
b"heartbeat_stat",
"id",
b"id",
"last_rmq_id",
b"last_rmq_id",
"network_type",
b"network_type",
"resource",
b"resource",
"status",
b"status",
"use_rmq2",
b"use_rmq2",
"user",
b"user",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"account_id",
b"account_id",
"adaptive_heartbeat",
b"adaptive_heartbeat",
"auth_service",
b"auth_service",
"auth_token",
b"auth_token",
"client_event",
b"client_event",
"device_id",
b"device_id",
"domain",
b"domain",
"heartbeat_stat",
b"heartbeat_stat",
"id",
b"id",
"last_rmq_id",
b"last_rmq_id",
"network_type",
b"network_type",
"received_persistent_id",
b"received_persistent_id",
"resource",
b"resource",
"setting",
b"setting",
"status",
b"status",
"use_rmq2",
b"use_rmq2",
"user",
b"user",
],
) -> None: ...
global___LoginRequest = LoginRequest
@typing_extensions.final
class LoginResponse(google.protobuf.message.Message):
"""*
TAG: 3
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
ID_FIELD_NUMBER: builtins.int
JID_FIELD_NUMBER: builtins.int
ERROR_FIELD_NUMBER: builtins.int
SETTING_FIELD_NUMBER: builtins.int
STREAM_ID_FIELD_NUMBER: builtins.int
LAST_STREAM_ID_RECEIVED_FIELD_NUMBER: builtins.int
HEARTBEAT_CONFIG_FIELD_NUMBER: builtins.int
SERVER_TIMESTAMP_FIELD_NUMBER: builtins.int
id: builtins.str
jid: builtins.str
"""Not used."""
@property
def error(self) -> global___ErrorInfo:
"""Null if login was ok."""
@property
def setting(
self,
) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[
global___Setting
]: ...
stream_id: builtins.int
last_stream_id_received: builtins.int
"""Should be "1" """
@property
def heartbeat_config(self) -> global___HeartbeatConfig: ...
server_timestamp: builtins.int
"""used by the client to synchronize with the server timestamp."""
def __init__(
self,
*,
id: builtins.str | None = ...,
jid: builtins.str | None = ...,
error: global___ErrorInfo | None = ...,
setting: collections.abc.Iterable[global___Setting] | None = ...,
stream_id: builtins.int | None = ...,
last_stream_id_received: builtins.int | None = ...,
heartbeat_config: global___HeartbeatConfig | None = ...,
server_timestamp: builtins.int | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"error",
b"error",
"heartbeat_config",
b"heartbeat_config",
"id",
b"id",
"jid",
b"jid",
"last_stream_id_received",
b"last_stream_id_received",
"server_timestamp",
b"server_timestamp",
"stream_id",
b"stream_id",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"error",
b"error",
"heartbeat_config",
b"heartbeat_config",
"id",
b"id",
"jid",
b"jid",
"last_stream_id_received",
b"last_stream_id_received",
"server_timestamp",
b"server_timestamp",
"setting",
b"setting",
"stream_id",
b"stream_id",
],
) -> None: ...
global___LoginResponse = LoginResponse
@typing_extensions.final
class StreamErrorStanza(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
TYPE_FIELD_NUMBER: builtins.int
TEXT_FIELD_NUMBER: builtins.int
type: builtins.str
text: builtins.str
def __init__(
self,
*,
type: builtins.str | None = ...,
text: builtins.str | None = ...,
) -> None: ...
def HasField(
self, field_name: typing_extensions.Literal["text", b"text", "type", b"type"]
) -> builtins.bool: ...
def ClearField(
self, field_name: typing_extensions.Literal["text", b"text", "type", b"type"]
) -> None: ...
global___StreamErrorStanza = StreamErrorStanza
@typing_extensions.final
class Close(google.protobuf.message.Message):
"""*
TAG: 4
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
def __init__(
self,
) -> None: ...
global___Close = Close
@typing_extensions.final
class Extension(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
ID_FIELD_NUMBER: builtins.int
DATA_FIELD_NUMBER: builtins.int
id: builtins.int
"""12: SelectiveAck
13: StreamAck
"""
data: builtins.bytes
def __init__(
self,
*,
id: builtins.int | None = ...,
data: builtins.bytes | None = ...,
) -> None: ...
def HasField(
self, field_name: typing_extensions.Literal["data", b"data", "id", b"id"]
) -> builtins.bool: ...
def ClearField(
self, field_name: typing_extensions.Literal["data", b"data", "id", b"id"]
) -> None: ...
global___Extension = Extension
@typing_extensions.final
class IqStanza(google.protobuf.message.Message):
"""*
TAG: 7
IqRequest must contain a single extension. IqResponse may contain 0 or 1
extensions.
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _IqType:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _IqTypeEnumTypeWrapper(
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[
IqStanza._IqType.ValueType
],
builtins.type,
):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
GET: IqStanza._IqType.ValueType # 0
SET: IqStanza._IqType.ValueType # 1
RESULT: IqStanza._IqType.ValueType # 2
IQ_ERROR: IqStanza._IqType.ValueType # 3
class IqType(_IqType, metaclass=_IqTypeEnumTypeWrapper): ...
GET: IqStanza.IqType.ValueType # 0
SET: IqStanza.IqType.ValueType # 1
RESULT: IqStanza.IqType.ValueType # 2
IQ_ERROR: IqStanza.IqType.ValueType # 3
RMQ_ID_FIELD_NUMBER: builtins.int
TYPE_FIELD_NUMBER: builtins.int
ID_FIELD_NUMBER: builtins.int
FROM_FIELD_NUMBER: builtins.int
TO_FIELD_NUMBER: builtins.int
ERROR_FIELD_NUMBER: builtins.int
EXTENSION_FIELD_NUMBER: builtins.int
PERSISTENT_ID_FIELD_NUMBER: builtins.int
STREAM_ID_FIELD_NUMBER: builtins.int
LAST_STREAM_ID_RECEIVED_FIELD_NUMBER: builtins.int
ACCOUNT_ID_FIELD_NUMBER: builtins.int
STATUS_FIELD_NUMBER: builtins.int
rmq_id: builtins.int
type: global___IqStanza.IqType.ValueType
id: builtins.str
to: builtins.str
@property
def error(self) -> global___ErrorInfo: ...
@property
def extension(self) -> global___Extension:
"""Only field used in the 38+ protocol (besides common last_stream_id_received, status, rmq_id)"""
persistent_id: builtins.str
stream_id: builtins.int
last_stream_id_received: builtins.int
account_id: builtins.int
status: builtins.int
def __init__(
self,
*,
rmq_id: builtins.int | None = ...,
type: global___IqStanza.IqType.ValueType | None = ...,
id: builtins.str | None = ...,
to: builtins.str | None = ...,
error: global___ErrorInfo | None = ...,
extension: global___Extension | None = ...,
persistent_id: builtins.str | None = ...,
stream_id: builtins.int | None = ...,
last_stream_id_received: builtins.int | None = ...,
account_id: builtins.int | None = ...,
status: builtins.int | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"account_id",
b"account_id",
"error",
b"error",
"extension",
b"extension",
"from",
b"from",
"id",
b"id",
"last_stream_id_received",
b"last_stream_id_received",
"persistent_id",
b"persistent_id",
"rmq_id",
b"rmq_id",
"status",
b"status",
"stream_id",
b"stream_id",
"to",
b"to",
"type",
b"type",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"account_id",
b"account_id",
"error",
b"error",
"extension",
b"extension",
"from",
b"from",
"id",
b"id",
"last_stream_id_received",
b"last_stream_id_received",
"persistent_id",
b"persistent_id",
"rmq_id",
b"rmq_id",
"status",
b"status",
"stream_id",
b"stream_id",
"to",
b"to",
"type",
b"type",
],
) -> None: ...
global___IqStanza = IqStanza
@typing_extensions.final
class AppData(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
KEY_FIELD_NUMBER: builtins.int
VALUE_FIELD_NUMBER: builtins.int
key: builtins.str
value: builtins.str
def __init__(
self,
*,
key: builtins.str | None = ...,
value: builtins.str | None = ...,
) -> None: ...
def HasField(
self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]
) -> builtins.bool: ...
def ClearField(
self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]
) -> None: ...
global___AppData = AppData
@typing_extensions.final
class DataMessageStanza(google.protobuf.message.Message):
"""*
TAG: 8
Not used.
optional int64 rmq_id = 1;
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
ID_FIELD_NUMBER: builtins.int
FROM_FIELD_NUMBER: builtins.int
TO_FIELD_NUMBER: builtins.int
CATEGORY_FIELD_NUMBER: builtins.int
TOKEN_FIELD_NUMBER: builtins.int
APP_DATA_FIELD_NUMBER: builtins.int
FROM_TRUSTED_SERVER_FIELD_NUMBER: builtins.int
PERSISTENT_ID_FIELD_NUMBER: builtins.int
STREAM_ID_FIELD_NUMBER: builtins.int
LAST_STREAM_ID_RECEIVED_FIELD_NUMBER: builtins.int
REG_ID_FIELD_NUMBER: builtins.int
DEVICE_USER_ID_FIELD_NUMBER: builtins.int
TTL_FIELD_NUMBER: builtins.int
SENT_FIELD_NUMBER: builtins.int
QUEUED_FIELD_NUMBER: builtins.int
STATUS_FIELD_NUMBER: builtins.int
RAW_DATA_FIELD_NUMBER: builtins.int
IMMEDIATE_ACK_FIELD_NUMBER: builtins.int
id: builtins.str
"""This is the message ID, set by client, DMP.9 (message_id)"""
to: builtins.str
"""Part of DMRequest - also the key in DataMessageProto."""
category: builtins.str
"""Package name. DMP.2"""
token: builtins.str
"""The collapsed key, DMP.3"""
@property
def app_data(
self,
) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[
global___AppData
]:
"""User data + GOOGLE. prefixed special entries, DMP.4"""
from_trusted_server: builtins.bool
"""Not used."""
persistent_id: builtins.str
"""Part of the ACK protocol, returned in DataMessageResponse on server side.
It's part of the key of DMP.
"""
stream_id: builtins.int
"""In-stream ack. Increments on each message sent - a bit redundant
Not used in DMP/DMR.
"""
last_stream_id_received: builtins.int
reg_id: builtins.str
"""Not used.
optional string permission = 12;
Sent by the device shortly after registration.
"""
device_user_id: builtins.int
"""Not used.
optional string pkg_signature = 14;
Not used.
optional string client_id = 15;
serial number of the target user, DMP.8
It is the 'serial number' according to user manager.
"""
ttl: builtins.int
"""Time to live, in seconds."""
sent: builtins.int
"""Timestamp ( according to client ) when message was sent by app, in seconds"""
queued: builtins.int
"""How long has the message been queued before the flush, in seconds.
This is needed to account for the time difference between server and
client: server should adjust 'sent' based on its 'receive' time.
"""
status: builtins.int
raw_data: builtins.bytes
"""Optional field containing the binary payload of the message."""
immediate_ack: builtins.bool
"""Not used.
How long the message was delayed before it was sent, in seconds.
optional int32 actual_delay = 23;
If set the server requests immediate ack. Used for important messages and
for testing.
"""
def __init__(
self,
*,
id: builtins.str | None = ...,
to: builtins.str | None = ...,
category: builtins.str | None = ...,
token: builtins.str | None = ...,
app_data: collections.abc.Iterable[global___AppData] | None = ...,
from_trusted_server: builtins.bool | None = ...,
persistent_id: builtins.str | None = ...,
stream_id: builtins.int | None = ...,
last_stream_id_received: builtins.int | None = ...,
reg_id: builtins.str | None = ...,
device_user_id: builtins.int | None = ...,
ttl: builtins.int | None = ...,
sent: builtins.int | None = ...,
queued: builtins.int | None = ...,
status: builtins.int | None = ...,
raw_data: builtins.bytes | None = ...,
immediate_ack: builtins.bool | None = ...,
) -> None: ...
def HasField(
self,
field_name: typing_extensions.Literal[
"category",
b"category",
"device_user_id",
b"device_user_id",
"from",
b"from",
"from_trusted_server",
b"from_trusted_server",
"id",
b"id",
"immediate_ack",
b"immediate_ack",
"last_stream_id_received",
b"last_stream_id_received",
"persistent_id",
b"persistent_id",
"queued",
b"queued",
"raw_data",
b"raw_data",
"reg_id",
b"reg_id",
"sent",
b"sent",
"status",
b"status",
"stream_id",
b"stream_id",
"to",
b"to",
"token",
b"token",
"ttl",
b"ttl",
],
) -> builtins.bool: ...
def ClearField(
self,
field_name: typing_extensions.Literal[
"app_data",
b"app_data",
"category",
b"category",
"device_user_id",
b"device_user_id",
"from",
b"from",
"from_trusted_server",
b"from_trusted_server",
"id",
b"id",
"immediate_ack",
b"immediate_ack",
"last_stream_id_received",
b"last_stream_id_received",
"persistent_id",
b"persistent_id",
"queued",
b"queued",
"raw_data",
b"raw_data",
"reg_id",
b"reg_id",
"sent",
b"sent",
"status",
b"status",
"stream_id",
b"stream_id",
"to",
b"to",
"token",
b"token",
"ttl",
b"ttl",
],
) -> None: ...
global___DataMessageStanza = DataMessageStanza
@typing_extensions.final
class StreamAck(google.protobuf.message.Message):
"""*
Included in IQ with ID 13, sent from client or server after 10 unconfirmed
messages.
No last_streamid_received required. This is included within an IqStanza,
which includes the last_stream_id_received.
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
def __init__(
self,
) -> None: ...
global___StreamAck = StreamAck
@typing_extensions.final
class SelectiveAck(google.protobuf.message.Message):
"""*
Included in IQ sent after LoginResponse from server with ID 12.
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
ID_FIELD_NUMBER: builtins.int
@property
def id(
self,
) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[
builtins.str
]: ...
def __init__(
self,
*,
id: collections.abc.Iterable[builtins.str] | None = ...,
) -> None: ...
def ClearField(
self, field_name: typing_extensions.Literal["id", b"id"]
) -> None: ...
global___SelectiveAck = SelectiveAck
firebase-messaging-0.4.4/firebase_messaging/py.typed 0000664 0000000 0000000 00000000000 14675551151 0022566 0 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/pyproject.toml 0000664 0000000 0000000 00000006166 14675551151 0020211 0 ustar 00root root 0000000 0000000 [project]
name = "firebase-messaging"
version = "0.4.4"
description = "FCM/GCM push notification client"
authors = [{ name = "sdb9696", email = "sdb9696@users.noreply.github.com" }]
license = { text="MIT" }
readme = "README.rst"
requires-python = ">=3.9.0"
dependencies = [
"protobuf>=4.24.3,<6",
"http-ece~=1.1.0",
"cryptography>=2.5",
"aiohttp>=3.9.3",
]
keywords = [
"Firebase",
"Firebase Cloud Messaging",
"Google Cloud Messaging",
]
classifiers = [
"Environment :: Other Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries :: Python Modules"
]
[project.urls]
Homepage = "https://github.com/sdb9696/firebase-messaging"
Repository = "https://github.com/sdb9696/firebase-messaging"
Documentation = "http://firebase-messaging.readthedocs.io/"
"Bug Tracker" = "https://github.com/sdb9696/firebase-messaging/issues"
[project.optional-dependencies]
docs = ["sphinx==7.1.2", "sphinx-rtd-theme~=1.3", "sphinx-autodoc-typehints~=1.24", "myst-parser"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.sdist]
include = [
"/firebase_messaging",
"/tests",
"/docs",
"/CHANGELOG.md",
]
[tool.uv]
dev-dependencies = [
"pre-commit",
"pytest",
"pytest-mock",
"pytest-asyncio",
"pytest-cov",
"async-timeout>=3.0.0",
"pytest-socket~=0.7.0",
"aioresponses~=0.7.6",
"types-protobuf>=4.24",
"mypy~=1.0",
"ruff>=0.6.8",
]
[tool.pytest.ini_options]
testpaths = "tests"
norecursedirs = ".git"
asyncio_mode = "auto"
addopts = "--disable-socket --allow-unix-socket"
[tool.coverage.run]
omit = [
"firebase_messaging/proto/*",
"firebase_messaging/decrypt.py*"
]
[tool.ruff]
target-version = "py39"
exclude = [
"firebase_messaging/proto/*"
]
lint.select = [
"E", # pycodestyle
# "D", # pydocstyle
"F", # pyflakes
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
"I", # isort
"S", # bandit
]
lint.ignore = [
"D105", # Missing docstring in magic method
"D107", # Missing docstring in `__init__`
]
[tool.ruff.lint.pydocstyle]
convention = "pep257"
[tool.ruff.lint.per-file-ignores]
"tests/*.py" = [
"D100",
"D101",
"D102",
"D103",
"D104",
"F401",
"S101", # allow asserts
"E501", # ignore line-too-longs
]
"docs/source/conf.py" = [
"D100",
"D103",
]
[tool.mypy]
# exclude pattern inside triple quotes should be synced to .pre-commit-config.yaml
exclude = '''(?x)(
^docs/.*|
^tests/.*|
^firebase_messaging/proto/.*py$
)'''
disallow_untyped_defs = true
[[tool.mypy.overrides]]
module = [
"firebase_messaging.proto.*"
]
disallow_untyped_defs = false
[tool.doc8]
paths = ["docs"]
ignore = ["D001"]
ignore-path = ["docs/build"]
firebase-messaging-0.4.4/tests/ 0000775 0000000 0000000 00000000000 14675551151 0016426 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/tests/__init__.py 0000664 0000000 0000000 00000000000 14675551151 0020525 0 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/tests/conftest.py 0000664 0000000 0000000 00000006362 14675551151 0020634 0 ustar 00root root 0000000 0000000 """Test configuration for the Ring platform."""
import asyncio
import json
import logging
import os
import threading
from unittest.mock import patch
import pytest
from aioresponses import CallbackResult, aioresponses
from google.protobuf.json_format import Parse as JsonParse
from firebase_messaging.fcmpushclient import (
FcmPushClient,
FcmPushClientConfig,
FcmRegisterConfig,
)
from firebase_messaging.proto.checkin_pb2 import AndroidCheckinResponse
from firebase_messaging.proto.mcs_pb2 import LoginResponse
from tests.fakes import FakeMcsEndpoint
def load_fixture(filename):
"""Load a fixture."""
path = os.path.join(os.path.dirname(__file__), "fixtures", filename)
with open(path) as fdp:
return fdp.read()
def load_fixture_as_dict(filename):
"""Load a fixture."""
return json.loads(load_fixture(filename))
def load_fixture_as_msg(filename, msg_class):
"""Load a fixture."""
msg = msg_class()
JsonParse(load_fixture(filename), msg)
return msg
@pytest.fixture()
async def fake_mcs_endpoint():
fmce = FakeMcsEndpoint()
async def _mock_open_conn(*_, **__):
return fmce.client_reader, fmce.client_writer
with patch("asyncio.open_connection", side_effect=_mock_open_conn, autospec=True):
yield fmce
fmce.close()
@pytest.fixture()
async def logged_in_push_client(request, fake_mcs_endpoint, mocker, caplog):
clients = {}
caplog.set_level(logging.DEBUG)
async def _logged_in_push_client(
msg_callback,
credentials,
*,
callback_obj=None,
supress_disconnect=False,
**config_kwargs,
):
config = FcmPushClientConfig(**config_kwargs)
fcm_config = FcmRegisterConfig("project-1234", "bar", "foobar", "foobar")
pr = FcmPushClient(
msg_callback,
fcm_config,
credentials,
None,
callback_context=callback_obj,
config=config,
)
await pr.checkin_or_register()
await pr.start()
await fake_mcs_endpoint.get_message()
lr = load_fixture_as_msg("login_response.json", LoginResponse)
await fake_mcs_endpoint.put_message(lr)
clients[pr] = supress_disconnect
return pr
yield _logged_in_push_client
for k, v in clients.items():
if not v:
await k.stop()
@pytest.fixture(autouse=True, name="aioresponses_mock")
def aioresponses_mock_fixture():
with aioresponses() as mock:
mock.post(
"https://android.clients.google.com/checkin",
body=load_fixture_as_msg(
"android_checkin_response.json", AndroidCheckinResponse
).SerializeToString(),
)
mock.post(
"https://android.clients.google.com/c2dm/register3",
body=load_fixture("gcm_register_response.txt"),
)
mock.post(
"https://firebaseinstallations.googleapis.com/v1/projects/project-1234/installations",
payload=load_fixture_as_dict("fcm_install_response.json"),
)
mock.post(
"https://fcmregistrations.googleapis.com/v1/projects/project-1234/registrations",
payload=load_fixture_as_dict("fcm_register_response.json"),
)
yield mock
firebase-messaging-0.4.4/tests/fakes.py 0000664 0000000 0000000 00000010165 14675551151 0020074 0 ustar 00root root 0000000 0000000 import asyncio
import struct
from unittest.mock import patch
# When support for cpython older than 3.11 is dropped
# async_timeout can be replaced with asyncio.timeout
from async_timeout import timeout as asyncio_timeout
from firebase_messaging.fcmpushclient import MCS_MESSAGE_TAG, MCS_VERSION, FcmPushClient
from firebase_messaging.proto.checkin_pb2 import AndroidCheckinResponse
from firebase_messaging.proto.mcs_pb2 import LoginResponse
class FakeMcsEndpoint:
def __init__(self):
# self.connection_mock = patch(
# "asyncio.open_connection", side_effect=self.open_connection, autospec=True
# )
# self.connection_mock.start()
self.client_writer = self.FakeWriter()
self.client_reader = self.FakeReader()
def close(self):
# self.connection_mock.stop()
pass
async def open_connection(self, *_, **__):
# Queues should be created on the loop that will be accessing them
self.client_writer = self.FakeWriter()
self.client_reader = self.FakeReader()
return self.client_reader, self.client_writer
async def put_message(self, message):
await self.client_reader.put_message(message)
async def put_error(self, error):
await self.client_reader.put_error(error)
async def get_message(self):
return await self.client_writer.get_message()
class FakeReader:
def __init__(self):
self.queue = asyncio.Queue()
self.lock = asyncio.Lock()
async def readexactly(self, size):
if size == 0:
return b""
val = await self.queue.get()
if isinstance(val, BaseException):
raise val
else:
for _ in range(1, size):
val += await self.queue.get()
return val
async def put_message(self, message):
include_version = isinstance(message, LoginResponse)
packet = FcmPushClient._make_packet(message, include_version)
async with self.lock:
for p in packet:
b = bytes([p])
await self.queue.put(b)
async def put_error(self, error):
async with self.lock:
await self.queue.put(error)
class FakeWriter:
def __init__(self):
self.queue = asyncio.Queue()
self.buf = ""
self.lock = asyncio.Lock()
def write(self, buffer):
for i in buffer:
b = bytes([i])
self.queue.put_nowait(b)
async def drain(self):
pass
def close(self):
pass
async def wait_closed(self):
pass
async def get_bytes(self, size):
async with self.lock:
val = b""
for _ in range(size):
val += await self.queue.get()
return val
async def get_message(self, timeout=2):
async with asyncio_timeout(timeout):
r = await self.get_bytes(1)
(b,) = struct.unpack("B", r)
if b == MCS_VERSION: # first message
r = await self.get_bytes(1)
(b,) = struct.unpack("B", r)
tag = b
size = await self._read_varint32()
msgstr = await self.get_bytes(size)
msg_class = next(
iter([c for c, t in MCS_MESSAGE_TAG.items() if t == tag])
)
msg = msg_class()
msg.ParseFromString(msgstr)
return msg
# protobuf variable length integers are encoded in base 128
# each byte contains 7 bits of the integer and the msb is set if there's
# more. pretty simple to implement
async def _read_varint32(self):
res = 0
shift = 0
while True:
r = await self.get_bytes(1)
(b,) = struct.unpack("B", r)
res |= (b & 0x7F) << shift
if (b & 0x80) == 0:
break
shift += 7
return res
firebase-messaging-0.4.4/tests/fixtures/ 0000775 0000000 0000000 00000000000 14675551151 0020277 5 ustar 00root root 0000000 0000000 firebase-messaging-0.4.4/tests/fixtures/android_checkin_response.json 0000664 0000000 0000000 00000000303 14675551151 0026210 0 ustar 00root root 0000000 0000000 {
"statsOk": true,
"timeMsec": "1697711296660",
"androidId": "5678901234567890123",
"securityToken": "0123456789012345678",
"versionInfo": "Y9B3d2P5mMMSQKri541rkWUVCal3tBg"
}
firebase-messaging-0.4.4/tests/fixtures/close.json 0000664 0000000 0000000 00000000003 14675551151 0022270 0 ustar 00root root 0000000 0000000 {}
firebase-messaging-0.4.4/tests/fixtures/credentials.json 0000664 0000000 0000000 00000004417 14675551151 0023475 0 ustar 00root root 0000000 0000000 {
"gcm": {
"token": "XYZ01234zyx:APA91b0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234abcdefghijklmnopqrstuvwxyz",
"app_id": "abcdef01-ef12-1234-12ef-abcdef012345",
"android_id": "5678901234567890123",
"security_token": "0123456789012345678"
},
"keys": {
"public": "BPEHm32RWI4db8FCk0IM6G9f9vz_uJeRiuU64Y5dkyZjkBXmyGgzwzZMylPaLNvg50EuoQmNlU7sSMUf0mYctn0=",
"private": "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg-jTz69Lsq4oa49_5PMMkcqYFwkEGc5L-zGDwUwfY5eahRANCAATxB5t9kViOHW_BQpNCDOhvX_b8_7iXkYrlOuGOXZMmY5AV5shoM8M2TMpT2izb4OdBLqEJjZVO7EjFH9JmHLZ9",
"secret": "_zGHqwp9rRP5cgzilMLvCA"
},
"fcm": {
"registration": {
"name": "projects/project-1234/registrations/abcdefghijklmnopqrstuvwxyz-0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234abcdefghijklmnopqrstuvwxyz_XYZ01234zyx:APA91b0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"token": "abcdefghijklmnopqrstuvwxyz-0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234abcdefghijklmnopqrstuvwxyz_XYZ01234zyx:APA91b0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"web": {
"endpoint": "https://fcm.googleapis.com/fcm/send/XYZ01234zyx:APA91b0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234abcdefghijklmnopqrstuvwxyz",
"p256dh": "123456967219824KDJDOWFNAFW=",
"auth": "DSFNDSAKGFAGFA=="
}
},
"installation": {
"token": "OPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijkl",
"expires_in": 604800,
"refresh_token": "1_OPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijkl",
"fid": "1234AMXDRTTYODLsd-nb14",
"created_at": 36245.436300371
}
},
"config": {
"bundle_id": "project.push.com",
"project_id": "project-1234",
"vapid_key": "BDOU99-h67HcA6JeFXHbSNMu7e2yNNu3RzoMj8TM4W88jITfq7ZmPvIM1Iv-4_l2LxQcYwhqby2xGpWwzjfAnG4"
}
}
firebase-messaging-0.4.4/tests/fixtures/data_message_stanza.json 0000664 0000000 0000000 00000001442 14675551151 0025170 0 ustar 00root root 0000000 0000000 {
"id": "ABCDEF12",
"from": "ZYXW01-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop-1_zyxwvutsrqZYXWVUTSRQ12345",
"category": "org.chromium.linux",
"appData": [
{
"key": "google.source",
"value": "webpush"
},
{
"key": "encryption",
"value": "salt=zZYXWVUTSRQ12yxwvutsrq=="
},
{
"key": "subtype",
"value": "abcdef01-ef12-1234-12ef-abcdef012345"
},
{
"key": "crypto-key",
"value": "dh=BAGEFtID7WlmwzQ9pbjdRYAhfPe7Z8lA3ZGIPUh0SE3ikoY2PIrWUP0rmhpE4Kl8ImgMUDjKWrz0WmtLxORIHuw="
}
],
"persistentId": "0:0123456789123456%0123456789abcdef",
"lastStreamIdReceived": 6,
"ttl": 240,
"sent": "1697189311810",
"rawData": "3vJvXzzGf1gF9D7eB6/ioZT+lWWstyY9B/Yjebjm7mU3YTg="
}
firebase-messaging-0.4.4/tests/fixtures/fcm_install_response.json 0000664 0000000 0000000 00000001102 14675551151 0025375 0 ustar 00root root 0000000 0000000 {
"name": "projects/123456789101/installations/1234AMXDRTTYODLsd-nb14",
"fid": "1234AMXDRTTYODLsd-nb14",
"refreshToken": "1_OPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijkl",
"authToken": {
"token": "OPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijklOPQRSTUVWXYZ-01234abcdefghijkl",
"expiresIn": "604800s"
}
}
firebase-messaging-0.4.4/tests/fixtures/fcm_register_response.json 0000664 0000000 0000000 00000001264 14675551151 0025564 0 ustar 00root root 0000000 0000000 {
"name": "projects/project-1234/registrations/abcdefghijklmnopqrstuvwxyz-0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234abcdefghijklmnopqrstuvwxyz_XYZ01234zyx:APA91b0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"token": "abcdefghijklmnopqrstuvwxyz-0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234abcdefghijklmnopqrstuvwxyz_XYZ01234zyx:APA91b0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"web": {
"endpoint": "https://fcm.googleapis.com/fcm/send/XYZ01234zyx:APA91b0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234abcdefghijklmnopqrstuvwxyz",
"p256dh": "123456967219824KDJDOWFNAFW=",
"auth": "DSFNDSAKGFAGFA=="
}
}
firebase-messaging-0.4.4/tests/fixtures/gcm_register_response.txt 0000664 0000000 0000000 00000000237 14675551151 0025432 0 ustar 00root root 0000000 0000000 token=XYZ01234zyx:APA91b0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234abcdefghijklmnopqrstuvwxyz
firebase-messaging-0.4.4/tests/fixtures/heartbeat_ack.json 0000664 0000000 0000000 00000000042 14675551151 0023743 0 ustar 00root root 0000000 0000000 {
"lastStreamIdReceived": 3
}
firebase-messaging-0.4.4/tests/fixtures/heartbeat_ping.json 0000664 0000000 0000000 00000000003 14675551151 0024137 0 ustar 00root root 0000000 0000000 {}
firebase-messaging-0.4.4/tests/fixtures/iq_stanza_selective_ack.json 0000664 0000000 0000000 00000000227 14675551151 0026045 0 ustar 00root root 0000000 0000000 {
"type": "SET",
"id": "",
"extension": {
"id": 12,
"data": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn=="
}
}
firebase-messaging-0.4.4/tests/fixtures/iq_stanza_stream_ack.json 0000664 0000000 0000000 00000000117 14675551151 0025353 0 ustar 00root root 0000000 0000000 {
"type": "SET",
"id": "",
"extension": {
"id": 13
}
}
firebase-messaging-0.4.4/tests/fixtures/login_request.json 0000664 0000000 0000000 00000001025 14675551151 0024050 0 ustar 00root root 0000000 0000000 {
"id": "chrome-63.0.3234.0",
"domain": "mcs.android.com",
"user": "0123456789876543210",
"resource": "9876543210987654321",
"authToken": "123456789123456789",
"deviceId": "android-490ca293031302b0",
"setting": [
{
"name": "new_vc",
"value": "1"
}
],
"adaptiveHeartbeat": false,
"heartbeatStat": {
"ip": "",
"timeout": true,
"intervalMs": 30000
},
"useRmq2": true,
"authService": "ANDROID_ID",
"networkType": 1
}
firebase-messaging-0.4.4/tests/fixtures/login_response.json 0000664 0000000 0000000 00000000230 14675551151 0024213 0 ustar 00root root 0000000 0000000 {
"id": "chrome-63.0.3234.0",
"jid": "user@firebase.com/notifications",
"lastStreamIdReceived": 1,
"serverTimestamp": "1697190143057"
}
firebase-messaging-0.4.4/tests/test_fcmpushclient.py 0000664 0000000 0000000 00000014767 14675551151 0022722 0 ustar 00root root 0000000 0000000 """The tests for the Ring platform."""
import asyncio
from base64 import standard_b64encode, urlsafe_b64decode
import pytest
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_der_private_key
from http_ece import encrypt
from firebase_messaging import FcmPushClient, FcmRegisterConfig
from firebase_messaging.proto.mcs_pb2 import (
Close,
DataMessageStanza,
HeartbeatAck,
HeartbeatPing,
IqStanza,
LoginRequest,
)
from tests.conftest import load_fixture_as_dict, load_fixture_as_msg
async def test_register():
pr = FcmPushClient(
None, FcmRegisterConfig("project-1234", "bar", "foobar", "foobar"), None
)
await pr.checkin_or_register()
async def test_login(logged_in_push_client, fake_mcs_endpoint, mocker, caplog):
await logged_in_push_client(None, None)
await asyncio.sleep(0.1)
assert (
len([record for record in caplog.records if record.levelname == "ERROR"]) == 0
)
assert "Succesfully logged in to MCS endpoint" in [
record.message for record in caplog.records if record.levelname == "INFO"
]
async def test_data_message_receive(
logged_in_push_client, fake_mcs_endpoint, mocker, caplog
):
notification = None
persistent_id = None
callback_obj = None
cb_loop = None
def on_msg(ntf, psid, obj=None):
nonlocal notification
nonlocal persistent_id
nonlocal callback_obj
nonlocal cb_loop
notification = ntf
persistent_id = psid
callback_obj = obj
cb_loop = asyncio.get_running_loop()
credentials = load_fixture_as_dict("credentials.json")
obj = "Foobar"
await logged_in_push_client(on_msg, credentials, callback_obj=obj)
dms = load_fixture_as_msg("data_message_stanza.json", DataMessageStanza)
await fake_mcs_endpoint.put_message(dms)
msg = await fake_mcs_endpoint.get_message()
assert isinstance(msg, IqStanza)
await asyncio.sleep(0.1)
assert (
len([record for record in caplog.records if record.levelname == "ERROR"]) == 0
)
assert notification == {"foo": "bar"}
assert persistent_id == dms.persistent_id
assert obj == callback_obj
async def test_connection_reset(logged_in_push_client, fake_mcs_endpoint, mocker):
# ConnectionResetError, TimeoutError, SSLError
pr = await logged_in_push_client(
None, None, abort_on_sequential_error_count=3, reset_interval=0.1
)
reset_spy = mocker.spy(pr, "_reset")
await fake_mcs_endpoint.put_error(ConnectionResetError())
await asyncio.sleep(0.1)
assert reset_spy.call_count == 1
msg = await fake_mcs_endpoint.get_message()
assert isinstance(msg, LoginRequest)
@pytest.mark.parametrize("error_count", [1, 2, 3, 6])
async def test_terminate(
logged_in_push_client, fake_mcs_endpoint, mocker, error_count, caplog
):
pr = await logged_in_push_client(
None, None, abort_on_sequential_error_count=error_count, reset_interval=0
)
reset_spy = mocker.spy(pr, "_reset")
term_spy = mocker.spy(pr, "_terminate")
for i in range(1, error_count + 1):
await fake_mcs_endpoint.put_error(ConnectionResetError())
await asyncio.sleep(0.1)
# client should reset while it gets errors < abort_on_sequential_error_count then it should terminate
if i < error_count:
assert reset_spy.call_count == i
assert term_spy.call_count == 0
msg = await fake_mcs_endpoint.get_message()
assert isinstance(msg, LoginRequest)
else:
assert reset_spy.call_count == i - 1
assert term_spy.call_count == 1
async def test_heartbeat_receive(logged_in_push_client, fake_mcs_endpoint, caplog):
await logged_in_push_client(None, None)
ping = load_fixture_as_msg("heartbeat_ping.json", HeartbeatPing)
await fake_mcs_endpoint.put_message(ping)
msg = await fake_mcs_endpoint.get_message()
assert isinstance(msg, HeartbeatAck)
assert (
len([record for record in caplog.records if record.levelname == "ERROR"]) == 0
)
async def test_heartbeat_send(logged_in_push_client, fake_mcs_endpoint, mocker, caplog):
pr: FcmPushClient = await logged_in_push_client(None, None)
ack = load_fixture_as_msg("heartbeat_ack.json", HeartbeatAck)
await pr._send_heartbeat()
ping_msg = await fake_mcs_endpoint.get_message()
await fake_mcs_endpoint.put_message(ack)
await asyncio.sleep(0.1)
assert isinstance(ping_msg, HeartbeatPing)
assert (
len(
[
record.message
for record in caplog.records
if record.levelname == "DEBUG"
and "Received heartbeat ack" in record.message
]
)
== 1
)
async def test_decrypt():
def get_app_data_by_key(msg, key):
for x in msg.app_data:
if x.key == key:
return x.value
def set_app_data_by_key(msg, key, value):
for x in msg.app_data:
if x.key == key:
x.value = value
dms = load_fixture_as_msg("data_message_stanza.json", DataMessageStanza)
credentials = load_fixture_as_dict("credentials.json")
raw_data = b'{ "foo" : "bar" }'
salt_str = get_app_data_by_key(dms, "encryption")[5:]
salt = urlsafe_b64decode(salt_str.encode("ascii"))
# Random key pair
sender_pub = "BAGEFtID7WlmwzQ9pbjdRYAhfPe7Z8lA3ZGIPUh0SE3ikoY2PIrWUP0rmhpE4Kl8ImgMUDjKWrz0WmtLxORIHuw"
sender_pri_der = urlsafe_b64decode(
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwSUpDfIqdJG3XVkn7t1GExHuW3gsqD4-J525w-rnCIihRANCAAQBhBbSA-1pZsM0PaW43UWAIXz3u2fJQN2RiD1IdEhN4pKGNjyK1lD9K5oaROCpfCJoDFA4ylq89FprS8TkSB7s".encode(
"ascii"
)
+ b"========"
)
sender_privkey = load_der_private_key(
sender_pri_der, password=None, backend=default_backend()
)
sender_sec = urlsafe_b64decode(
credentials["keys"]["secret"].encode("ascii") + b"========"
)
receiver_pub_key = urlsafe_b64decode(
credentials["keys"]["public"].encode("ascii") + b"="
)
raw_data_encrypted = encrypt(
raw_data,
salt=salt,
private_key=sender_privkey,
dh=receiver_pub_key,
version="aesgcm",
auth_secret=sender_sec,
)
set_app_data_by_key(dms, "crypto-key", "dh=" + sender_pub)
raw_data_decrypted = FcmPushClient._decrypt_raw_data(
credentials, sender_pub + "=", salt_str, raw_data_encrypted
)
assert raw_data_decrypted == raw_data
firebase-messaging-0.4.4/uv.lock 0000664 0000000 0000000 00000654561 14675551151 0016611 0 ustar 00root root 0000000 0000000 version = 1
requires-python = ">=3.9.0"
[[package]]
name = "aiohappyeyeballs"
version = "2.4.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/d9/e710a5c9e51b4d5a977c823ce323a81d344da8c1b6fba16bb270a8be800d/aiohappyeyeballs-2.4.2.tar.gz", hash = "sha256:4ca893e6c5c1f5bf3888b04cb5a3bee24995398efef6e0b9f747b5e89d84fd74", size = 18391 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/13/64/40165ff77ade5203284e3015cf88e11acb07d451f6bf83fff71192912a0d/aiohappyeyeballs-2.4.2-py3-none-any.whl", hash = "sha256:8522691d9a154ba1145b157d6d5c15e5c692527ce6a53c5e5f9876977f6dab2f", size = 14105 },
]
[[package]]
name = "aiohttp"
version = "3.10.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohappyeyeballs" },
{ name = "aiosignal" },
{ name = "async-timeout", marker = "python_full_version < '3.11'" },
{ name = "attrs" },
{ name = "frozenlist" },
{ name = "multidict" },
{ name = "yarl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2b/97/15c51bbfcc184bcb4d473b7b02e7b54b6978e0083556a9cd491875cf11f7/aiohttp-3.10.6.tar.gz", hash = "sha256:d2578ef941be0c2ba58f6f421a703527d08427237ed45ecb091fed6f83305336", size = 7538429 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/00/a5/20d5b4cc1dbf5292434ad968af698c3e25b149394060578c4e83edfe5c56/aiohttp-3.10.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:682836fc672972cc3101cc9e30d49c5f7e8f1d010478d46119fe725a4545acfd", size = 586587 },
{ url = "https://files.pythonhosted.org/packages/d6/77/bcb8f5e382d800673c6457cab3cb24143ae30578682687d334a556fe4021/aiohttp-3.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:289fa8a20018d0d5aa9e4b35d899bd51bcb80f0d5f365d9a23e30dac3b79159b", size = 398987 },
{ url = "https://files.pythonhosted.org/packages/19/b1/874ca8a6fd581303f1f99efc71aff034e1b955702d54b96a61d97f18387f/aiohttp-3.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8617c96a20dd57e7e9d398ff9d04f3d11c4d28b1767273a5b1a018ada5a654d3", size = 390350 },
{ url = "https://files.pythonhosted.org/packages/41/2b/18f60f36d3b0d6b4f9680b00e129058086984af33ab01743d8f8d662ae43/aiohttp-3.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdbeff1b062751c2a2a55b171f7050fb7073633c699299d042e962aacdbe1a07", size = 1228449 },
{ url = "https://files.pythonhosted.org/packages/d5/77/801a07f67a6ccfc2d6e8c372e196b6019f33d637e6a3abf84f876983dbfd/aiohttp-3.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ea35d849cdd4a9268f910bff4497baebbc1aa3f2f625fd8ccd9ac99c860c621", size = 1264246 },
{ url = "https://files.pythonhosted.org/packages/bd/ff/984219306cbc1fedd689e9cfe7894d0cb2ae0038f2d7079e2788a79383ee/aiohttp-3.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473961b3252f3b949bb84873d6e268fb6d8aa0ccc6eb7404fa58c76a326bb8e1", size = 1298031 },
{ url = "https://files.pythonhosted.org/packages/25/34/96445dc2db0ff0e7ffe71abb73e298a62c2724b774470d5b232ed8ee89ad/aiohttp-3.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d2665c5df629eb2f981dab244c01bfa6cdc185f4ffa026639286c4d56fafb54", size = 1221827 },
{ url = "https://files.pythonhosted.org/packages/6d/59/52170050e3e83e476321cce2232c456d55ecf0b67faf9a31b73328d7b65f/aiohttp-3.10.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25d92f794f1332f656e3765841fc2b7ad5c26c3f3d01e8949eeb3495691cf9f4", size = 1193436 },
{ url = "https://files.pythonhosted.org/packages/4f/ad/cb020bdb02e41ed4cf0f9a1031c67424d9dd1b1dd3e5fd98053ed3b4f72f/aiohttp-3.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9bd6b2033993d5ae80883bb29b83fb2b432270bbe067c2f53cc73bb57c46065f", size = 1193175 },
{ url = "https://files.pythonhosted.org/packages/59/d3/58cf6e9c81064d07173ee0e31743fa18212e3c76d1041a30164e91e91e7d/aiohttp-3.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d7f408c43f5e75ea1edc152fb375e8f46ef916f545fb66d4aebcbcfad05e2796", size = 1192674 },
{ url = "https://files.pythonhosted.org/packages/b2/1f/c203e3ff4636885f8d47228a717f248b7acd5761a9fb57650f8ad393cb1a/aiohttp-3.10.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:cf8b8560aa965f87bf9c13bf9fed7025993a155ca0ce8422da74bf46d18c2f5f", size = 1246831 },
{ url = "https://files.pythonhosted.org/packages/e2/e4/8534f620113c9922d911271678f6f053192627035bfa7b0b62c9baa48908/aiohttp-3.10.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14477c4e52e2f17437b99893fd220ffe7d7ee41df5ebf931a92b8ca82e6fd094", size = 1263732 },
{ url = "https://files.pythonhosted.org/packages/3c/99/d5ada3481146b42312a83b16304b001125df8e8e7751c71a0f26cf4fb38f/aiohttp-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb138fbf9f53928e779650f5ed26d0ea1ed8b2cab67f0ea5d63afa09fdc07593", size = 1215377 },
{ url = "https://files.pythonhosted.org/packages/82/2d/a5afdfb5c37a7dbb4468ff39a4581a6290b2ced18fb5513981a9154c91c1/aiohttp-3.10.6-cp310-cp310-win32.whl", hash = "sha256:9843d683b8756971797be171ead21511d2215a2d6e3c899c6e3107fbbe826791", size = 362309 },
{ url = "https://files.pythonhosted.org/packages/49/2e/d06c4bf365685ba0ea501e5bb5b4a4b0c3f90236f8a38ee0083c56624847/aiohttp-3.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:f8b8e49fe02f744d38352daca1dbef462c3874900bd8166516f6ea8e82b5aacf", size = 380724 },
{ url = "https://files.pythonhosted.org/packages/d5/f0/6c921693dd264db370916dab69cc3267ca4bb14296b4ca88b3855f6152cd/aiohttp-3.10.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f52e54fd776ad0da1006708762213b079b154644db54bcfc62f06eaa5b896402", size = 586118 },
{ url = "https://files.pythonhosted.org/packages/df/14/12804459bd128ff3c7d60dadaf49be9e7027df5c8800290518113c411d00/aiohttp-3.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:995ab1a238fd0d19dc65f2d222e5eb064e409665c6426a3e51d5101c1979ee84", size = 398614 },
{ url = "https://files.pythonhosted.org/packages/49/2c/066640d3c3dd52d4c11dfcff64c6eebe4a7607571bc9cb8ae2c2b4013367/aiohttp-3.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0749c4d5a08a802dd66ecdf59b2df4d76b900004017468a7bb736c3b5a3dd902", size = 390262 },
{ url = "https://files.pythonhosted.org/packages/71/50/6db8a9ba23ee4d5621ec2a59427c271cc1ddaf4fc1a9c02c9dcba1ebe671/aiohttp-3.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e05b39158f2af0e2438cc2075cfc271f4ace0c3cc4a81ec95b27a0432e161951", size = 1306113 },
{ url = "https://files.pythonhosted.org/packages/51/fd/a923745abb24657264b2122c24a296468cf8c16ba68b7b569060d6c32620/aiohttp-3.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9f196c970db2dcde4f24317e06615363349dc357cf4d7a3b0716c20ac6d7bcd", size = 1344288 },
{ url = "https://files.pythonhosted.org/packages/f8/7a/5f1397305aa5885a35dce0b10681aa547537348a18d107d96a07e99bebb8/aiohttp-3.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:47647c8af04a70e07a2462931b0eba63146a13affa697afb4ecbab9d03a480ce", size = 1378118 },
{ url = "https://files.pythonhosted.org/packages/fd/80/4f1c4b5459a27437a8f18f91d6000fdc45b677aee879129deaadc94c1a23/aiohttp-3.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c0efe7e99f6d94d63274c06344bd0e9c8daf184ce5602a29bc39e00a18720", size = 1292337 },
{ url = "https://files.pythonhosted.org/packages/e1/57/cef69e70f18271f86080a3d28571598baf0dccb2fc726fbd74b91a56d51a/aiohttp-3.10.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9721cdd83a994225352ca84cd537760d41a9da3c0eacb3ff534747ab8fba6d0", size = 1251407 },
{ url = "https://files.pythonhosted.org/packages/b0/dd/8b718a8ecb271d484c6d43f4ae3d63e684c259367c8c2cda861f1bf12cfd/aiohttp-3.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b82c8ebed66ce182893e7c0b6b60ba2ace45b1df104feb52380edae266a4850", size = 1271350 },
{ url = "https://files.pythonhosted.org/packages/f8/37/c80d05752ecbe7419ec61d39facff8d77914e295c8d45eb250d1fa03ae78/aiohttp-3.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b169f8e755e541b72e714b89a831b315bbe70db44e33fead28516c9e13d5f931", size = 1265888 },
{ url = "https://files.pythonhosted.org/packages/4f/44/9862295fabcadcf7d79e9a92eb8528866d602042571c43c333d94c7f3025/aiohttp-3.10.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0be3115753baf8b4153e64f9aa7bf6c0c64af57979aa900c31f496301b374570", size = 1321251 },
{ url = "https://files.pythonhosted.org/packages/57/62/5b92e910aa95c2558b418eb68f0d117aab968cdd15019c06ea1c66d0baf2/aiohttp-3.10.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e1f80cd17d81a404b6e70ef22bfe1870bafc511728397634ad5f5efc8698df56", size = 1338856 },
{ url = "https://files.pythonhosted.org/packages/7e/e6/bb013958e9fcfb982d8dba12b0c72621427619cd0a11bb3023601c205988/aiohttp-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6419728b08fb6380c66a470d2319cafcec554c81780e2114b7e150329b9a9a7f", size = 1298691 },
{ url = "https://files.pythonhosted.org/packages/f7/c7/cc2dc01f89d8a0ee2d84d8d0c85b48ec62a427bcd865736f9ceb340c0117/aiohttp-3.10.6-cp311-cp311-win32.whl", hash = "sha256:bd294dcdc1afdc510bb51d35444003f14e327572877d016d576ac3b9a5888a27", size = 361858 },
{ url = "https://files.pythonhosted.org/packages/9f/2a/60284a07a0353250cf64db9728980a3bb9a55eeea334d79c48e65801460a/aiohttp-3.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:bf861da9a43d282d6dd9dcd64c23a0fccf2c5aa5cd7c32024513c8c79fb69de3", size = 381204 },
{ url = "https://files.pythonhosted.org/packages/41/6b/0db03d1105e5e8564fd39a87729fd910300a8021b2c59f6f57ed963fe896/aiohttp-3.10.6-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2708baccdc62f4b1251e59c2aac725936a900081f079b88843dabcab0feeeb27", size = 583162 },
{ url = "https://files.pythonhosted.org/packages/d0/9e/c44dddee462c38853a0c32b50c4deed09790d27496ab9eb3b481614344a5/aiohttp-3.10.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7475da7a5e2ccf1a1c86c8fee241e277f4874c96564d06f726d8df8e77683ef7", size = 395317 },
{ url = "https://files.pythonhosted.org/packages/25/0e/c0dfb1604645ab64e2b1210e624f951a024a2e9683feb563bbf979874220/aiohttp-3.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02108326574ff60267b7b35b17ac5c0bbd0008ccb942ce4c48b657bb90f0b8aa", size = 390533 },
{ url = "https://files.pythonhosted.org/packages/5c/50/8c3eba14ce77fd78f1def3788cbc75b54291dd4d8f5647d721316437f5da/aiohttp-3.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:029a019627b37fa9eac5c75cc54a6bb722c4ebbf5a54d8c8c0fb4dd8facf2702", size = 1311699 },
{ url = "https://files.pythonhosted.org/packages/bd/02/d0f12cfc7ade482d81c6d2c4c5f2f98964d6305560b7df0b7712212241ca/aiohttp-3.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a637d387db6fdad95e293fab5433b775fd104ae6348d2388beaaa60d08b38c4", size = 1350180 },
{ url = "https://files.pythonhosted.org/packages/31/2b/f78ff8d84e700a279434dd371ae6e87e12a13f9ed2a5efe9cd6aacd749d4/aiohttp-3.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1a16f3fc1944c61290d33c88dc3f09ba62d159b284c38c5331868425aca426", size = 1392281 },
{ url = "https://files.pythonhosted.org/packages/a5/f8/a8722a471cbf19e56763545fd5bc0fdf7b61324535f0b35bd6f0548d4016/aiohttp-3.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b292f37969f9cc54f4643f0be7dacabf3612b3b4a65413661cf6c350226787", size = 1305932 },
{ url = "https://files.pythonhosted.org/packages/38/a5/897caff83bfe41fd749056b11282504772b34c2dfe730aaf8e84bbd3a660/aiohttp-3.10.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0754690a3a26e819173a34093798c155bafb21c3c640bff13be1afa1e9d421f9", size = 1259884 },
{ url = "https://files.pythonhosted.org/packages/e1/75/effbadbf5c9a536f90769544467da311efd6e8c43671bc0729055c59d363/aiohttp-3.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:164ecd32e65467d86843dbb121a6666c3deb23b460e3f8aefdcaacae79eb718a", size = 1270764 },
{ url = "https://files.pythonhosted.org/packages/33/34/33e07d1bc34406bfc0877f22eed071060796431488c8eb6d456c583a74a9/aiohttp-3.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438c5863feb761f7ca3270d48c292c334814459f61cc12bab5ba5b702d7c9e56", size = 1279882 },
{ url = "https://files.pythonhosted.org/packages/2e/e4/ffed46ce0b45564cbf715b0b97725840468c7c5a9d6e8d560082c29ad4bf/aiohttp-3.10.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ba18573bb1de1063d222f41de64a0d3741223982dcea863b3f74646faf618ec7", size = 1316432 },
{ url = "https://files.pythonhosted.org/packages/ce/00/488d68568f60aa5dbf9d41ef60d276ffbafeab553bf79b00225de7133e0b/aiohttp-3.10.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c82a94ddec996413a905f622f3da02c4359952aab8d817c01cf9915419525e95", size = 1343562 },
{ url = "https://files.pythonhosted.org/packages/14/3e/3679c1438fcb0aadddff32e97b3b88b1c8aea80276d374ec543a5ed70d0d/aiohttp-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92351aa5363fc3c1f872ca763f86730ced32b01607f0c9662b1fa711087968d0", size = 1305803 },
{ url = "https://files.pythonhosted.org/packages/76/48/fe117dffa13c69d9670e107cbf3dea20be9f7fc5d30d2fd3fd6252f28c58/aiohttp-3.10.6-cp312-cp312-win32.whl", hash = "sha256:3e15e33bfc73fa97c228f72e05e8795e163a693fd5323549f49367c76a6e5883", size = 358921 },
{ url = "https://files.pythonhosted.org/packages/92/9f/7281a6dae91c9cc3f23dfb865f074151810216f31bdb46843bfde8e39f17/aiohttp-3.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:fe517113fe4d35d9072b826c3e147d63c5f808ca8167d450b4f96c520c8a1d8d", size = 378938 },
{ url = "https://files.pythonhosted.org/packages/12/7f/89eb922fda25d5b9c7c08d14d50c788d998f148210478059b7549040424a/aiohttp-3.10.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:482f74057ea13d387a7549d7a7ecb60e45146d15f3e58a2d93a0ad2d5a8457cd", size = 575722 },
{ url = "https://files.pythonhosted.org/packages/84/6d/eb3965c55748f960751b752969983982a995d2aa21f023ed30fe5a471629/aiohttp-3.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:03fa40d1450ee5196e843315ddf74a51afc7e83d489dbfc380eecefea74158b1", size = 391518 },
{ url = "https://files.pythonhosted.org/packages/78/4a/98c9d9cee601477eda8f851376eff88e864e9f3147cbc3a428da47d90ed0/aiohttp-3.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e52e59ed5f4cc3a3acfe2a610f8891f216f486de54d95d6600a2c9ba1581f4d", size = 387037 },
{ url = "https://files.pythonhosted.org/packages/2d/b0/6136aefae0f0d2abe4a435af71a944781e37bbe6fd836a23ff41bbba0682/aiohttp-3.10.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b3935a22c9e41a8000d90588bed96cf395ef572dbb409be44c6219c61d900d", size = 1286703 },
{ url = "https://files.pythonhosted.org/packages/cd/8a/a17ec94a7b6394efeeaca16df8d1e9359f0aa83548e40bf16b5853ed7684/aiohttp-3.10.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bef1480ee50f75abcfcb4b11c12de1005968ca9d0172aec4a5057ba9f2b644f", size = 1323244 },
{ url = "https://files.pythonhosted.org/packages/35/37/4cf6d2a8dce91ea7ff8b8ed8e1ef5c6a5934e07b4da5993ae95660b7cfbc/aiohttp-3.10.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:671745ea7db19693ce867359d503772177f0b20fa8f6ee1e74e00449f4c4151d", size = 1368034 },
{ url = "https://files.pythonhosted.org/packages/0d/d5/e939fcf26bd5c7760a1b71eff7396f6ca0e3c807088086551db28af0c090/aiohttp-3.10.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b50b367308ca8c12e0b50cba5773bc9abe64c428d3fd2bbf5cd25aab37c77bf", size = 1282395 },
{ url = "https://files.pythonhosted.org/packages/46/44/85d5d61b3ac50f30766cd2c1d22e6f937f027922621fc91581ead05749f6/aiohttp-3.10.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a504d7cdb431a777d05a124fd0b21efb94498efa743103ea01b1e3136d2e4fb", size = 1236147 },
{ url = "https://files.pythonhosted.org/packages/61/35/43eee26590f369906151cea78297554304ed2ceda5a5ed69cc2e907e9903/aiohttp-3.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66bc81361131763660b969132a22edce2c4d184978ba39614e8f8f95db5c95f8", size = 1249963 },
{ url = "https://files.pythonhosted.org/packages/44/b5/e099ad2bf7ad6ab5bb685f66a7599dc7f9fb4879eb987a4bf02ca2886974/aiohttp-3.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:27cf19a38506e2e9f12fc17e55f118f04897b0a78537055d93a9de4bf3022e3d", size = 1248579 },
{ url = "https://files.pythonhosted.org/packages/85/81/520348e8ec472679e65deb87c2a2bb2ad2c40e328746245bd35251b7ee4f/aiohttp-3.10.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3468b39f977a11271517c6925b226720e148311039a380cc9117b1e2258a721f", size = 1293005 },
{ url = "https://files.pythonhosted.org/packages/e5/a8/1ddd2af786c3b4f30187bc98464b8e3c54c6bbf18062a20291c6b5b03f27/aiohttp-3.10.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9d26da22a793dfd424be1050712a70c0afd96345245c29aced1e35dbace03413", size = 1319740 },
{ url = "https://files.pythonhosted.org/packages/0a/6f/a757fdf01ce4d20fcfee35af3b63a2393dbd3478873c4ea9aaad24b093f1/aiohttp-3.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:844d48ff9173d0b941abed8b2ea6a412f82b56d9ab1edb918c74000c15839362", size = 1281177 },
{ url = "https://files.pythonhosted.org/packages/9d/d9/e5866f341cfad4de82caf570218a424f96914192a9230dd6f6dfe4653a93/aiohttp-3.10.6-cp313-cp313-win32.whl", hash = "sha256:2dd56e3c43660ed3bea67fd4c5025f1ac1f9ecf6f0b991a6e5efe2e678c490c5", size = 357148 },
{ url = "https://files.pythonhosted.org/packages/57/cc/ba781a170fd4405819cc988026cfa16a9397ffebf5639dc84ad65d518448/aiohttp-3.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:c91781d969fbced1993537f45efe1213bd6fccb4b37bfae2a026e20d6fbed206", size = 376413 },
{ url = "https://files.pythonhosted.org/packages/e3/8c/09c36451df52c753e46be8a1d9533d61d19acdced8424e06575d41285e24/aiohttp-3.10.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5db26bbca8e7968c4c977a0c640e0b9ce7224e1f4dcafa57870dc6ee28e27de6", size = 588214 },
{ url = "https://files.pythonhosted.org/packages/2c/90/2e5f130dbac00f615c99a041fbd0734f40d68f94f32e7e5bc74ac148e228/aiohttp-3.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3fb4216e3ec0dbc01db5ba802f02ed78ad8f07121be54eb9e918448cc3f61b7c", size = 399874 },
{ url = "https://files.pythonhosted.org/packages/d3/de/b60c688d89c357b23084facc1602a23dcfac812d50175bdd6b0d941e8e08/aiohttp-3.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a976ef488f26e224079deb3d424f29144c6d5ba4ded313198169a8af8f47fb82", size = 391201 },
{ url = "https://files.pythonhosted.org/packages/00/b8/a3559410e6fa6e96eec4623a517c84e6774576a276dad5380e1720871760/aiohttp-3.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a86610174de8a85a920e956e2d4f9945e7da89f29a00e95ac62a4a414c4ef4e", size = 1233301 },
{ url = "https://files.pythonhosted.org/packages/72/cd/62c6c417bc5e49087ae7ae9f66f64c10df6601ead4d2646f5a4a7630ac30/aiohttp-3.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:217791c6a399cc4f2e6577bb44344cba1f5714a2aebf6a0bea04cfa956658284", size = 1270683 },
{ url = "https://files.pythonhosted.org/packages/57/67/e6dc17dbdefb459ec3e5b6e8b3332f0e11683fac6fa7ac4b74335a9edc7a/aiohttp-3.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba3662d41abe2eab0eeec7ee56f33ef4e0b34858f38abf24377687f9e1fb00a5", size = 1304500 },
{ url = "https://files.pythonhosted.org/packages/d0/d3/553e55b6adc881e44e9024d92f9dd70c538d59a19d6a58cb715f0838ce24/aiohttp-3.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4dfa5ad4bce9ca30a76117fbaa1c1decf41ebb6c18a4e098df44298941566f9", size = 1225049 },
{ url = "https://files.pythonhosted.org/packages/29/a1/f444b1c39039b9f020d02e871c5afd6e0eaeecf34bfcd47ef8f82408c1bf/aiohttp-3.10.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0009258e97502936d3bd5bf2ced15769629097d0abb81e6495fba1047824fe0", size = 1196214 },
{ url = "https://files.pythonhosted.org/packages/3b/50/bab30b4bbe1ef7d66d97358129c34379039c69b2b528ff02804a42b0b4da/aiohttp-3.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0a75d5c9fb4f06c41d029ae70ad943c3a844c40c0a769d12be4b99b04f473d3d", size = 1196350 },
{ url = "https://files.pythonhosted.org/packages/9b/8a/3731748b1080b2195268c85010727406ac3cee2fa878318d46de5614372f/aiohttp-3.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8198b7c002aae2b40b2d16bfe724b9a90bcbc9b78b2566fc96131ef4e382574d", size = 1196837 },
{ url = "https://files.pythonhosted.org/packages/7c/3c/51f9dbdabcdacf54b9e9ec1af210509e1a7e262647a106f720ed683b35ee/aiohttp-3.10.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4611db8c907f90fe86be112efdc2398cd7b4c8eeded5a4f0314b70fdea8feab0", size = 1250939 },
{ url = "https://files.pythonhosted.org/packages/fd/51/e4fde27da37a28c5bdef08d9393115f062c2e198f720172b12bb53b6c4d4/aiohttp-3.10.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ff99ae06eef85c7a565854826114ced72765832ee16c7e3e766c5e4c5b98d20e", size = 1265712 },
{ url = "https://files.pythonhosted.org/packages/1c/0d/b9c0d5ad9dc99cdfba665ba9ac6bd7aa9b97c866aef180477fabc54eaa56/aiohttp-3.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7641920bdcc7cd2d3ddfb8bb9133a6c9536b09dbd49490b79e125180b2d25b93", size = 1216963 },
{ url = "https://files.pythonhosted.org/packages/83/3e/c9ad8da2750ad9014a3d00be4809d8b96991ebdc4903ab78c2793362e192/aiohttp-3.10.6-cp39-cp39-win32.whl", hash = "sha256:e2e7d5591ea868d5ec82b90bbeb366a198715672841d46281b623e23079593db", size = 362896 },
{ url = "https://files.pythonhosted.org/packages/dc/b9/f952f6b156d01a04b6b110ba01f5fed975afdcfaca72ed4d07db964930ce/aiohttp-3.10.6-cp39-cp39-win_amd64.whl", hash = "sha256:b504c08c45623bf5c7ca41be380156d925f00199b3970efd758aef4a77645feb", size = 381395 },
]
[[package]]
name = "aioresponses"
version = "0.7.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ff/63/bb78ed078e2d514050aadc42a932465a83c43c628746f0e788500ec0bf5d/aioresponses-0.7.6.tar.gz", hash = "sha256:f795d9dbda2d61774840e7e32f5366f45752d1adc1b74c9362afd017296c7ee1", size = 35846 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/1e/c259a960a4dff46840133ce19682ef14db2922da80a6088283ec9c3f647a/aioresponses-0.7.6-py2.py3-none-any.whl", hash = "sha256:d2c26defbb9b440ea2685ec132e90700907fd10bcca3e85ec2f157219f0d26f7", size = 11875 },
]
[[package]]
name = "aiosignal"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "frozenlist" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ae/67/0952ed97a9793b4958e5736f6d2b346b414a2cd63e82d05940032f45b32f/aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", size = 19422 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/ac/a7305707cb852b7e16ff80eaf5692309bde30e2b1100a1fcacdc8f731d97/aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17", size = 7617 },
]
[[package]]
name = "alabaster"
version = "0.7.16"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511 },
]
[[package]]
name = "async-timeout"
version = "4.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/87/d6/21b30a550dafea84b1b8eee21b5e23fa16d010ae006011221f33dcd8d7f8/async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", size = 8345 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028", size = 5721 },
]
[[package]]
name = "attrs"
version = "24.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 },
]
[[package]]
name = "babel"
version = "2.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 },
]
[[package]]
name = "certifi"
version = "2024.8.30"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 },
]
[[package]]
name = "cffi"
version = "1.17.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 },
{ url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 },
{ url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 },
{ url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 },
{ url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 },
{ url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 },
{ url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 },
{ url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 },
{ url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 },
{ url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 },
{ url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 },
{ url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 },
{ url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 },
{ url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 },
{ url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 },
{ url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 },
{ url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 },
{ url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 },
{ url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 },
{ url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 },
{ url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 },
{ url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 },
{ url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 },
{ url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 },
{ url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 },
{ url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 },
{ url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 },
{ url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 },
{ url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 },
{ url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 },
{ url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 },
{ url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 },
{ url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 },
{ url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 },
{ url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 },
{ url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 },
{ url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 },
{ url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 },
{ url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 },
{ url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 },
{ url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 },
{ url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
{ url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
{ url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
{ url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 },
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
{ url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220 },
{ url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605 },
{ url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910 },
{ url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200 },
{ url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565 },
{ url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635 },
{ url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218 },
{ url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486 },
{ url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911 },
{ url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632 },
{ url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820 },
{ url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290 },
]
[[package]]
name = "cfgv"
version = "3.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219 },
{ url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521 },
{ url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383 },
{ url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223 },
{ url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101 },
{ url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699 },
{ url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065 },
{ url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505 },
{ url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425 },
{ url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287 },
{ url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929 },
{ url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605 },
{ url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646 },
{ url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846 },
{ url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343 },
{ url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
{ url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
{ url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
{ url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
{ url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
{ url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
{ url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
{ url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
{ url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
{ url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
{ url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
{ url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
{ url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
{ url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
{ url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
{ url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
{ url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
{ url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
{ url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
{ url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
{ url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
{ url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
{ url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
{ url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
{ url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
{ url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
{ url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
{ url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
{ url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
{ url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
{ url = "https://files.pythonhosted.org/packages/f7/9d/bcf4a449a438ed6f19790eee543a86a740c77508fbc5ddab210ab3ba3a9a/charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", size = 194198 },
{ url = "https://files.pythonhosted.org/packages/66/fe/c7d3da40a66a6bf2920cce0f436fa1f62ee28aaf92f412f0bf3b84c8ad6c/charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", size = 122494 },
{ url = "https://files.pythonhosted.org/packages/2a/9d/a6d15bd1e3e2914af5955c8eb15f4071997e7078419328fee93dfd497eb7/charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", size = 120393 },
{ url = "https://files.pythonhosted.org/packages/3d/85/5b7416b349609d20611a64718bed383b9251b5a601044550f0c8983b8900/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", size = 138331 },
{ url = "https://files.pythonhosted.org/packages/79/66/8946baa705c588521afe10b2d7967300e49380ded089a62d38537264aece/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", size = 148097 },
{ url = "https://files.pythonhosted.org/packages/44/80/b339237b4ce635b4af1c73742459eee5f97201bd92b2371c53e11958392e/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", size = 140711 },
{ url = "https://files.pythonhosted.org/packages/98/69/5d8751b4b670d623aa7a47bef061d69c279e9f922f6705147983aa76c3ce/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", size = 142251 },
{ url = "https://files.pythonhosted.org/packages/1f/8d/33c860a7032da5b93382cbe2873261f81467e7b37f4ed91e25fed62fd49b/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", size = 144636 },
{ url = "https://files.pythonhosted.org/packages/c2/65/52aaf47b3dd616c11a19b1052ce7fa6321250a7a0b975f48d8c366733b9f/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", size = 139514 },
{ url = "https://files.pythonhosted.org/packages/51/fd/0ee5b1c2860bb3c60236d05b6e4ac240cf702b67471138571dad91bcfed8/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", size = 145528 },
{ url = "https://files.pythonhosted.org/packages/e1/9c/60729bf15dc82e3aaf5f71e81686e42e50715a1399770bcde1a9e43d09db/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", size = 149804 },
{ url = "https://files.pythonhosted.org/packages/53/cd/aa4b8a4d82eeceb872f83237b2d27e43e637cac9ffaef19a1321c3bafb67/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", size = 141708 },
{ url = "https://files.pythonhosted.org/packages/54/7f/cad0b328759630814fcf9d804bfabaf47776816ad4ef2e9938b7e1123d04/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561", size = 142708 },
{ url = "https://files.pythonhosted.org/packages/c1/9d/254a2f1bcb0ce9acad838e94ed05ba71a7cb1e27affaa4d9e1ca3958cdb6/charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", size = 92830 },
{ url = "https://files.pythonhosted.org/packages/2f/0e/d7303ccae9735ff8ff01e36705ad6233ad2002962e8668a970fc000c5e1b/charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", size = 100376 },
{ url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "coverage"
version = "7.6.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/61/eb7ce5ed62bacf21beca4937a90fe32545c91a3c8a42a30c6616d48fc70d/coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", size = 206690 },
{ url = "https://files.pythonhosted.org/packages/7d/73/041928e434442bd3afde5584bdc3f932fb4562b1597629f537387cec6f3d/coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", size = 207127 },
{ url = "https://files.pythonhosted.org/packages/c7/c8/6ca52b5147828e45ad0242388477fdb90df2c6cbb9a441701a12b3c71bc8/coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", size = 235654 },
{ url = "https://files.pythonhosted.org/packages/d5/da/9ac2b62557f4340270942011d6efeab9833648380109e897d48ab7c1035d/coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc", size = 233598 },
{ url = "https://files.pythonhosted.org/packages/53/23/9e2c114d0178abc42b6d8d5281f651a8e6519abfa0ef460a00a91f80879d/coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", size = 234732 },
{ url = "https://files.pythonhosted.org/packages/0f/7e/a0230756fb133343a52716e8b855045f13342b70e48e8ad41d8a0d60ab98/coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", size = 233816 },
{ url = "https://files.pythonhosted.org/packages/28/7c/3753c8b40d232b1e5eeaed798c875537cf3cb183fb5041017c1fdb7ec14e/coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", size = 232325 },
{ url = "https://files.pythonhosted.org/packages/57/e3/818a2b2af5b7573b4b82cf3e9f137ab158c90ea750a8f053716a32f20f06/coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", size = 233418 },
{ url = "https://files.pythonhosted.org/packages/c8/fb/4532b0b0cefb3f06d201648715e03b0feb822907edab3935112b61b885e2/coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", size = 209343 },
{ url = "https://files.pythonhosted.org/packages/5a/25/af337cc7421eca1c187cc9c315f0a755d48e755d2853715bfe8c418a45fa/coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", size = 210136 },
{ url = "https://files.pythonhosted.org/packages/ad/5f/67af7d60d7e8ce61a4e2ddcd1bd5fb787180c8d0ae0fbd073f903b3dd95d/coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", size = 206796 },
{ url = "https://files.pythonhosted.org/packages/e1/0e/e52332389e057daa2e03be1fbfef25bb4d626b37d12ed42ae6281d0a274c/coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", size = 207244 },
{ url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279 },
{ url = "https://files.pythonhosted.org/packages/70/6c/a9ccd6fe50ddaf13442a1e2dd519ca805cbe0f1fcd377fba6d8339b98ccb/coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", size = 236859 },
{ url = "https://files.pythonhosted.org/packages/14/6f/8351b465febb4dbc1ca9929505202db909c5a635c6fdf33e089bbc3d7d85/coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", size = 238549 },
{ url = "https://files.pythonhosted.org/packages/68/3c/289b81fa18ad72138e6d78c4c11a82b5378a312c0e467e2f6b495c260907/coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", size = 237477 },
{ url = "https://files.pythonhosted.org/packages/ed/1c/aa1efa6459d822bd72c4abc0b9418cf268de3f60eeccd65dc4988553bd8d/coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", size = 236134 },
{ url = "https://files.pythonhosted.org/packages/fb/c8/521c698f2d2796565fe9c789c2ee1ccdae610b3aa20b9b2ef980cc253640/coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", size = 236910 },
{ url = "https://files.pythonhosted.org/packages/7d/30/033e663399ff17dca90d793ee8a2ea2890e7fdf085da58d82468b4220bf7/coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", size = 209348 },
{ url = "https://files.pythonhosted.org/packages/20/05/0d1ccbb52727ccdadaa3ff37e4d2dc1cd4d47f0c3df9eb58d9ec8508ca88/coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", size = 210230 },
{ url = "https://files.pythonhosted.org/packages/7e/d4/300fc921dff243cd518c7db3a4c614b7e4b2431b0d1145c1e274fd99bd70/coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", size = 206983 },
{ url = "https://files.pythonhosted.org/packages/e1/ab/6bf00de5327ecb8db205f9ae596885417a31535eeda6e7b99463108782e1/coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", size = 207221 },
{ url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342 },
{ url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371 },
{ url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455 },
{ url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924 },
{ url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252 },
{ url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897 },
{ url = "https://files.pythonhosted.org/packages/b6/e9/d9cc3deceb361c491b81005c668578b0dfa51eed02cd081620e9a62f24ec/coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", size = 209606 },
{ url = "https://files.pythonhosted.org/packages/47/c8/5a2e41922ea6740f77d555c4d47544acd7dc3f251fe14199c09c0f5958d3/coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", size = 210373 },
{ url = "https://files.pythonhosted.org/packages/8c/f9/9aa4dfb751cb01c949c990d136a0f92027fbcc5781c6e921df1cb1563f20/coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", size = 207007 },
{ url = "https://files.pythonhosted.org/packages/b9/67/e1413d5a8591622a46dd04ff80873b04c849268831ed5c304c16433e7e30/coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", size = 207269 },
{ url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886 },
{ url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037 },
{ url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038 },
{ url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690 },
{ url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765 },
{ url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611 },
{ url = "https://files.pythonhosted.org/packages/74/e4/7ff20d6a0b59eeaab40b3140a71e38cf52547ba21dbcf1d79c5a32bba61b/coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", size = 209671 },
{ url = "https://files.pythonhosted.org/packages/35/59/1812f08a85b57c9fdb6d0b383d779e47b6f643bc278ed682859512517e83/coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", size = 210368 },
{ url = "https://files.pythonhosted.org/packages/9c/15/08913be1c59d7562a3e39fce20661a98c0a3f59d5754312899acc6cb8a2d/coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", size = 207758 },
{ url = "https://files.pythonhosted.org/packages/c4/ae/b5d58dff26cade02ada6ca612a76447acd69dccdbb3a478e9e088eb3d4b9/coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", size = 208035 },
{ url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839 },
{ url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569 },
{ url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927 },
{ url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401 },
{ url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301 },
{ url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 },
{ url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 },
{ url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 },
{ url = "https://files.pythonhosted.org/packages/19/d3/d54c5aa83268779d54c86deb39c1c4566e5d45c155369ca152765f8db413/coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", size = 206688 },
{ url = "https://files.pythonhosted.org/packages/a5/fe/137d5dca72e4a258b1bc17bb04f2e0196898fe495843402ce826a7419fe3/coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", size = 207120 },
{ url = "https://files.pythonhosted.org/packages/78/5b/a0a796983f3201ff5485323b225d7c8b74ce30c11f456017e23d8e8d1945/coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", size = 235249 },
{ url = "https://files.pythonhosted.org/packages/4e/e1/76089d6a5ef9d68f018f65411fcdaaeb0141b504587b901d74e8587606ad/coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", size = 233237 },
{ url = "https://files.pythonhosted.org/packages/9a/6f/eef79b779a540326fee9520e5542a8b428cc3bfa8b7c8f1022c1ee4fc66c/coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", size = 234311 },
{ url = "https://files.pythonhosted.org/packages/75/e1/656d65fb126c29a494ef964005702b012f3498db1a30dd562958e85a4049/coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", size = 233453 },
{ url = "https://files.pythonhosted.org/packages/68/6a/45f108f137941a4a1238c85f28fd9d048cc46b5466d6b8dda3aba1bb9d4f/coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", size = 231958 },
{ url = "https://files.pythonhosted.org/packages/9b/e7/47b809099168b8b8c72ae311efc3e88c8d8a1162b3ba4b8da3cfcdb85743/coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", size = 232938 },
{ url = "https://files.pythonhosted.org/packages/52/80/052222ba7058071f905435bad0ba392cc12006380731c37afaf3fe749b88/coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", size = 209352 },
{ url = "https://files.pythonhosted.org/packages/b8/d8/1b92e0b3adcf384e98770a00ca095da1b5f7b483e6563ae4eb5e935d24a1/coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", size = 210153 },
{ url = "https://files.pythonhosted.org/packages/a5/2b/0354ed096bca64dc8e32a7cbcae28b34cb5ad0b1fe2125d6d99583313ac0/coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", size = 198926 },
]
[package.optional-dependencies]
toml = [
{ name = "tomli", marker = "python_full_version <= '3.11'" },
]
[[package]]
name = "cryptography"
version = "43.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/ba/0664727028b37e249e73879348cc46d45c5c1a2a2e81e8166462953c5755/cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d", size = 686927 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/28/b92c98a04ba762f8cdeb54eba5c4c84e63cac037a7c5e70117d337b15ad6/cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d", size = 6223222 },
{ url = "https://files.pythonhosted.org/packages/33/13/1193774705783ba364121aa2a60132fa31a668b8ababd5edfa1662354ccd/cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062", size = 3794751 },
{ url = "https://files.pythonhosted.org/packages/5e/4b/39bb3c4c8cfb3e94e736b8d8859ce5c81536e91a1033b1d26770c4249000/cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962", size = 3981827 },
{ url = "https://files.pythonhosted.org/packages/ce/dc/1471d4d56608e1013237af334b8a4c35d53895694fbb73882d1c4fd3f55e/cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277", size = 3780034 },
{ url = "https://files.pythonhosted.org/packages/ad/43/7a9920135b0d5437cc2f8f529fa757431eb6a7736ddfadfdee1cc5890800/cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a", size = 3993407 },
{ url = "https://files.pythonhosted.org/packages/cc/42/9ab8467af6c0b76f3d9b8f01d1cf25b9c9f3f2151f4acfab888d21c55a72/cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042", size = 3886457 },
{ url = "https://files.pythonhosted.org/packages/a4/65/430509e31700286ec02868a2457d2111d03ccefc20349d24e58d171ae0a7/cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494", size = 4081499 },
{ url = "https://files.pythonhosted.org/packages/bb/18/a04b6467e6e09df8c73b91dcee8878f4a438a43a3603dc3cd6f8003b92d8/cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2", size = 2616504 },
{ url = "https://files.pythonhosted.org/packages/cc/73/0eacbdc437202edcbdc07f3576ed8fb8b0ab79d27bf2c5d822d758a72faa/cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d", size = 3067456 },
{ url = "https://files.pythonhosted.org/packages/8a/b6/bc54b371f02cffd35ff8dc6baba88304d7cf8e83632566b4b42e00383e03/cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d", size = 6225263 },
{ url = "https://files.pythonhosted.org/packages/00/0e/8217e348a1fa417ec4c78cd3cdf24154f5e76fd7597343a35bd403650dfd/cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806", size = 3794368 },
{ url = "https://files.pythonhosted.org/packages/3d/ed/38b6be7254d8f7251fde8054af597ee8afa14f911da67a9410a45f602fc3/cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85", size = 3981750 },
{ url = "https://files.pythonhosted.org/packages/64/f3/b7946c3887cf7436f002f4cbb1e6aec77b8d299b86be48eeadfefb937c4b/cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c", size = 3778925 },
{ url = "https://files.pythonhosted.org/packages/ac/7e/ebda4dd4ae098a0990753efbb4b50954f1d03003846b943ea85070782da7/cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1", size = 3993152 },
{ url = "https://files.pythonhosted.org/packages/43/f6/feebbd78a3e341e3913846a3bb2c29d0b09b1b3af1573c6baabc2533e147/cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa", size = 3886392 },
{ url = "https://files.pythonhosted.org/packages/bd/4c/ab0b9407d5247576290b4fd8abd06b7f51bd414f04eef0f2800675512d61/cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4", size = 4082606 },
{ url = "https://files.pythonhosted.org/packages/05/36/e532a671998d6fcfdb9122da16434347a58a6bae9465e527e450e0bc60a5/cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47", size = 2617948 },
{ url = "https://files.pythonhosted.org/packages/b3/c6/c09cee6968add5ff868525c3815e5dccc0e3c6e89eec58dc9135d3c40e88/cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb", size = 3070445 },
{ url = "https://files.pythonhosted.org/packages/18/23/4175dcd935e1649865e1af7bd0b827cc9d9769a586dcc84f7cbe96839086/cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034", size = 3152694 },
{ url = "https://files.pythonhosted.org/packages/ea/45/967da50269954b993d4484bf85026c7377bd551651ebdabba94905972556/cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d", size = 3713077 },
{ url = "https://files.pythonhosted.org/packages/df/e6/ccd29a1f9a6b71294e1e9f530c4d779d5dd37c8bb736c05d5fb6d98a971b/cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289", size = 3915597 },
{ url = "https://files.pythonhosted.org/packages/a2/80/fb7d668f1be5e4443b7ac191f68390be24f7c2ebd36011741f62c7645eb2/cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84", size = 2989208 },
{ url = "https://files.pythonhosted.org/packages/b2/aa/782e42ccf854943dfce72fb94a8d62220f22084ff07076a638bc3f34f3cc/cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365", size = 3154685 },
{ url = "https://files.pythonhosted.org/packages/3e/fd/70f3e849ad4d6cca2118ee6938e0b52326d02406f10912356151dd4b6868/cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96", size = 3713909 },
{ url = "https://files.pythonhosted.org/packages/21/b0/4ecefa99519eaa32af49a3ad002bb3e795f9e6eb32221fd87736247fa3cb/cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172", size = 3916544 },
{ url = "https://files.pythonhosted.org/packages/8c/42/2948dd87b237565c77b28b674d972c7f983ffa3977dc8b8ad0736f6a7d97/cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2", size = 2989774 },
]
[[package]]
name = "distlib"
version = "0.3.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c4/91/e2df406fb4efacdf46871c25cde65d3c6ee5e173b7e5a4547a47bae91920/distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64", size = 609931 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8e/41/9307e4f5f9976bc8b7fea0b66367734e8faf3ec84bc0d412d8cfabbb66cd/distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", size = 468850 },
]
[[package]]
name = "docutils"
version = "0.18.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/57/b1/b880503681ea1b64df05106fc7e3c4e3801736cf63deffc6fa7fc5404cf5/docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06", size = 2043249 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8d/14/69b4bad34e3f250afe29a854da03acb6747711f3df06c359fa053fae4e76/docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c", size = 570050 },
]
[[package]]
name = "exceptiongroup"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
]
[[package]]
name = "filelock"
version = "3.16.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 },
]
[[package]]
name = "firebase-messaging"
version = "0.4.4"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
{ name = "cryptography" },
{ name = "http-ece" },
{ name = "protobuf" },
]
[package.optional-dependencies]
docs = [
{ name = "myst-parser" },
{ name = "sphinx" },
{ name = "sphinx-autodoc-typehints" },
{ name = "sphinx-rtd-theme" },
]
[package.dev-dependencies]
dev = [
{ name = "aioresponses" },
{ name = "async-timeout" },
{ name = "mypy" },
{ name = "pre-commit" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "pytest-mock" },
{ name = "pytest-socket" },
{ name = "ruff" },
{ name = "types-protobuf" },
]
[package.metadata]
requires-dist = [
{ name = "aiohttp", specifier = ">=3.9.3" },
{ name = "cryptography", specifier = ">=2.5" },
{ name = "http-ece", specifier = "~=1.1.0" },
{ name = "myst-parser", marker = "extra == 'docs'" },
{ name = "protobuf", specifier = ">=4.24.3,<6" },
{ name = "sphinx", marker = "extra == 'docs'", specifier = "==7.1.2" },
{ name = "sphinx-autodoc-typehints", marker = "extra == 'docs'", specifier = "~=1.24" },
{ name = "sphinx-rtd-theme", marker = "extra == 'docs'", specifier = "~=1.3" },
]
[package.metadata.requires-dev]
dev = [
{ name = "aioresponses", specifier = "~=0.7.6" },
{ name = "async-timeout", specifier = ">=3.0.0" },
{ name = "mypy", specifier = "~=1.0" },
{ name = "pre-commit" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "pytest-mock" },
{ name = "pytest-socket", specifier = "~=0.7.0" },
{ name = "ruff", specifier = ">=0.6.8" },
{ name = "types-protobuf", specifier = ">=4.24" },
]
[[package]]
name = "frozenlist"
version = "1.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cf/3d/2102257e7acad73efc4a0c306ad3953f68c504c16982bbdfee3ad75d8085/frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", size = 37820 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/35/1328c7b0f780d34f8afc1d87ebdc2bb065a123b24766a0b475f0d67da637/frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", size = 94315 },
{ url = "https://files.pythonhosted.org/packages/f4/d6/ca016b0adcf8327714ccef969740688808c86e0287bf3a639ff582f24e82/frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", size = 53805 },
{ url = "https://files.pythonhosted.org/packages/ae/83/bcdaa437a9bd693ba658a0310f8cdccff26bd78e45fccf8e49897904a5cd/frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", size = 52163 },
{ url = "https://files.pythonhosted.org/packages/d4/e9/759043ab7d169b74fe05ebfbfa9ee5c881c303ebc838e308346204309cd0/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", size = 238595 },
{ url = "https://files.pythonhosted.org/packages/f8/ce/b9de7dc61e753dc318cf0de862181b484178210c5361eae6eaf06792264d/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", size = 262428 },
{ url = "https://files.pythonhosted.org/packages/36/ce/dc6f29e0352fa34ebe45421960c8e7352ca63b31630a576e8ffb381e9c08/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", size = 258867 },
{ url = "https://files.pythonhosted.org/packages/51/47/159ac53faf8a11ae5ee8bb9db10327575557504e549cfd76f447b969aa91/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", size = 229412 },
{ url = "https://files.pythonhosted.org/packages/ec/25/0c87df2e53c0c5d90f7517ca0ff7aca78d050a8ec4d32c4278e8c0e52e51/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", size = 239539 },
{ url = "https://files.pythonhosted.org/packages/97/94/a1305fa4716726ae0abf3b1069c2d922fcfd442538cb850f1be543f58766/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", size = 253379 },
{ url = "https://files.pythonhosted.org/packages/53/82/274e19f122e124aee6d113188615f63b0736b4242a875f482a81f91e07e2/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", size = 245901 },
{ url = "https://files.pythonhosted.org/packages/b8/28/899931015b8cffbe155392fe9ca663f981a17e1adc69589ee0e1e7cdc9a2/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", size = 263797 },
{ url = "https://files.pythonhosted.org/packages/6e/4f/b8a5a2f10c4a58c52a52a40cf6cf1ffcdbf3a3b64f276f41dab989bf3ab5/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", size = 264415 },
{ url = "https://files.pythonhosted.org/packages/b0/2c/7be3bdc59dbae444864dbd9cde82790314390ec54636baf6b9ce212627ad/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", size = 253964 },
{ url = "https://files.pythonhosted.org/packages/2e/ec/4fb5a88f6b9a352aed45ab824dd7ce4801b7bcd379adcb927c17a8f0a1a8/frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", size = 44559 },
{ url = "https://files.pythonhosted.org/packages/61/15/2b5d644d81282f00b61e54f7b00a96f9c40224107282efe4cd9d2bf1433a/frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", size = 50434 },
{ url = "https://files.pythonhosted.org/packages/01/bc/8d33f2d84b9368da83e69e42720cff01c5e199b5a868ba4486189a4d8fa9/frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", size = 97060 },
{ url = "https://files.pythonhosted.org/packages/af/b2/904500d6a162b98a70e510e743e7ea992241b4f9add2c8063bf666ca21df/frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", size = 55347 },
{ url = "https://files.pythonhosted.org/packages/5b/9c/f12b69997d3891ddc0d7895999a00b0c6a67f66f79498c0e30f27876435d/frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", size = 53374 },
{ url = "https://files.pythonhosted.org/packages/ac/6e/e0322317b7c600ba21dec224498c0c5959b2bce3865277a7c0badae340a9/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", size = 273288 },
{ url = "https://files.pythonhosted.org/packages/a7/76/180ee1b021568dad5b35b7678616c24519af130ed3fa1e0f1ed4014e0f93/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", size = 284737 },
{ url = "https://files.pythonhosted.org/packages/05/08/40159d706a6ed983c8aca51922a93fc69f3c27909e82c537dd4054032674/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", size = 280267 },
{ url = "https://files.pythonhosted.org/packages/e0/18/9f09f84934c2b2aa37d539a322267939770362d5495f37783440ca9c1b74/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", size = 258778 },
{ url = "https://files.pythonhosted.org/packages/b3/c9/0bc5ee7e1f5cc7358ab67da0b7dfe60fbd05c254cea5c6108e7d1ae28c63/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", size = 272276 },
{ url = "https://files.pythonhosted.org/packages/12/5d/147556b73a53ad4df6da8bbb50715a66ac75c491fdedac3eca8b0b915345/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", size = 272424 },
{ url = "https://files.pythonhosted.org/packages/83/61/2087bbf24070b66090c0af922685f1d0596c24bb3f3b5223625bdeaf03ca/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", size = 260881 },
{ url = "https://files.pythonhosted.org/packages/a8/be/a235bc937dd803258a370fe21b5aa2dd3e7bfe0287a186a4bec30c6cccd6/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", size = 282327 },
{ url = "https://files.pythonhosted.org/packages/5d/e7/b2469e71f082948066b9382c7b908c22552cc705b960363c390d2e23f587/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74", size = 281502 },
{ url = "https://files.pythonhosted.org/packages/db/1b/6a5b970e55dffc1a7d0bb54f57b184b2a2a2ad0b7bca16a97ca26d73c5b5/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", size = 272292 },
{ url = "https://files.pythonhosted.org/packages/1a/05/ebad68130e6b6eb9b287dacad08ea357c33849c74550c015b355b75cc714/frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", size = 44446 },
{ url = "https://files.pythonhosted.org/packages/b3/21/c5aaffac47fd305d69df46cfbf118768cdf049a92ee6b0b5cb029d449dcf/frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", size = 50459 },
{ url = "https://files.pythonhosted.org/packages/b4/db/4cf37556a735bcdb2582f2c3fa286aefde2322f92d3141e087b8aeb27177/frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", size = 93937 },
{ url = "https://files.pythonhosted.org/packages/46/03/69eb64642ca8c05f30aa5931d6c55e50b43d0cd13256fdd01510a1f85221/frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", size = 53656 },
{ url = "https://files.pythonhosted.org/packages/3f/ab/c543c13824a615955f57e082c8a5ee122d2d5368e80084f2834e6f4feced/frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", size = 51868 },
{ url = "https://files.pythonhosted.org/packages/a9/b8/438cfd92be2a124da8259b13409224d9b19ef8f5a5b2507174fc7e7ea18f/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", size = 280652 },
{ url = "https://files.pythonhosted.org/packages/54/72/716a955521b97a25d48315c6c3653f981041ce7a17ff79f701298195bca3/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", size = 286739 },
{ url = "https://files.pythonhosted.org/packages/65/d8/934c08103637567084568e4d5b4219c1016c60b4d29353b1a5b3587827d6/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", size = 289447 },
{ url = "https://files.pythonhosted.org/packages/70/bb/d3b98d83ec6ef88f9bd63d77104a305d68a146fd63a683569ea44c3085f6/frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", size = 265466 },
{ url = "https://files.pythonhosted.org/packages/0b/f2/b8158a0f06faefec33f4dff6345a575c18095a44e52d4f10c678c137d0e0/frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", size = 281530 },
{ url = "https://files.pythonhosted.org/packages/ea/a2/20882c251e61be653764038ece62029bfb34bd5b842724fff32a5b7a2894/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", size = 281295 },
{ url = "https://files.pythonhosted.org/packages/4c/f9/8894c05dc927af2a09663bdf31914d4fb5501653f240a5bbaf1e88cab1d3/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", size = 268054 },
{ url = "https://files.pythonhosted.org/packages/37/ff/a613e58452b60166507d731812f3be253eb1229808e59980f0405d1eafbf/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", size = 286904 },
{ url = "https://files.pythonhosted.org/packages/cc/6e/0091d785187f4c2020d5245796d04213f2261ad097e0c1cf35c44317d517/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", size = 290754 },
{ url = "https://files.pythonhosted.org/packages/a5/c2/e42ad54bae8bcffee22d1e12a8ee6c7717f7d5b5019261a8c861854f4776/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", size = 282602 },
{ url = "https://files.pythonhosted.org/packages/b6/61/56bad8cb94f0357c4bc134acc30822e90e203b5cb8ff82179947de90c17f/frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", size = 44063 },
{ url = "https://files.pythonhosted.org/packages/3e/dc/96647994a013bc72f3d453abab18340b7f5e222b7b7291e3697ca1fcfbd5/frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", size = 50452 },
{ url = "https://files.pythonhosted.org/packages/d3/fb/6f2a22086065bc16797f77168728f0e59d5b89be76dd184e06b404f1e43b/frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e", size = 97291 },
{ url = "https://files.pythonhosted.org/packages/4d/23/7f01123d0e5adcc65cbbde5731378237dea7db467abd19e391f1ddd4130d/frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d", size = 55249 },
{ url = "https://files.pythonhosted.org/packages/8b/c9/a81e9af48291954a883d35686f32308238dc968043143133b8ac9e2772af/frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8", size = 53676 },
{ url = "https://files.pythonhosted.org/packages/57/15/172af60c7e150a1d88ecc832f2590721166ae41eab582172fe1e9844eab4/frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0", size = 239365 },
{ url = "https://files.pythonhosted.org/packages/8c/a4/3dc43e259960ad268ef8f2bf92912c2d2cd2e5275a4838804e03fd6f085f/frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b", size = 265592 },
{ url = "https://files.pythonhosted.org/packages/a0/c1/458cf031fc8cd29a751e305b1ec773785ce486106451c93986562c62a21e/frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0", size = 261274 },
{ url = "https://files.pythonhosted.org/packages/4a/32/21329084b61a119ecce0b2942d30312a34a7a0dccd01dcf7b40bda80f22c/frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897", size = 230787 },
{ url = "https://files.pythonhosted.org/packages/70/b0/6f1ebdabfb604e39a0f84428986b89ab55f246b64cddaa495f2c953e1f6b/frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7", size = 240674 },
{ url = "https://files.pythonhosted.org/packages/a3/05/50c53f1cdbfdf3d2cb9582a4ea5e12cd939ce33bd84403e6d07744563486/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742", size = 255712 },
{ url = "https://files.pythonhosted.org/packages/b8/3d/cbc6f057f7d10efb7f1f410e458ac090f30526fd110ed2b29bb56ec38fe1/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea", size = 247618 },
{ url = "https://files.pythonhosted.org/packages/96/86/d5e9cd583aed98c9ee35a3aac2ce4d022ce9de93518e963aadf34a18143b/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5", size = 266868 },
{ url = "https://files.pythonhosted.org/packages/0f/6e/542af762beb9113f13614a590cafe661e0e060cddddee6107c8833605776/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9", size = 266439 },
{ url = "https://files.pythonhosted.org/packages/ea/db/8b611e23fda75da5311b698730a598df54cfe6236678001f449b1dedb241/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6", size = 256677 },
{ url = "https://files.pythonhosted.org/packages/eb/06/732cefc0c46c638e4426a859a372a50e4c9d62e65dbfa7ddcf0b13e6a4f2/frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932", size = 44825 },
{ url = "https://files.pythonhosted.org/packages/29/eb/2110c4be2f622e87864e433efd7c4ee6e4f8a59ff2a93c1aa426ee50a8b8/frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0", size = 50652 },
{ url = "https://files.pythonhosted.org/packages/83/10/466fe96dae1bff622021ee687f68e5524d6392b0a2f80d05001cd3a451ba/frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", size = 11552 },
]
[[package]]
name = "http-ece"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2b/1a/60ccc29fccd4789b7cada188b114185e8a5d63aba0d93262adbbe776cfe5/http_ece-1.1.0.tar.gz", hash = "sha256:932ebc2fa7c216954c320a188ae9c1f04d01e67bec9cdce1bfbc912813b0b4f8", size = 4902 }
[[package]]
name = "identify"
version = "2.6.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/29/bb/25024dbcc93516c492b75919e76f389bac754a3e4248682fba32b250c880/identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98", size = 99097 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7d/0c/4ef72754c050979fdcc06c744715ae70ea37e734816bb6514f79df77a42f/identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0", size = 98972 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "imagesize"
version = "1.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 },
]
[[package]]
name = "importlib-metadata"
version = "8.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "jinja2"
version = "3.1.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
]
[[package]]
name = "markupsafe"
version = "2.1.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/54/ad5eb37bf9d51800010a74e4665425831a9db4e7c4e0fde4352e391e808e/MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", size = 18206 },
{ url = "https://files.pythonhosted.org/packages/6a/4a/a4d49415e600bacae038c67f9fecc1d5433b9d3c71a4de6f33537b89654c/MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", size = 14079 },
{ url = "https://files.pythonhosted.org/packages/0a/7b/85681ae3c33c385b10ac0f8dd025c30af83c78cec1c37a6aa3b55e67f5ec/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", size = 26620 },
{ url = "https://files.pythonhosted.org/packages/7c/52/2b1b570f6b8b803cef5ac28fdf78c0da318916c7d2fe9402a84d591b394c/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", size = 25818 },
{ url = "https://files.pythonhosted.org/packages/29/fe/a36ba8c7ca55621620b2d7c585313efd10729e63ef81e4e61f52330da781/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", size = 25493 },
{ url = "https://files.pythonhosted.org/packages/60/ae/9c60231cdfda003434e8bd27282b1f4e197ad5a710c14bee8bea8a9ca4f0/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", size = 30630 },
{ url = "https://files.pythonhosted.org/packages/65/dc/1510be4d179869f5dafe071aecb3f1f41b45d37c02329dfba01ff59e5ac5/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", size = 29745 },
{ url = "https://files.pythonhosted.org/packages/30/39/8d845dd7d0b0613d86e0ef89549bfb5f61ed781f59af45fc96496e897f3a/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", size = 30021 },
{ url = "https://files.pythonhosted.org/packages/c7/5c/356a6f62e4f3c5fbf2602b4771376af22a3b16efa74eb8716fb4e328e01e/MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", size = 16659 },
{ url = "https://files.pythonhosted.org/packages/69/48/acbf292615c65f0604a0c6fc402ce6d8c991276e16c80c46a8f758fbd30c/MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", size = 17213 },
{ url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219 },
{ url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098 },
{ url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014 },
{ url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220 },
{ url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756 },
{ url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988 },
{ url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718 },
{ url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317 },
{ url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670 },
{ url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224 },
{ url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215 },
{ url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069 },
{ url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452 },
{ url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462 },
{ url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869 },
{ url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906 },
{ url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296 },
{ url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 },
{ url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 },
{ url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 },
{ url = "https://files.pythonhosted.org/packages/0f/31/780bb297db036ba7b7bbede5e1d7f1e14d704ad4beb3ce53fb495d22bc62/MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", size = 18193 },
{ url = "https://files.pythonhosted.org/packages/6c/77/d77701bbef72892affe060cdacb7a2ed7fd68dae3b477a8642f15ad3b132/MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", size = 14073 },
{ url = "https://files.pythonhosted.org/packages/d9/a7/1e558b4f78454c8a3a0199292d96159eb4d091f983bc35ef258314fe7269/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", size = 26486 },
{ url = "https://files.pythonhosted.org/packages/5f/5a/360da85076688755ea0cceb92472923086993e86b5613bbae9fbc14136b0/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", size = 25685 },
{ url = "https://files.pythonhosted.org/packages/6a/18/ae5a258e3401f9b8312f92b028c54d7026a97ec3ab20bfaddbdfa7d8cce8/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", size = 25338 },
{ url = "https://files.pythonhosted.org/packages/0b/cc/48206bd61c5b9d0129f4d75243b156929b04c94c09041321456fd06a876d/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", size = 30439 },
{ url = "https://files.pythonhosted.org/packages/d1/06/a41c112ab9ffdeeb5f77bc3e331fdadf97fa65e52e44ba31880f4e7f983c/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", size = 29531 },
{ url = "https://files.pythonhosted.org/packages/02/8c/ab9a463301a50dab04d5472e998acbd4080597abc048166ded5c7aa768c8/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", size = 29823 },
{ url = "https://files.pythonhosted.org/packages/bc/29/9bc18da763496b055d8e98ce476c8e718dcfd78157e17f555ce6dd7d0895/MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", size = 16658 },
{ url = "https://files.pythonhosted.org/packages/f6/f8/4da07de16f10551ca1f640c92b5f316f9394088b183c6a57183df6de5ae4/MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", size = 17211 },
]
[[package]]
name = "mdit-py-plugins"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
]
sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
]
[[package]]
name = "multidict"
version = "6.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/29/68/259dee7fd14cf56a17c554125e534f6274c2860159692a414d0b402b9a6d/multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60", size = 48628 },
{ url = "https://files.pythonhosted.org/packages/50/79/53ba256069fe5386a4a9e80d4e12857ced9de295baf3e20c68cdda746e04/multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1", size = 29327 },
{ url = "https://files.pythonhosted.org/packages/ff/10/71f1379b05b196dae749b5ac062e87273e3f11634f447ebac12a571d90ae/multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53", size = 29689 },
{ url = "https://files.pythonhosted.org/packages/71/45/70bac4f87438ded36ad4793793c0095de6572d433d98575a5752629ef549/multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5", size = 126639 },
{ url = "https://files.pythonhosted.org/packages/80/cf/17f35b3b9509b4959303c05379c4bfb0d7dd05c3306039fc79cf035bbac0/multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581", size = 134315 },
{ url = "https://files.pythonhosted.org/packages/ef/1f/652d70ab5effb33c031510a3503d4d6efc5ec93153562f1ee0acdc895a57/multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56", size = 129471 },
{ url = "https://files.pythonhosted.org/packages/a6/64/2dd6c4c681688c0165dea3975a6a4eab4944ea30f35000f8b8af1df3148c/multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429", size = 124585 },
{ url = "https://files.pythonhosted.org/packages/87/56/e6ee5459894c7e554b57ba88f7257dc3c3d2d379cb15baaa1e265b8c6165/multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748", size = 116957 },
{ url = "https://files.pythonhosted.org/packages/36/9e/616ce5e8d375c24b84f14fc263c7ef1d8d5e8ef529dbc0f1df8ce71bb5b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db", size = 128609 },
{ url = "https://files.pythonhosted.org/packages/8c/4f/4783e48a38495d000f2124020dc96bacc806a4340345211b1ab6175a6cb4/multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056", size = 123016 },
{ url = "https://files.pythonhosted.org/packages/3e/b3/4950551ab8fc39862ba5e9907dc821f896aa829b4524b4deefd3e12945ab/multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76", size = 133542 },
{ url = "https://files.pythonhosted.org/packages/96/4d/f0ce6ac9914168a2a71df117935bb1f1781916acdecbb43285e225b484b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160", size = 130163 },
{ url = "https://files.pythonhosted.org/packages/be/72/17c9f67e7542a49dd252c5ae50248607dfb780bcc03035907dafefb067e3/multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7", size = 126832 },
{ url = "https://files.pythonhosted.org/packages/71/9f/72d719e248cbd755c8736c6d14780533a1606ffb3fbb0fbd77da9f0372da/multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0", size = 26402 },
{ url = "https://files.pythonhosted.org/packages/04/5a/d88cd5d00a184e1ddffc82aa2e6e915164a6d2641ed3606e766b5d2f275a/multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d", size = 28800 },
{ url = "https://files.pythonhosted.org/packages/93/13/df3505a46d0cd08428e4c8169a196131d1b0c4b515c3649829258843dde6/multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", size = 48570 },
{ url = "https://files.pythonhosted.org/packages/f0/e1/a215908bfae1343cdb72f805366592bdd60487b4232d039c437fe8f5013d/multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", size = 29316 },
{ url = "https://files.pythonhosted.org/packages/70/0f/6dc70ddf5d442702ed74f298d69977f904960b82368532c88e854b79f72b/multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", size = 29640 },
{ url = "https://files.pythonhosted.org/packages/d8/6d/9c87b73a13d1cdea30b321ef4b3824449866bd7f7127eceed066ccb9b9ff/multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b", size = 131067 },
{ url = "https://files.pythonhosted.org/packages/cc/1e/1b34154fef373371fd6c65125b3d42ff5f56c7ccc6bfff91b9b3c60ae9e0/multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72", size = 138507 },
{ url = "https://files.pythonhosted.org/packages/fb/e0/0bc6b2bac6e461822b5f575eae85da6aae76d0e2a79b6665d6206b8e2e48/multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304", size = 133905 },
{ url = "https://files.pythonhosted.org/packages/ba/af/73d13b918071ff9b2205fcf773d316e0f8fefb4ec65354bbcf0b10908cc6/multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351", size = 129004 },
{ url = "https://files.pythonhosted.org/packages/74/21/23960627b00ed39643302d81bcda44c9444ebcdc04ee5bedd0757513f259/multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb", size = 121308 },
{ url = "https://files.pythonhosted.org/packages/8b/5c/cf282263ffce4a596ed0bb2aa1a1dddfe1996d6a62d08842a8d4b33dca13/multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3", size = 132608 },
{ url = "https://files.pythonhosted.org/packages/d7/3e/97e778c041c72063f42b290888daff008d3ab1427f5b09b714f5a8eff294/multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399", size = 127029 },
{ url = "https://files.pythonhosted.org/packages/47/ac/3efb7bfe2f3aefcf8d103e9a7162572f01936155ab2f7ebcc7c255a23212/multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423", size = 137594 },
{ url = "https://files.pythonhosted.org/packages/42/9b/6c6e9e8dc4f915fc90a9b7798c44a30773dea2995fdcb619870e705afe2b/multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3", size = 134556 },
{ url = "https://files.pythonhosted.org/packages/1d/10/8e881743b26aaf718379a14ac58572a240e8293a1c9d68e1418fb11c0f90/multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753", size = 130993 },
{ url = "https://files.pythonhosted.org/packages/45/84/3eb91b4b557442802d058a7579e864b329968c8d0ea57d907e7023c677f2/multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80", size = 26405 },
{ url = "https://files.pythonhosted.org/packages/9f/0b/ad879847ecbf6d27e90a6eabb7eff6b62c129eefe617ea45eae7c1f0aead/multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926", size = 28795 },
{ url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 },
{ url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 },
{ url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 },
{ url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 },
{ url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 },
{ url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 },
{ url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 },
{ url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 },
{ url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 },
{ url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 },
{ url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 },
{ url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 },
{ url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 },
{ url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 },
{ url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 },
{ url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 },
{ url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 },
{ url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 },
{ url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 },
{ url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 },
{ url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 },
{ url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 },
{ url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 },
{ url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 },
{ url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 },
{ url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 },
{ url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 },
{ url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 },
{ url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 },
{ url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 },
{ url = "https://files.pythonhosted.org/packages/e7/c9/9e153a6572b38ac5ff4434113af38acf8d5e9957897cdb1f513b3d6614ed/multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c", size = 48550 },
{ url = "https://files.pythonhosted.org/packages/76/f5/79565ddb629eba6c7f704f09a09df085c8dc04643b12506f10f718cee37a/multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1", size = 29298 },
{ url = "https://files.pythonhosted.org/packages/60/1b/9851878b704bc98e641a3e0bce49382ae9e05743dac6d97748feb5b7baba/multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c", size = 29641 },
{ url = "https://files.pythonhosted.org/packages/89/87/d451d45aab9e422cb0fb2f7720c31a4c1d3012c740483c37f642eba568fb/multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c", size = 126202 },
{ url = "https://files.pythonhosted.org/packages/fa/b4/27cbe9f3e2e469359887653f2e45470272eef7295139916cc21107c6b48c/multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f", size = 133925 },
{ url = "https://files.pythonhosted.org/packages/4d/a3/afc841899face8adfd004235ce759a37619f6ec99eafd959650c5ce4df57/multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875", size = 129039 },
{ url = "https://files.pythonhosted.org/packages/5e/41/0d0fb18c1ad574f807196f5f3d99164edf9de3e169a58c6dc2d6ed5742b9/multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255", size = 124072 },
{ url = "https://files.pythonhosted.org/packages/00/22/defd7a2e71a44e6e5b9a5428f972e5b572e7fe28e404dfa6519bbf057c93/multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30", size = 116532 },
{ url = "https://files.pythonhosted.org/packages/91/25/f7545102def0b1d456ab6449388eed2dfd822debba1d65af60194904a23a/multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057", size = 128173 },
{ url = "https://files.pythonhosted.org/packages/45/79/3dbe8d35fc99f5ea610813a72ab55f426cb9cf482f860fa8496e5409be11/multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657", size = 122654 },
{ url = "https://files.pythonhosted.org/packages/97/cb/209e735eeab96e1b160825b5d0b36c56d3862abff828fc43999bb957dcad/multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28", size = 133197 },
{ url = "https://files.pythonhosted.org/packages/e4/3a/a13808a7ada62808afccea67837a79d00ad6581440015ef00f726d064c2d/multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972", size = 129754 },
{ url = "https://files.pythonhosted.org/packages/77/dd/8540e139eafb240079242da8f8ffdf9d3f4b4ad1aac5a786cd4050923783/multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43", size = 126402 },
{ url = "https://files.pythonhosted.org/packages/86/99/e82e1a275d8b1ea16d3a251474262258dbbe41c05cce0c01bceda1fc8ea5/multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada", size = 26421 },
{ url = "https://files.pythonhosted.org/packages/86/1c/9fa630272355af7e4446a2c7550c259f11ee422ab2d30ff90a0a71cf3d9e/multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a", size = 28791 },
{ url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 },
]
[[package]]
name = "mypy"
version = "1.11.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mypy-extensions" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5c/86/5d7cbc4974fd564550b80fbb8103c05501ea11aa7835edf3351d90095896/mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79", size = 3078806 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/cd/815368cd83c3a31873e5e55b317551500b12f2d1d7549720632f32630333/mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a", size = 10939401 },
{ url = "https://files.pythonhosted.org/packages/f1/27/e18c93a195d2fad75eb96e1f1cbc431842c332e8eba2e2b77eaf7313c6b7/mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef", size = 10111697 },
{ url = "https://files.pythonhosted.org/packages/dc/08/cdc1fc6d0d5a67d354741344cc4aa7d53f7128902ebcbe699ddd4f15a61c/mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383", size = 12500508 },
{ url = "https://files.pythonhosted.org/packages/64/12/aad3af008c92c2d5d0720ea3b6674ba94a98cdb86888d389acdb5f218c30/mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8", size = 13020712 },
{ url = "https://files.pythonhosted.org/packages/03/e6/a7d97cc124a565be5e9b7d5c2a6ebf082379ffba99646e4863ed5bbcb3c3/mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7", size = 9567319 },
{ url = "https://files.pythonhosted.org/packages/e2/aa/cc56fb53ebe14c64f1fe91d32d838d6f4db948b9494e200d2f61b820b85d/mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385", size = 10859630 },
{ url = "https://files.pythonhosted.org/packages/04/c8/b19a760fab491c22c51975cf74e3d253b8c8ce2be7afaa2490fbf95a8c59/mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca", size = 10037973 },
{ url = "https://files.pythonhosted.org/packages/88/57/7e7e39f2619c8f74a22efb9a4c4eff32b09d3798335625a124436d121d89/mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104", size = 12416659 },
{ url = "https://files.pythonhosted.org/packages/fc/a6/37f7544666b63a27e46c48f49caeee388bf3ce95f9c570eb5cfba5234405/mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4", size = 12897010 },
{ url = "https://files.pythonhosted.org/packages/84/8b/459a513badc4d34acb31c736a0101c22d2bd0697b969796ad93294165cfb/mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6", size = 9562873 },
{ url = "https://files.pythonhosted.org/packages/35/3a/ed7b12ecc3f6db2f664ccf85cb2e004d3e90bec928e9d7be6aa2f16b7cdf/mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318", size = 10990335 },
{ url = "https://files.pythonhosted.org/packages/04/e4/1a9051e2ef10296d206519f1df13d2cc896aea39e8683302f89bf5792a59/mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36", size = 10007119 },
{ url = "https://files.pythonhosted.org/packages/f3/3c/350a9da895f8a7e87ade0028b962be0252d152e0c2fbaafa6f0658b4d0d4/mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987", size = 12506856 },
{ url = "https://files.pythonhosted.org/packages/b6/49/ee5adf6a49ff13f4202d949544d3d08abb0ea1f3e7f2a6d5b4c10ba0360a/mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca", size = 12952066 },
{ url = "https://files.pythonhosted.org/packages/27/c0/b19d709a42b24004d720db37446a42abadf844d5c46a2c442e2a074d70d9/mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70", size = 9664000 },
{ url = "https://files.pythonhosted.org/packages/16/64/bb5ed751487e2bea0dfaa6f640a7e3bb88083648f522e766d5ef4a76f578/mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6", size = 10937294 },
{ url = "https://files.pythonhosted.org/packages/a9/a3/67a0069abed93c3bf3b0bebb8857e2979a02828a4a3fd82f107f8f1143e8/mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70", size = 10107707 },
{ url = "https://files.pythonhosted.org/packages/2f/4d/0379daf4258b454b1f9ed589a9dabd072c17f97496daea7b72fdacf7c248/mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d", size = 12498367 },
{ url = "https://files.pythonhosted.org/packages/3b/dc/3976a988c280b3571b8eb6928882dc4b723a403b21735a6d8ae6ed20e82b/mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d", size = 13018014 },
{ url = "https://files.pythonhosted.org/packages/83/84/adffc7138fb970e7e2a167bd20b33bb78958370179853a4ebe9008139342/mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24", size = 9568056 },
{ url = "https://files.pythonhosted.org/packages/42/3a/bdf730640ac523229dd6578e8a581795720a9321399de494374afc437ec5/mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12", size = 2619625 },
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
]
[[package]]
name = "myst-parser"
version = "3.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "docutils" },
{ name = "jinja2" },
{ name = "markdown-it-py" },
{ name = "mdit-py-plugins" },
{ name = "pyyaml" },
{ name = "sphinx" },
]
sdist = { url = "https://files.pythonhosted.org/packages/49/64/e2f13dac02f599980798c01156393b781aec983b52a6e4057ee58f07c43a/myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87", size = 92392 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e2/de/21aa8394f16add8f7427f0a1326ccd2b3a2a8a3245c9252bc5ac034c6155/myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1", size = 83163 },
]
[[package]]
name = "nodeenv"
version = "1.9.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
]
[[package]]
name = "packaging"
version = "24.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 },
]
[[package]]
name = "platformdirs"
version = "4.3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
]
[[package]]
name = "pluggy"
version = "1.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
]
[[package]]
name = "pre-commit"
version = "3.8.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cfgv" },
{ name = "identify" },
{ name = "nodeenv" },
{ name = "pyyaml" },
{ name = "virtualenv" },
]
sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643 },
]
[[package]]
name = "protobuf"
version = "5.28.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b1/a4/4579a61de526e19005ceeb93e478b61d77aa38c8a85ad958ff16a9906549/protobuf-5.28.2.tar.gz", hash = "sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0", size = 422494 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/30/231764750e0987755b7b8d66771f161e5f002e165d27b72154c776dbabf7/protobuf-5.28.2-cp310-abi3-win32.whl", hash = "sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d", size = 419662 },
{ url = "https://files.pythonhosted.org/packages/7d/46/3fdf7462160135aee6a530f1ec66665b5b4132fa2e1002ab971bc6ec2589/protobuf-5.28.2-cp310-abi3-win_amd64.whl", hash = "sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132", size = 431479 },
{ url = "https://files.pythonhosted.org/packages/37/45/d2a760580f8f2ed2825ba44cb370e0a4011ddef85e728f46ea3dd565a8a5/protobuf-5.28.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7", size = 414736 },
{ url = "https://files.pythonhosted.org/packages/e6/23/ed718dc18e6a561445ece1e7a17d2dda0c634ad9cf663102b47f10005d8f/protobuf-5.28.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f", size = 316518 },
{ url = "https://files.pythonhosted.org/packages/23/08/a1ce0415a115c2b703bfa798f06f0e43ca91dbe29d6180bf86a9287b15e2/protobuf-5.28.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f", size = 316605 },
{ url = "https://files.pythonhosted.org/packages/2d/27/d10aaae326ffd446893d4d1ef18e22c4ece87eb4273ee560ce4e616f0959/protobuf-5.28.2-cp39-cp39-win32.whl", hash = "sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36", size = 419587 },
{ url = "https://files.pythonhosted.org/packages/94/12/af94b0654fa6bde64272b2abab39b221544c32e9e911284745569f65e73a/protobuf-5.28.2-cp39-cp39-win_amd64.whl", hash = "sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276", size = 431552 },
{ url = "https://files.pythonhosted.org/packages/9b/55/f24e3b801d2e108c48aa2b1b59bb791b5cffba89465cbbf66fc98de89270/protobuf-5.28.2-py3-none-any.whl", hash = "sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece", size = 169566 },
]
[[package]]
name = "pycparser"
version = "2.22"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
]
[[package]]
name = "pygments"
version = "2.18.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 },
]
[[package]]
name = "pytest"
version = "8.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 },
]
[[package]]
name = "pytest-asyncio"
version = "0.24.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 },
]
[[package]]
name = "pytest-cov"
version = "5.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "coverage", extra = ["toml"] },
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990 },
]
[[package]]
name = "pytest-mock"
version = "3.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 },
]
[[package]]
name = "pytest-socket"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/05/ff/90c7e1e746baf3d62ce864c479fd53410b534818b9437413903596f81580/pytest_socket-0.7.0.tar.gz", hash = "sha256:71ab048cbbcb085c15a4423b73b619a8b35d6a307f46f78ea46be51b1b7e11b3", size = 12389 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/19/58/5d14cb5cb59409e491ebe816c47bf81423cd03098ea92281336320ae5681/pytest_socket-0.7.0-py3-none-any.whl", hash = "sha256:7e0f4642177d55d317bbd58fc68c6bd9048d6eadb2d46a89307fa9221336ce45", size = 6754 },
]
[[package]]
name = "pyyaml"
version = "6.0.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 },
{ url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 },
{ url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 },
{ url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 },
{ url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 },
{ url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 },
{ url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 },
{ url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 },
{ url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 },
{ url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
{ url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
{ url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
{ url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
{ url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
{ url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
{ url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
{ url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
{ url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
{ url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
{ url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
{ url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
{ url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
{ url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
{ url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
{ url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
{ url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
{ url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
{ url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
{ url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
{ url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
{ url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
{ url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
{ url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
{ url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 },
{ url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 },
{ url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 },
{ url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 },
{ url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 },
{ url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 },
{ url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 },
{ url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 },
{ url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 },
]
[[package]]
name = "requests"
version = "2.32.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
]
[[package]]
name = "ruff"
version = "0.6.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/74/f9/4ce3e765a72ab8fe0f80f48508ea38b4196daab3da14d803c21349b2d367/ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18", size = 3084543 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/db/07/42ee57e8b76ca585297a663a552b4f6d6a99372ca47fdc2276ef72cc0f2f/ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2", size = 10404327 },
{ url = "https://files.pythonhosted.org/packages/eb/51/d42571ff8156d65086acb72d39aa64cb24181db53b497d0ed6293f43f07a/ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c", size = 10018797 },
{ url = "https://files.pythonhosted.org/packages/c1/d7/fa5514a60b03976af972b67fe345deb0335dc96b9f9a9fa4df9890472427/ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5", size = 9691303 },
{ url = "https://files.pythonhosted.org/packages/d6/c4/d812a74976927e51d0782a47539069657ac78535779bfa4d061c4fc8d89d/ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f", size = 10719452 },
{ url = "https://files.pythonhosted.org/packages/ec/b6/aa700c4ae6db9b3ee660e23f3c7db596e2b16a3034b797704fba33ddbc96/ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb", size = 10161353 },
{ url = "https://files.pythonhosted.org/packages/ea/39/0b10075ffcd52ff3a581b9b69eac53579deb230aad300ce8f9d0b58e77bc/ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f", size = 10980630 },
{ url = "https://files.pythonhosted.org/packages/c1/af/9eb9efc98334f62652e2f9318f137b2667187851911fac3b395365a83708/ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0", size = 11768996 },
{ url = "https://files.pythonhosted.org/packages/e0/59/8b1369cf7878358952b1c0a1559b4d6b5c824c003d09b0db26d26c9d094f/ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87", size = 11317469 },
{ url = "https://files.pythonhosted.org/packages/b9/6d/e252e9b11bbca4114c386ee41ad559d0dac13246201d77ea1223c6fea17f/ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098", size = 12467185 },
{ url = "https://files.pythonhosted.org/packages/48/44/7caa223af7d4ea0f0b2bd34acca65a7694a58317714675a2478815ab3f45/ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0", size = 10887766 },
{ url = "https://files.pythonhosted.org/packages/81/ed/394aff3a785f171869158b9d5be61eec9ffb823c3ad5d2bdf2e5f13cb029/ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750", size = 10711609 },
{ url = "https://files.pythonhosted.org/packages/47/31/f31d04c842e54699eab7e3b864538fea26e6c94b71806cd10aa49f13e1c1/ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce", size = 10237621 },
{ url = "https://files.pythonhosted.org/packages/20/95/a764e84acf11d425f2f23b8b78b4fd715e9c20be4aac157c6414ca859a67/ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa", size = 10558329 },
{ url = "https://files.pythonhosted.org/packages/2a/76/d4e38846ac9f6dd62dce858a54583911361b5339dcf8f84419241efac93a/ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44", size = 10954102 },
{ url = "https://files.pythonhosted.org/packages/e7/36/f18c678da6c69f8d022480f3e8ddce6e4a52e07602c1d212056fbd234f8f/ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a", size = 8511090 },
{ url = "https://files.pythonhosted.org/packages/4c/c4/0ca7d8ffa358b109db7d7d045a1a076fd8e5d9cbeae022242d3c060931da/ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263", size = 9350079 },
{ url = "https://files.pythonhosted.org/packages/d9/bd/a8b0c64945a92eaeeb8d0283f27a726a776a1c9d12734d990c5fc7a1278c/ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc", size = 8669595 },
]
[[package]]
name = "snowballstemmer"
version = "2.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 },
]
[[package]]
name = "sphinx"
version = "7.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "alabaster" },
{ name = "babel" },
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "docutils" },
{ name = "imagesize" },
{ name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "jinja2" },
{ name = "packaging" },
{ name = "pygments" },
{ name = "requests" },
{ name = "snowballstemmer" },
{ name = "sphinxcontrib-applehelp" },
{ name = "sphinxcontrib-devhelp" },
{ name = "sphinxcontrib-htmlhelp" },
{ name = "sphinxcontrib-jsmath" },
{ name = "sphinxcontrib-qthelp" },
{ name = "sphinxcontrib-serializinghtml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/dc/01/688bdf9282241dca09fe6e3a1110eda399fa9b10d0672db609e37c2e7a39/sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f", size = 6828258 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/48/17/325cf6a257d84751a48ae90752b3d8fe0be8f9535b6253add61c49d0d9bc/sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe", size = 3169543 },
]
[[package]]
name = "sphinx-autodoc-typehints"
version = "1.25.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "sphinx" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a1/19/1131e37f815864efbdb6498cc41f5ce3da82b7e952456900968132c7dcba/sphinx_autodoc_typehints-1.25.3.tar.gz", hash = "sha256:70db10b391acf4e772019765991d2de0ff30ec0899b9ba137706dc0b3c4835e0", size = 37709 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/d3/8c375e2a6ce21c29021d413e5ebb4d285431acd209f82b89101f157d4b9b/sphinx_autodoc_typehints-1.25.3-py3-none-any.whl", hash = "sha256:d3da7fa9a9761eff6ff09f8b1956ae3090a2d4f4ad54aebcade8e458d6340835", size = 19239 },
]
[[package]]
name = "sphinx-rtd-theme"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "docutils" },
{ name = "sphinx" },
{ name = "sphinxcontrib-jquery" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/3e/477c5b3ed78b6818d673f63512db12ace8c89e83eb9eecc913f9e2cc8416/sphinx_rtd_theme-1.3.0.tar.gz", hash = "sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931", size = 2785069 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/01/76f40a18e9209bb098c1c1313c823dbbd001b23a2db71e7fd4eb5a48559c/sphinx_rtd_theme-1.3.0-py2.py3-none-any.whl", hash = "sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0", size = 2824803 },
]
[[package]]
name = "sphinxcontrib-applehelp"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 },
]
[[package]]
name = "sphinxcontrib-devhelp"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 },
]
[[package]]
name = "sphinxcontrib-htmlhelp"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 },
]
[[package]]
name = "sphinxcontrib-jquery"
version = "4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "sphinx" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104 },
]
[[package]]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 },
]
[[package]]
name = "sphinxcontrib-qthelp"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 },
]
[[package]]
name = "sphinxcontrib-serializinghtml"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 },
]
[[package]]
name = "tomli"
version = "2.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 },
]
[[package]]
name = "types-protobuf"
version = "5.28.0.20240924"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/90/c3/217fe2c6a4b8ed75c5ecbd27ae8dedd7bc8e8728ac4b29d16005d3a3aba2/types-protobuf-5.28.0.20240924.tar.gz", hash = "sha256:d181af8a256e5a91ce8d5adb53496e880efd9144c7d54483e3653332b60296f0", size = 54324 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/61/2b/98bfe67a73b15964513b471ce10b610ab0df28825900e0e7517b2bf23952/types_protobuf-5.28.0.20240924-py3-none-any.whl", hash = "sha256:5cecf612ccdefb7dc95f7a51fb502902f20fc2e6681cd500184aaa1b3931d6a7", size = 68761 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]
[[package]]
name = "urllib3"
version = "2.2.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 },
]
[[package]]
name = "virtualenv"
version = "20.26.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "distlib" },
{ name = "filelock" },
{ name = "platformdirs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/bf/4c/66ce54c8736ff164e85117ca36b02a1e14c042a6963f85eeda82664fda4e/virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4", size = 9371932 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c6/1d/e1a44fdd6d30829ba21fc58b5d98a67e7aae8f4165f11d091e53aec12560/virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6", size = 5999288 },
]
[[package]]
name = "yarl"
version = "1.13.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "multidict" },
]
sdist = { url = "https://files.pythonhosted.org/packages/27/6e/b26e831b6abede32fba3763131eb2c52987f574277daa65e10a5fda6021c/yarl-1.13.0.tar.gz", hash = "sha256:02f117a63d11c8c2ada229029f8bb444a811e62e5041da962de548f26ac2c40f", size = 165688 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/e8/5eb11fc80f93aadb4da491bff2f8ad8fced64fd4415dd4ecd32252fe3c12/yarl-1.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:66c028066be36d54e7a0a38e832302b23222e75db7e65ed862dc94effc8ef062", size = 189620 },
{ url = "https://files.pythonhosted.org/packages/ee/93/bd1545ff3d1d2087ac769d5b4b03204b03591136409e5188b73a5689f575/yarl-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:517f9d90ca0224bb7002266eba6e70d8fcc8b1d0c9321de2407e41344413ed46", size = 115525 },
{ url = "https://files.pythonhosted.org/packages/50/a1/9cf139b0e89c1a8deed77b5d40b998bee707b0e53629487f2c34348a72f4/yarl-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5378cb60f4209505f6aa60423c174336bd7b22e0d8beb87a2a99ad50787f1341", size = 113695 },
{ url = "https://files.pythonhosted.org/packages/e4/3f/45a2ed60a32db65cf6d3dcdc3b37c235f44728a1d49d4fc14a16ac1e0a0e/yarl-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0675a9cf65176e11692b20a516d5f744849251aa24024f422582d2d1bf7c8c82", size = 443623 },
{ url = "https://files.pythonhosted.org/packages/f2/48/1321e9c514e798c89446b1ff6867e8cc6285ce014a4dac6de68de04fd71a/yarl-1.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:419c22b419034b4ee3ba1c27cbbfef01ca8d646f9292f614f008093143334cdc", size = 469118 },
{ url = "https://files.pythonhosted.org/packages/a0/d4/2e401fc232de4dc566b02e6c19cb043b33ecd249663f6ace3654f484dc4e/yarl-1.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf10e525e461f43831d82149d904f35929d89f3ccd65beaf7422aecd500dd39", size = 463210 },
{ url = "https://files.pythonhosted.org/packages/e8/98/cb9082e0270f47678ac155f41fa1f0a1b607181bcb923dacd542595c6520/yarl-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d78ebad57152d301284761b03a708aeac99c946a64ba967d47cbcc040e36688b", size = 447903 },
{ url = "https://files.pythonhosted.org/packages/ad/bd/371a1824a185923b3829cb3f50328012269d86b3a17644e9a0b36e3ea0d5/yarl-1.13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e480a12cec58009eeaeee7f48728dc8f629f8e0f280d84957d42c361969d84da", size = 432828 },
{ url = "https://files.pythonhosted.org/packages/28/6a/f5b6cbf40012974cf86c1174d23cae0cadc0bf78ead244222cb5f22f3bec/yarl-1.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e5462756fb34c884ca9d4875b6d2ec80957a767123151c467c97a9b423617048", size = 444771 },
{ url = "https://files.pythonhosted.org/packages/3b/34/370e8ce2763c4d2328ee12a0b0ada01d480ad1f9f6019489cf36dfe98595/yarl-1.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bff0d468664cdf7b2a6bfd5e17d4a7025edb52df12e0e6e17223387b421d425c", size = 449360 },
{ url = "https://files.pythonhosted.org/packages/06/70/5d565edeb49ea5321a5cdf95824ba61b02802e0e082b9e36f10ef849ac5e/yarl-1.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ffd8a9758b5df7401a49d50e76491f4c582cf7350365439563062cdff45bf16", size = 472735 },
{ url = "https://files.pythonhosted.org/packages/82/c1/9b65e5771ddff3838241f6c9b1dcd65620d6a218fad9a4aeb62a99867a16/yarl-1.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ca71238af0d247d07747cb7202a9359e6e1d6d9e277041e1ad2d9f36b3a111a6", size = 470916 },
{ url = "https://files.pythonhosted.org/packages/e6/2f/d9f7a79cb1d079d947f1202d778f75063102146c7b06597c168d8dcb063a/yarl-1.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fda4404bbb6f91e327827f4483d52fe24f02f92de91217954cf51b1cb9ee9c41", size = 458737 },
{ url = "https://files.pythonhosted.org/packages/59/d6/08ec9b926e6a6254ed1b6178817193c5a93d43ba01888df037c3470b3973/yarl-1.13.0-cp310-cp310-win32.whl", hash = "sha256:e557e2681b47a0ecfdfbea44743b3184d94d31d5ce0e4b13ff64ce227a40f86e", size = 102349 },
{ url = "https://files.pythonhosted.org/packages/6b/9a/2980744994bbbf3a04c3b487044978a9c174367ca9a81c676ced01f5b12f/yarl-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:3590ed9c7477059aea067a58ec87b433bbd47a2ceb67703b1098cca1ba075f0d", size = 111343 },
{ url = "https://files.pythonhosted.org/packages/d5/78/59418fa1cc0ef69cef153b4e6163b1a3850d129a45b92aad8f9d244ac879/yarl-1.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8986fa2be78193dc8b8c27bd0d3667fe612f7232844872714c4200499d5225ca", size = 189559 },
{ url = "https://files.pythonhosted.org/packages/9a/41/af4aa6046a4da16b32768bd788ac331c8397ac264b336ed5695c591f198b/yarl-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0db15ce35dfd100bc9ab40173f143fbea26c84d7458d63206934fe5548fae25d", size = 115451 },
{ url = "https://files.pythonhosted.org/packages/8a/50/1496bff64799e82c06852d5b60d29d9d60d4c4fdebf8f5b1fae505d1a10a/yarl-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:49bee8c99586482a238a7b2ec0ef94e5f186bfdbb8204d14a3dd31867b3875ce", size = 113706 },
{ url = "https://files.pythonhosted.org/packages/ff/17/a68f080c08edb27b8b5a62d418ed9ba1d90242af6792c6c2138180923265/yarl-1.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c73e0f8375b75806b8771890580566a2e6135e6785250840c4f6c45b69eb72d", size = 486063 },
{ url = "https://files.pythonhosted.org/packages/ed/2e/fce9be4ff0df5a112e9007e587acee326ec89b98286fba221e8337e969fe/yarl-1.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ab16c9e94726fdfcbf5b37a641c9d9d0b35cc31f286a2c3b9cad6451cb53b2b", size = 505935 },
{ url = "https://files.pythonhosted.org/packages/f0/c9/a797a46a28c2d68a600ec02c601014cd545a2ca33db34d1b8fc0ae854396/yarl-1.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:784d6e50ea96b3bbb078eb7b40d8c0e3674c2f12da4f0061f889b2cfdbab8f37", size = 500357 },
{ url = "https://files.pythonhosted.org/packages/33/64/9ff1dd2c6acffd739adca70d6b16b059aea2a4ba750c4444aa5d59197e26/yarl-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:580fdb2ea48a40bcaa709ee0dc71f64e7a8f23b44356cc18cd9ce55dc3bc3212", size = 488873 },
{ url = "https://files.pythonhosted.org/packages/5a/d1/a9c696e15311f2b32028f8ff3b447c8334316a6029d30d13f73a081517a4/yarl-1.13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d2845f1a37438a8e11e4fcbbf6ffd64bc94dc9cb8c815f72d0eb6f6c622deb0", size = 471532 },
{ url = "https://files.pythonhosted.org/packages/d2/ca/accf33604a178cf785c71c8cce9956f37638e2e72cea23543fe4adaa9595/yarl-1.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bcb374db7a609484941c01380c1450728ec84d9c3e68cd9a5feaecb52626c4be", size = 485599 },
{ url = "https://files.pythonhosted.org/packages/ff/25/d92abf667d068103b912a56b39d889615a8b07fb8d9ae922cf8fdbe52ede/yarl-1.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:561a5f6c054927cf5a993dd7b032aeebc10644419e65db7dd6bdc0b848806e65", size = 483480 },
{ url = "https://files.pythonhosted.org/packages/ef/94/8a058039537682febfda57fb5d333f8bf7237dad5eab9323f5380325308a/yarl-1.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b536c2ac042add7f276d4e5857b08364fc32f28e02add153f6f214de50f12d07", size = 514004 },
{ url = "https://files.pythonhosted.org/packages/8c/ed/8253a619335c6a692f909b6406e1764369733eed5af3991bbb91bf4a3b24/yarl-1.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:52b7bb09bb48f7855d574480e2efc0c30d31cab4e6ffc6203e2f7ffbf2e4496a", size = 516259 },
{ url = "https://files.pythonhosted.org/packages/e2/17/e8ec3b51d5d02a768a75d6aee5edb8e303483fe3d0bf1f49c1dacf48fe47/yarl-1.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e4dddf99a853b3f60f3ce6363fb1ad94606113743cf653f116a38edd839a4461", size = 498386 },
{ url = "https://files.pythonhosted.org/packages/df/09/2c631d07df653b53c4880416ceb138e1ed7d079329430ef5bc363f02e8c4/yarl-1.13.0-cp311-cp311-win32.whl", hash = "sha256:0b489858642e4e92203941a8fdeeb6373c0535aa986200b22f84d4b39cd602ba", size = 102394 },
{ url = "https://files.pythonhosted.org/packages/df/a0/362619ab4141c2229eb43fa0a62447b14845a4ea50e362a40fd7c934c4aa/yarl-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:31748bee7078db26008bf94d39693c682a26b5c3a80a67194a4c9c8fe3b5cf47", size = 111636 },
{ url = "https://files.pythonhosted.org/packages/11/21/09da58324fca4a9b6ff5710109bd26217b40dee9e6729adb3786e82831c7/yarl-1.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3a9b2650425b2ab9cc68865978963601b3c2414e1d94ef04f193dd5865e1bd79", size = 190212 },
{ url = "https://files.pythonhosted.org/packages/83/9d/3a703336f3b8bbb907ad12bc46fe1f4b795e924b7b923dbcf604212ac3e1/yarl-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:73777f145cd591e1377bf8d8a97e5f8e39c9742ad0f100c898bba1f963aef662", size = 116036 },
{ url = "https://files.pythonhosted.org/packages/42/06/bb53625353041364ba2a8a0ca6bbfe2dafcfeec846d038f44d20746ebc70/yarl-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:144b9e9164f21da81731c970dbda52245b343c0f67f3609d71013dd4d0db9ebf", size = 113890 },
{ url = "https://files.pythonhosted.org/packages/4b/1f/e4c00883ea4debfd34b1c812887f897760c5bdb49de7fc4862df12d2599b/yarl-1.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3628e4e572b1db95285a72c4be102356f2dfc6214d9f126de975fd51b517ae55", size = 483935 },
{ url = "https://files.pythonhosted.org/packages/af/93/7ff1c47e5530d79b2e1dc55a38641b079361c7cf5fa754bc50250ca15445/yarl-1.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0bd3caf554a52da78ec08415ebedeb6b9636436ca2afda9b5b9ff4a533478940", size = 499696 },
{ url = "https://files.pythonhosted.org/packages/ee/ad/a2d9a167460b2043123fc1aeef908131f8d47aa939a0563e4ae44015cb89/yarl-1.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d7a44ae252efb0fcd79ac0997416721a44345f53e5aec4a24f489d983aa00e3", size = 497233 },
{ url = "https://files.pythonhosted.org/packages/5f/6f/0fc937394438542790290416a69bd26e4c91d5cb16d2288ef03518f5ec81/yarl-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b78a1f57780eeeb17f5e1be851ab9fa951b98811e1bb4b5a53f74eec3e2666", size = 490132 },
{ url = "https://files.pythonhosted.org/packages/e5/d0/a7b4e6af60aba824cc8528e870fc41bb84f59fa5e6eecde5fb9908d1793c/yarl-1.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79de5f8432b53d1261d92761f71dfab5fc7e1c75faa12a3535c27e681dacfa9d", size = 469328 },
{ url = "https://files.pythonhosted.org/packages/db/95/3ed41ceaf3bc6ce48eacc0313054223436b4cf66c825f92d5cb806a1b37f/yarl-1.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f603216d62e9680bfac7fb168ef9673fd98abbb50c43e73d97615dfa1afebf57", size = 485630 },
{ url = "https://files.pythonhosted.org/packages/37/9c/b0ae1c3253c9b910abd5c20d4ee4f15b547fea61eaef6ae9f5b9e1b7bf0d/yarl-1.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:acf27399c94270103d68f86118a183008d601e4c2c3a7e98dcde0e3b0163132f", size = 485996 },
{ url = "https://files.pythonhosted.org/packages/7e/23/51879b22108fa24ea5b9f96a225016f73a8b148bdd70adad510a5536abe5/yarl-1.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:08037790f973367431b9406a7b9d940e872cca12e081bce3b7cea068daf81f0a", size = 506685 },
{ url = "https://files.pythonhosted.org/packages/f5/ed/2e3034d7adb7fdeb6b64789d3e92408a45db5ca31e707dd2114758e8f7d9/yarl-1.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33e2f5ef965e69a1f2a1b0071a70c4616157da5a5478f3c2f6e185e06c56a322", size = 516992 },
{ url = "https://files.pythonhosted.org/packages/1d/7d/b091b5b444d522f80a5cd54208cf20a99aa7450a3218afc83f6f4a1001ca/yarl-1.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:38a3b742c923fe2cab7d2e2c67220d17da8d0433e8bfe038356167e697ef5524", size = 502349 },
{ url = "https://files.pythonhosted.org/packages/99/ce/e4e14c485086be114ddfdc11b192190a6f0d680d26d66929da42ca51b5bd/yarl-1.13.0-cp312-cp312-win32.whl", hash = "sha256:ab3ee57b25ce15f79ade27b7dfb5e678af26e4b93be5a4e22655acd9d40b81ba", size = 102301 },
{ url = "https://files.pythonhosted.org/packages/72/2f/d5657e841b51e1855a752cb6cea5c7e266e2e61d8784e8bf6d241ae38b39/yarl-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:26214b0a9b8f4b7b04e67eee94a82c9b4e5c721f4d1ce7e8c87c78f0809b7684", size = 111676 },
{ url = "https://files.pythonhosted.org/packages/3c/9d/62e0325479f6e225c006db065b81d92a214e15dbd9d5f08b7f58cbea2a1d/yarl-1.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:91251614cca1ba4ab0507f1ba5f5a44e17a5e9a4c7f0308ea441a994bdac3fc7", size = 186212 },
{ url = "https://files.pythonhosted.org/packages/33/e6/216ca46bb456cc6942f0098abb67b192c52733292d37cb4f230889c8c826/yarl-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fe6946c3cbcfbed67c5e50dae49baff82ad054aaa10ff7a4db8dfac646b7b479", size = 114218 },
{ url = "https://files.pythonhosted.org/packages/c4/9d/1e937ba8820129effa4fcb8d7188e990711d73f6eaff0888a9205e33cecd/yarl-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:de97ee57e00a82ebb8c378fc73c5d9a773e4c2cec8079ff34ebfef61c8ba5b11", size = 112118 },
{ url = "https://files.pythonhosted.org/packages/62/19/9f60d2c8bfd9820708268c4466e4d52d64b6ecec26557a26d9a7c3d60991/yarl-1.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1129737da2291c9952a93c015e73583dd66054f3ae991c8674f6e39c46d95dd3", size = 471387 },
{ url = "https://files.pythonhosted.org/packages/4c/a9/d6936a780b35a202a9eb93905d283da4243fcfca85f464571d7ce6f5759e/yarl-1.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37049eb26d637a5b2f00562f65aad679f5d231c4c044edcd88320542ad66a2d9", size = 485837 },
{ url = "https://files.pythonhosted.org/packages/f1/62/1903cb89c2b069c985fb0577a152652b80a8700b6f96a72c2b127c00cac3/yarl-1.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d15aff3477fecb7a469d1fdf5939a686fbc5a16858022897d3e9fc99301f19", size = 486662 },
{ url = "https://files.pythonhosted.org/packages/ea/70/17a1092eec93b9b2ca2fe7e9c854b52f968a5457a16c0192cb1684f666e9/yarl-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa187a8599e0425f26b25987d884a8b67deb5565f1c450c3a6e8d3de2cdc8715", size = 478867 },
{ url = "https://files.pythonhosted.org/packages/3e/ab/20d8b6ff384b126e2aca1546b8ba93e4a4aee35cfa68043b8015cf2fb309/yarl-1.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d95fcc9508390db73a0f1c7e78d9a1b1a3532a3f34ceff97c0b3b04140fbe6e4", size = 456455 },
{ url = "https://files.pythonhosted.org/packages/6a/31/66bebe242af5f0615b2a6f7ae9ac37633983c621eb333367830500f8f954/yarl-1.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d04ea92a3643a9bb28aa6954fff718342caab2cc3d25d0160fe16e26c4a9acb7", size = 474964 },
{ url = "https://files.pythonhosted.org/packages/69/02/67d94189a94d191edf47b8a34721e0e7265556e821e9bb2f856da7f8af39/yarl-1.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2842a89b697d8ca3dda6a25b4e4d835d14afe25a315c8a79dbdf5f70edfd0960", size = 477474 },
{ url = "https://files.pythonhosted.org/packages/60/33/a746b05fedc340e8055d38b3f892418577252b1dbd6be474faebe1ceb9f3/yarl-1.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db463fce425f935eee04a9182c74fdf9ed90d3bd2079d4a17f8fb7a2d7c11009", size = 491950 },
{ url = "https://files.pythonhosted.org/packages/5b/75/9759d92dc66108264f305f1ddb3ae02bcc247849a6673ebb678a082d398e/yarl-1.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3ff602aa84420b301c083ae7f07df858ae8e371bf3be294397bda3e0b27c6290", size = 502141 },
{ url = "https://files.pythonhosted.org/packages/e8/8d/4d9f6fa810eca7e07ae7bc6eea0136a4268a32439e6ce6e7454470c51dac/yarl-1.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a9a1a600e8449f3a24bc7dca513be8d69db173fe842e8332a7318b5b8757a6af", size = 492846 },
{ url = "https://files.pythonhosted.org/packages/77/b1/da12907ccb4cea4781357ec027e81e141251726aeffa6ea2c8d1f62cc117/yarl-1.13.0-cp313-cp313-win32.whl", hash = "sha256:5540b4896b244a6539f22b613b32b5d1b737e08011aa4ed56644cb0519d687df", size = 486417 },
{ url = "https://files.pythonhosted.org/packages/66/9e/05d133e7523035517e0dc912a59779dcfd5e978aff32c1c2a3cbc1fd4e7c/yarl-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:08a3b0b8d10092dade46424fe775f2c9bc32e5a985fdd6afe410fe28598db6b2", size = 493756 },
{ url = "https://files.pythonhosted.org/packages/47/44/3b6725635d226feb5e0263716a05186657afb548e7ef7ac1e11c0e255b9a/yarl-1.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:92abbe37e3fb08935e0e95ac5f83f7b286a6f2575f542225ec7afde405ed1fa1", size = 192323 },
{ url = "https://files.pythonhosted.org/packages/82/d7/d03fef722c92b07f9b91832a55e7fa624ec8dbc1700f146168461d0be425/yarl-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1932c7bfa537f89ad5ca3d1e7e05de3388bb9e893230a384159fb974f6e9f90c", size = 117070 },
{ url = "https://files.pythonhosted.org/packages/4a/52/19eef59dde0dc4e6bc73376dca047757cd6b39872b9fba690fbf33dc1fc2/yarl-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4483680e129b2a4250be20947b554cd5f7140fa9e5a1e4f1f42717cf91f8676a", size = 114997 },
{ url = "https://files.pythonhosted.org/packages/c7/f0/830618cabed258962ca81325f41216cb99ece2297d5c1744b5f3f7acfbea/yarl-1.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f6f4a352d0beea5dd39315ab16fc26f0122d13457a7e65ad4f06c7961dcf87a", size = 450259 },
{ url = "https://files.pythonhosted.org/packages/70/30/18c2a2ec41d2af39e99b7589b019622d2c4abf1f7e44fff5455ebcf6925f/yarl-1.13.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a67f20e97462dee8a89e9b997a78932959d2ed991e8f709514cb4160143e7b1", size = 477472 },
{ url = "https://files.pythonhosted.org/packages/11/cf/ce66ab9a022d824592e387b63d18b3fc19ba31731187201ae4b4ef2e199c/yarl-1.13.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf4f3a87bd52f8f33b0155cd0f6f22bdf2092d88c6c6acbb1aee3bc206ecbe35", size = 470167 },
{ url = "https://files.pythonhosted.org/packages/bd/7c/a7c60205831a8bb3dcafe350650936e69c01b45575c8d971e835a10ae585/yarl-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deb70c006be548076d628630aad9a3ef3a1b2c28aaa14b395cf0939b9124252e", size = 455050 },
{ url = "https://files.pythonhosted.org/packages/0b/88/b5d1013311f2f0552b7186033fa02eb14501c87f55c042f15b0267382f0c/yarl-1.13.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf7a9b31729b97985d4a796808859dfd0e37b55f1ca948d46a568e56e51dd8fb", size = 439771 },
{ url = "https://files.pythonhosted.org/packages/c3/f8/83ea7ef4f67ab9f930440bca4f9ea84f3f616876d3589df3e546b3fc2a14/yarl-1.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d807417ceebafb7ce18085a1205d28e8fcb1435a43197d7aa3fab98f5bfec5ef", size = 452000 },
{ url = "https://files.pythonhosted.org/packages/23/f2/dc0d49343fa4d0bedd06df4f6665c8438e49108ba3b443d1e245d779750b/yarl-1.13.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9671d0d65f86e0a0eee59c5b05e381c44e3d15c36c2a67da247d5d82875b4e4e", size = 455543 },
{ url = "https://files.pythonhosted.org/packages/06/40/b9dc6e2da5ff8c07470b4f8643589c9d55381900c317f4a1dfc67e269a47/yarl-1.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:13a9cd39e47ca4dc25139d3c63fe0dc6acf1b24f9d94d3b5197ac578fbfd84bf", size = 481106 },
{ url = "https://files.pythonhosted.org/packages/90/27/6e6c709ee54374494b8580c26ca3daac6039ba6c7f1b12214d99cf16fabc/yarl-1.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:acf8c219a59df22609cfaff4a7158a0946f273e3b03a5385f1fdd502496f0cff", size = 478299 },
{ url = "https://files.pythonhosted.org/packages/da/f3/62bdf7d9fbddbf13cc99c1d941b1687d2890034160cbf76dc6579ab62416/yarl-1.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:12c92576633027f297c26e52aba89f6363b460f483d85cf7c14216eb55d06d02", size = 466354 },
{ url = "https://files.pythonhosted.org/packages/42/e2/56a217a01e2ea0f963fe4a4af68c9af745a090d999704a677a5fe9f5403e/yarl-1.13.0-cp39-cp39-win32.whl", hash = "sha256:c2518660bd8166e770b76ce92514b491b8720ae7e7f5f975cd888b1592894d2c", size = 103369 },
{ url = "https://files.pythonhosted.org/packages/46/91/e3ea08a1770f7ba9822e391f1561d6505c4a7e313c3ea8ec65f26182ab93/yarl-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:db90702060b1cdb7c7609d04df5f68a12fd5581d013ad379e58e0c2e651d92b8", size = 112502 },
{ url = "https://files.pythonhosted.org/packages/10/ae/c3c059042053b92ae25363818901d0634708a3a85048e5ac835bd547107e/yarl-1.13.0-py3-none-any.whl", hash = "sha256:c7d35ff2a5a51bc6d40112cdb4ca3fd9636482ce8c6ceeeee2301e34f7ed7556", size = 39813 },
]
[[package]]
name = "zipp"
version = "3.20.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/bf/5c0000c44ebc80123ecbdddba1f5dcd94a5ada602a9c225d84b5aaa55e86/zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", size = 24199 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", size = 9200 },
]