pax_global_header00006660000000000000000000000064147651447100014523gustar00rootroot0000000000000052 comment=472b94f6191222db317f5cc53f17e84cf03b27bb
bluetooth-data-tools-1.26.1/000077500000000000000000000000001476514471000156645ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/.all-contributorsrc000066400000000000000000000004501476514471000215140ustar00rootroot00000000000000{
"projectName": "bluetooth-data-tools",
"projectOwner": "bdraco",
"repoType": "github",
"repoHost": "https://github.com",
"files": ["README.md"],
"imageSize": 80,
"commit": true,
"commitConvention": "angular",
"contributors": [],
"contributorsPerLine": 7,
"skipCi": true
}
bluetooth-data-tools-1.26.1/.editorconfig000066400000000000000000000004441476514471000203430ustar00rootroot00000000000000# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
end_of_line = lf
[*.bat]
indent_style = tab
end_of_line = crlf
[LICENSE]
insert_final_newline = false
[Makefile]
indent_style = tab
bluetooth-data-tools-1.26.1/.github/000077500000000000000000000000001476514471000172245ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001476514471000214075ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/.github/ISSUE_TEMPLATE/1-bug_report.md000066400000000000000000000004221476514471000242350ustar00rootroot00000000000000---
name: Bug report
about: Create a report to help us improve
labels: bug
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Additional context**
Add any other context about the problem here.
bluetooth-data-tools-1.26.1/.github/ISSUE_TEMPLATE/2-feature-request.md000066400000000000000000000006721476514471000252160ustar00rootroot00000000000000---
name: Feature request
about: Suggest an idea for this project
labels: enhancement
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.
bluetooth-data-tools-1.26.1/.github/dependabot.yml000066400000000000000000000013441476514471000220560ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
commit-message:
prefix: "chore(ci): "
groups:
github-actions:
patterns:
- "*"
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
bluetooth-data-tools-1.26.1/.github/labels.toml000066400000000000000000000035151476514471000213670ustar00rootroot00000000000000[breaking]
color = "ffcc00"
name = "breaking"
description = "Breaking change."
[bug]
color = "d73a4a"
name = "bug"
description = "Something isn't working"
[dependencies]
color = "0366d6"
name = "dependencies"
description = "Pull requests that update a dependency file"
[github_actions]
color = "000000"
name = "github_actions"
description = "Update of github actions"
[documentation]
color = "1bc4a5"
name = "documentation"
description = "Improvements or additions to documentation"
[duplicate]
color = "cfd3d7"
name = "duplicate"
description = "This issue or pull request already exists"
[enhancement]
color = "a2eeef"
name = "enhancement"
description = "New feature or request"
["good first issue"]
color = "7057ff"
name = "good first issue"
description = "Good for newcomers"
["help wanted"]
color = "008672"
name = "help wanted"
description = "Extra attention is needed"
[invalid]
color = "e4e669"
name = "invalid"
description = "This doesn't seem right"
[nochangelog]
color = "555555"
name = "nochangelog"
description = "Exclude pull requests from changelog"
[question]
color = "d876e3"
name = "question"
description = "Further information is requested"
[removed]
color = "e99695"
name = "removed"
description = "Removed piece of functionalities."
[tests]
color = "bfd4f2"
name = "tests"
description = "CI, CD and testing related changes"
[wontfix]
color = "ffffff"
name = "wontfix"
description = "This will not be worked on"
[discussion]
color = "c2e0c6"
name = "discussion"
description = "Some discussion around the project"
[hacktoberfest]
color = "ffa663"
name = "hacktoberfest"
description = "Good issues for Hacktoberfest"
[answered]
color = "0ee2b6"
name = "answered"
description = "Automatically closes as answered after a delay"
[waiting]
color = "5f7972"
name = "waiting"
description = "Automatically closes if no answer after a delay"
bluetooth-data-tools-1.26.1/.github/workflows/000077500000000000000000000000001476514471000212615ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/.github/workflows/ci.yml000066400000000000000000000206541476514471000224060ustar00rootroot00000000000000name: CI
on:
push:
branches:
- main
pull_request:
concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- uses: pre-commit/action@v3.0.1
# Make sure commit messages follow the conventional commits convention:
# https://www.conventionalcommits.org
commitlint:
name: Lint Commit Messages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@v6
test:
strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
os:
- ubuntu-latest
- macOS-latest
- windows-latest
extension:
- "skip_cython"
- "use_cython"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- uses: snok/install-poetry@v1.4.1
- name: Install Dependencies
run: |
if [ "${{ matrix.extension }}" = "skip_cython" ]; then
SKIP_CYTHON=1 poetry install --only=main,dev
else
poetry install --only=main,dev
fi
shell: bash
- name: Test with Pytest
run: poetry run pytest --cov-report=xml -v -Wdefault --cov=bluetooth_data_tools --cov-report=term-missing:skip-covered tests
shell: bash
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
benchmark:
strategy:
fail-fast: false
matrix:
python-version:
- "3.13"
os:
- ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: snok/install-poetry@v1.4.1
- name: Install Dependencies
run: poetry install --only=main,dev,benchmark
env:
REQUIRE_CYTHON: 1
- name: Run benchmarks
run: |
poetry run pytest bench --benchmark-autosave
echo '# Benchmark Results' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
poetry run pytest-benchmark compare --columns=mean,ops >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
shell: bash
codspeed_benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python 3.13
uses: actions/setup-python@v5
with:
python-version: 3.13
- uses: snok/install-poetry@v1.4.1
- name: Install Dependencies
run: |
REQUIRE_CYTHON=1 poetry install --only=main,dev
shell: bash
- name: Run benchmarks
uses: CodSpeedHQ/action@v3
with:
token: ${{ secrets.CODSPEED_TOKEN }}
run: poetry run pytest --no-cov -vvvvv --codspeed tests/benchmarks
release:
needs:
- test
- lint
- commitlint
runs-on: ubuntu-latest
environment: release
concurrency: release
permissions:
id-token: write
contents: write
outputs:
released: ${{ steps.release.outputs.released }}
newest_release_tag: ${{ steps.release.outputs.tag }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.head_ref || github.ref_name }}
# Do a dry run of PSR
- name: Test release
uses: python-semantic-release/python-semantic-release@v9.21.0
if: github.ref_name != 'main'
with:
root_options: --noop
# On main branch: actual PSR + upload to PyPI & GitHub
- name: Release
uses: python-semantic-release/python-semantic-release@v9.21.0
id: release
if: github.ref_name == 'main'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: steps.release.outputs.released == 'true'
- name: Publish package distributions to GitHub Releases
uses: python-semantic-release/upload-to-gh-release@main
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
build_wheels:
needs: [release]
if: needs.release.outputs.released == 'true'
name: Wheels for ${{ matrix.os }} (${{ matrix.musl == 'musllinux' && 'musllinux' || 'manylinux' }}) ${{ matrix.qemu }} ${{ matrix.pyver }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
[
windows-latest,
ubuntu-24.04-arm,
ubuntu-latest,
macos-13,
macos-latest,
]
qemu: [""]
musl: [""]
pyver: [""]
include:
- os: ubuntu-latest
musl: "musllinux"
- os: ubuntu-24.04-arm
musl: "musllinux"
# qemu is slow, make a single
# runner per Python version
- os: ubuntu-latest
qemu: armv7l
musl: "musllinux"
pyver: cp310
- os: ubuntu-latest
qemu: armv7l
musl: "musllinux"
pyver: cp311
- os: ubuntu-latest
qemu: armv7l
musl: "musllinux"
pyver: cp312
- os: ubuntu-latest
qemu: armv7l
musl: "musllinux"
pyver: cp313
# qemu is slow, make a single
# runner per Python version
- os: ubuntu-latest
qemu: armv7l
musl: ""
pyver: cp310
- os: ubuntu-latest
qemu: armv7l
musl: ""
pyver: cp311
- os: ubuntu-latest
qemu: armv7l
musl: ""
pyver: cp312
- os: ubuntu-latest
qemu: armv7l
musl: ""
pyver: cp313
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.newest_release_tag }}
fetch-depth: 0
# Used to host cibuildwheel
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Set up QEMU
if: ${{ matrix.qemu }}
uses: docker/setup-qemu-action@v3
with:
platforms: all
# This should be temporary
# xref https://github.com/docker/setup-qemu-action/issues/188
# xref https://github.com/tonistiigi/binfmt/issues/215
image: tonistiigi/binfmt:qemu-v8.1.5
id: qemu
- name: Prepare emulation
if: ${{ matrix.qemu }}
run: |
if [[ -n "${{ matrix.qemu }}" ]]; then
# Build emulated architectures only if QEMU is set,
# use default "auto" otherwise
echo "CIBW_ARCHS_LINUX=${{ matrix.qemu }}" >> $GITHUB_ENV
fi
- name: Limit to a specific Python version on slow QEMU
if: ${{ matrix.pyver }}
run: |
if [[ -n "${{ matrix.pyver }}" ]]; then
echo "CIBW_BUILD=${{ matrix.pyver }}*" >> $GITHUB_ENV
fi
- name: Build wheels
uses: pypa/cibuildwheel@v2.23.0
env:
CIBW_SKIP: cp36-* cp37-* cp38-* cp39-* pp* ${{ matrix.musl == 'musllinux' && '*manylinux*' || '*musllinux*' }}
REQUIRE_CYTHON: 1
- uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.os }}-${{ matrix.musl }}-${{ matrix.pyver }}-${{ matrix.qemu }}
path: ./wheelhouse/*.whl
upload_pypi:
needs: [build_wheels]
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
steps:
- uses: actions/download-artifact@v4
with:
# unpacks default artifact into dist/
# if `name: artifact` is omitted, the action will create extra parent dir
path: dist
pattern: wheels-*
merge-multiple: true
- uses:
pypa/gh-action-pypi-publish@v1.12.4
# To test: repository_url: https://test.pypi.org/legacy/
bluetooth-data-tools-1.26.1/.github/workflows/hacktoberfest.yml000066400000000000000000000005341476514471000246320ustar00rootroot00000000000000name: Hacktoberfest
on:
schedule:
# Run every day in October
- cron: "0 0 * 10 *"
# Run on the 1st of November to revert
- cron: "0 13 1 11 *"
jobs:
hacktoberfest:
runs-on: ubuntu-latest
steps:
- uses: browniebroke/hacktoberfest-labeler-action@v2.3.0
with:
github_token: ${{ secrets.GH_PAT }}
bluetooth-data-tools-1.26.1/.github/workflows/issue-manager.yml000066400000000000000000000013401476514471000245420ustar00rootroot00000000000000name: Issue Manager
on:
schedule:
- cron: "0 0 * * *"
issue_comment:
types:
- created
issues:
types:
- labeled
pull_request_target:
types:
- labeled
workflow_dispatch:
jobs:
issue-manager:
runs-on: ubuntu-latest
steps:
- uses: tiangolo/issue-manager@0.5.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >
{
"answered": {
"message": "Assuming the original issue was solved, it will be automatically closed now."
},
"waiting": {
"message": "Automatically closing. To re-open, please provide the additional information requested."
}
}
bluetooth-data-tools-1.26.1/.github/workflows/labels.yml000066400000000000000000000007741476514471000232560ustar00rootroot00000000000000name: Sync Github labels
on:
push:
branches:
- main
paths:
- ".github/**"
jobs:
labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install labels
run: pip install labels
- name: Sync config with Github
run: labels -u ${{ github.repository_owner }} -t ${{ secrets.GITHUB_TOKEN }} sync -f .github/labels.toml
bluetooth-data-tools-1.26.1/.gitignore000066400000000000000000000041231476514471000176540ustar00rootroot00000000000000# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# IDEA project files
.idea/
bluetooth-data-tools-1.26.1/.gitpod.yml000066400000000000000000000003061476514471000177520ustar00rootroot00000000000000tasks:
- command: |
pip install poetry
PIP_USER=false poetry install
- command: |
pip install pre-commit
pre-commit install
PIP_USER=false pre-commit install-hooks
bluetooth-data-tools-1.26.1/.pre-commit-config.yaml000066400000000000000000000026141476514471000221500ustar00rootroot00000000000000# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: "CHANGELOG.md"
default_stages: [pre-commit]
ci:
autofix_commit_msg: "chore(pre-commit.ci): auto fixes"
autoupdate_commit_msg: "chore(pre-commit.ci): pre-commit autoupdate"
repos:
- repo: https://github.com/commitizen-tools/commitizen
rev: v4.4.1
hooks:
- id: commitizen
stages: [commit-msg]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: debug-statements
- id: check-builtin-literals
- id: check-case-conflict
- id: check-docstring-first
- id: check-json
- id: check-toml
- id: check-xml
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
- id: debug-statements
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
args: ["--tab-width", "2"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.10
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.15.0
hooks:
- id: mypy
additional_dependencies: []
bluetooth-data-tools-1.26.1/.readthedocs.yml000066400000000000000000000010511476514471000207470ustar00rootroot00000000000000# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: "3.12"
jobs:
post_create_environment:
# Install poetry
- pip install poetry
post_install:
# Install dependencies
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs
# Build documentation in the docs directory with Sphinx
sphinx:
configuration: docs/conf.py
bluetooth-data-tools-1.26.1/CHANGELOG.md000066400000000000000000001072151476514471000175030ustar00rootroot00000000000000# CHANGELOG
## v1.26.1 (2025-03-15)
### Bug Fixes
- Increase size of parse_advertisement_data_tuple cache to 1024
([#124](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/124),
[`00076dc`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/00076dc4b3f51cdbfa15ef5b2a8a1dc420dccb36))
fixes #123
This one churns quite a bit and from how often its called, its a drain on performance having it so
small.
### Chores
- Update deps ([#125](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/125),
[`0e6492a`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/0e6492a35a3f5055b15167e15aa2e6f875fbfdc0))
- Updating coverage (7.6.10 -> 7.6.12) - Updating pytest (8.3.4 -> 8.3.5) - Updating cryptography
(44.0.1 -> 44.0.2)
- **pre-commit.ci**: Pre-commit autoupdate
([#120](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/120),
[`e45380e`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/e45380edd13e155372917a30362661e1b7686e65))
## v1.26.0 (2025-03-10)
### Features
- Reduce gap bytes slicing overhead
([#119](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/119),
[`577561c`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/577561c3318f17fc02e7cdd20935290181f1b0fd))
## v1.25.1 (2025-03-05)
### Bug Fixes
- Use trusted publishing for wheel uploads
([#118](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/118),
[`2bddddf`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/2bddddf888b9efe28b3ba25fefad10ccd5ff2dcb))
## v1.25.0 (2025-03-05)
### Features
- Reduce size of wheels ([#117](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/117),
[`cc720fc`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/cc720fcc0cf6a4b68bc46895396fd32628aa1d52))
## v1.24.1 (2025-03-05)
### Bug Fixes
- Ensure Python 3.10 wheels build
([#116](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/116),
[`91725a9`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/91725a9a74e3efd36f3091368179fe2cbf0b2e7d))
## v1.24.0 (2025-03-05)
### Chores
- Update release workflow for newer PSR
([#114](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/114),
[`93eabee`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/93eabee3d0a0f58be29b6486a181ab3237687147))
- **ci**: Bump python-semantic-release/python-semantic-release from 9.17.0 to 9.21.0 in the
github-actions group ([#113](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/113),
[`bbcc3b3`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/bbcc3b3347e18119fce258ec2d9508212ec30903))
- **pre-commit.ci**: Pre-commit autoupdate
([#111](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/111),
[`1a45dae`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/1a45dae9cd5bb10ced6edc306ce3f6d4274e1642))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#112](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/112),
[`dc9f71f`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/dc9f71fff7f478b48fe3f1fd10ea53cb245e7c96))
updates: - [github.com/commitizen-tools/commitizen: v4.2.1 →
v4.4.1](https://github.com/commitizen-tools/commitizen/compare/v4.2.1...v4.4.1) -
[github.com/astral-sh/ruff-pre-commit: v0.9.6 →
v0.9.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.6...v0.9.9)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Features
- Add new wheel builds ([#115](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/115),
[`37369e8`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/37369e8f3f70e3734ad5b1912c7cab4f0a1582b6))
* feat: optimize wheel builds
Reduce size of objects
* chore: armv7l wheels
## v1.23.4 (2025-02-04)
### Bug Fixes
- Update poetry to v2 + add license to metadata
([#110](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/110),
[`9cce169`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/9cce16967d35170770d8d62cb1c3d8dd66b36688))
### Chores
- **pre-commit.ci**: Pre-commit autoupdate
([#109](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/109),
[`2edb0ed`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/2edb0edcd5fb75d9e440edd8298fca2f09f2b97f))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
## v1.23.3 (2025-02-02)
### Bug Fixes
- Migrate to trusted publishing
([#108](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/108),
[`38023b0`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/38023b02ee67709007d5f513131de6948df3acf3))
## v1.23.2 (2025-02-02)
### Bug Fixes
- Missing wheels ([#107](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/107),
[`a7ace68`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/a7ace6842165e610610ebd65521548a5953bc5c4))
### Chores
- Fix CI badge ([#106](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/106),
[`cd32f09`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/cd32f09231d2263cafe2acf249b5e88b67394756))
## v1.23.1 (2025-02-02)
### Bug Fixes
- Adjust poetry groups to fix docs build
([#104](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/104),
[`b74c2bf`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/b74c2bffdc5278dc6751999ed39ec7e289cac15e))
### Build System
- **deps-dev**: Bump pytest-codspeed from 3.1.2 to 3.2.0
([#101](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/101),
[`9973926`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/9973926f18fa669b06e74a332939a66baeb0ce42))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
### Chores
- Bump some GHA deps ([#103](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/103),
[`85cc71f`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/85cc71f02c66f2d56c5dd3615fc95b3497736b98))
- Fix docs build ([#100](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/100),
[`4f0903e`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/4f0903e4a226823ce341793ed9a41f3b2635b0fa))
- Update dependabot.yml to include GHA
([`ef9f356`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/ef9f3564f198069b4e93ecba86abc4a6f2c9ec4f))
- **ci**: Bump the github-actions group across 1 directory with 5 updates
([#105](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/105),
[`268c0f4`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/268c0f46c7bd6cbfd42b748dbca27fa1817d1cc0))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## v1.23.0 (2025-02-02)
### Chores
- Bump upload/download actions to v4
([#97](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/97),
[`82e5d05`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/82e5d0563cb4c7b407e5db5dc82bee864c052549))
- **pre-commit.ci**: Pre-commit autoupdate
([#96](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/96),
[`55b134e`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/55b134e587ffbd26c57d7a1637059d01d5f8a1f5))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#98](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/98),
[`3a6e50c`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/3a6e50ca688a2576ab897c7bc46331cfe6aed387))
### Features
- Improve cache performance
([#99](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/99),
[`0dcf7ff`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/0dcf7ffba7c278bf10453c92464d8b0adcfd09d5))
## v1.22.0 (2025-01-17)
### Features
- Migrate benchmarks to use Python 3.13
([#95](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/95),
[`949684e`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/949684ec43507bac7f5513c636cf7bb58222d957))
## v1.21.0 (2025-01-17)
### Build System
- **deps**: Bump cryptography from 43.0.0 to 43.0.1
([#69](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/69),
[`5dec99c`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/5dec99cad1c56d7db6cf5fc489a0a000b27bd303))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump cryptography from 43.0.1 to 43.0.3 in the pip group
([#73](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/73),
[`dc234ae`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/dc234ae8da9ad1eff8671c858954e7af49d7d12a))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump cryptography from 43.0.3 to 44.0.0
([#86](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/86),
[`5e2b7c8`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/5e2b7c80f933e3cceb45220c923a4e13b95aa3da))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump jinja2 from 3.1.4 to 3.1.5 in the pip group
([#88](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/88),
[`e1fc660`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/e1fc660567f8988cbef6530eb25c928cff7b8c0a))
- **deps**: Bump myst-parser from 1.0.0 to 3.0.1
([#74](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/74),
[`02fa6ae`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/02fa6aed518c1b6a1ecc51c14eaf834ef5d9bb37))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump myst-parser from 3.0.1 to 4.0.0
([#89](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/89),
[`b2cf3d4`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/b2cf3d4c04a7c6fff74b89aedb7231b80825eb95))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump sphinx from 5.3.0 to 6.2.1
([#65](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/65),
[`8fdc7ca`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/8fdc7cade9c3b87dfdd77cb741d1fd83fbc154ea))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump sphinx from 6.2.1 to 7.4.7
([#87](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/87),
[`ba46d70`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/ba46d700817d7fffa9c3bbc6a83593a99bc55c29))
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 6.2.1 to 7.4.7. - [Release
notes](https://github.com/sphinx-doc/sphinx/releases) -
[Changelog](https://github.com/sphinx-doc/sphinx/blob/v7.4.7/CHANGES.rst) -
[Commits](https://github.com/sphinx-doc/sphinx/compare/v6.2.1...v7.4.7)
--- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production
update-type: version-update:semver-major ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump sphinx from 7.4.7 to 8.1.3
([#92](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/92),
[`2f563b2`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/2f563b236150097c94670a83c8b4e598d7d39e43))
- **deps**: Bump sphinx-rtd-theme from 1.2.0 to 2.0.0
([#58](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/58),
[`e9c06db`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/e9c06db989f1a49664e83dd9fd73734e2df9904c))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump sphinx-rtd-theme from 2.0.0 to 3.0.1
([#75](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/75),
[`3ab4688`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/3ab468886fc9405ba78958a28cc6d40f2cb37f93))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump sphinx-rtd-theme from 3.0.1 to 3.0.2
([#81](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/81),
[`17c43a6`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/17c43a69a58e52a270f01cfc9ecba90aa24ed5c6))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump pytest from 8.3.2 to 8.3.3
([#71](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/71),
[`d0d69a5`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/d0d69a519aeb8608f6aee02b61a75fd070c852a3))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump pytest from 8.3.3 to 8.3.4
([#84](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/84),
[`93a3bdf`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/93a3bdfa8695db95784f7d8addac06ac657f9c75))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump pytest-benchmark from 4.0.0 to 5.1.0
([#80](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/80),
[`733077b`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/733077bcc040346e531a1e5e01eeed331a602426))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump pytest-cov from 5.0.0 to 6.0.0
([#79](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/79),
[`0dc38f7`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/0dc38f793247f326a16cee588034f872c7a3f95f))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
### Chores
- Add codspeed benchmarks ([#91](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/91),
[`05fa2c7`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/05fa2c7a3fc29eb12e1b44d110470441f57ca738))
- **pre-commit.ci**: Pre-commit autoupdate
([#66](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/66),
[`7b041ce`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/7b041ce4c82fa4545ea61a8f761d6facf8a232a0))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#67](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/67),
[`fd49b0f`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/fd49b0fa4ef7918787b70a2d8b1191d5b879e1ea))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#70](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/70),
[`5b0c528`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/5b0c528118f4d3984f6a3cfa7e6a15135d027b82))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#72](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/72),
[`da219f4`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/da219f4eac7b20bc20e5d57b12e2a4eb3d95fb91))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#76](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/76),
[`1da6085`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/1da608594010fd5c0e923fef8167e1d9d60e08ab))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#78](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/78),
[`0ca9d53`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/0ca9d5330f4da6ef8afdbeb23756f374f4567ea8))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#82](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/82),
[`9d1907e`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/9d1907e09285e0654f55071ace3c3a0b08f464ae))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#83](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/83),
[`9433456`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/9433456c4b2715610769252d72ba02e07b9a0aca))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#85](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/85),
[`7c1eb08`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/7c1eb08ddf65acaf2675d2c277a3c0544d634ff3))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#90](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/90),
[`dbdd4a3`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/dbdd4a3e9544891e29a02d545e5ff1f5870f7952))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#93](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/93),
[`8f4e202`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/8f4e20288fd1e76681ae9e6a1bb12c87baaef8ae))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Features
- Add aarch64 wheels ([#94](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/94),
[`88d134b`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/88d134b1ac5377c515f613d5481cc35c969493e0))
## v1.20.0 (2024-08-24)
### Build System
- **deps**: Bump cryptography from 41.0.3 to 43.0.0
([#61](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/61),
[`97317cc`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/97317ccbca8d7c44db8c13f0375e883b74de2685))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump myst-parser from 0.18.1 to 1.0.0
([#60](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/60),
[`aa46cee`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/aa46ceef5d365c658079e06db31f90fc45f8030f))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump pytest from 7.3.1 to 8.3.2
([#59](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/59),
[`950f182`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/950f1828e3e545b02576774f2093b602e45ca6b2))
- **deps-dev**: Bump pytest-cov from 3.0.0 to 5.0.0
([#62](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/62),
[`4b2d18b`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/4b2d18b5a2206f61328c78c15693c21df1404429))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
### Chores
- Bump deps for py3.13 ([#64](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/64),
[`9cb11ad`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/9cb11adbaa37030c4853dc426511dbe395a3410b))
- Create dependabot.yml
([`f292e9a`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/f292e9a1d0162e797790c9033d69849faeb6f7fd))
- **pre-commit.ci**: Pre-commit autoupdate
([#54](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/54),
[`8931368`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/8931368a38cd4e79b3c2bccbc82d6451f0690250))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#55](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/55),
[`66b7913`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/66b79139325af943bcef6978c10566fd673eef1c))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#56](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/56),
[`e8162e3`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/e8162e361c0a1822484205eb9b34a383552d33e4))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Features
- Python 3.13 support ([#57](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/57),
[`468014e`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/468014ee5e69253eb4534013ee8cebf1ffe06c70))
## v1.19.4 (2024-07-29)
### Bug Fixes
- Speed up int_to_bluetooth_address C implementation
([#52](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/52),
[`7d46575`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/7d4657515cd313aff84fee17bd027d092bd3ae3c))
### Chores
- Fix CI ([#50](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/50),
[`434460c`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/434460ca1cbb379b0000c6f88b546ac4659df1bd))
- Fix commitlint configuration
([#51](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/51),
[`7a68c25`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/7a68c2534b59a8c8bf9c2bbb40ddbbc125f69cc9))
- Make benchmarks runnable in CI
([#49](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/49),
[`daac28d`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/daac28de645075625410138988832c8d0f644aea))
- Remove IDEA configuration from repository
([#47](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/47),
[`3dbabf7`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/3dbabf76a0c9d07ef7baceb770f8152e8d958ece))
- Switch linting & formatting to Ruff
([#48](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/48),
[`3ae1bcd`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/3ae1bcda5e65801bdadc87ab9179fbac9ab74520))
- **pre-commit.ci**: Pre-commit autoupdate
([#45](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/45),
[`9a4466d`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/9a4466da63f6c8377b9ed6ccbc664813d9ff925a))
* chore(pre-commit.ci): pre-commit autoupdate
updates: - [github.com/commitizen-tools/commitizen: v2.28.0 →
v3.27.0](https://github.com/commitizen-tools/commitizen/compare/v2.28.0...v3.27.0) -
[github.com/pre-commit/pre-commit-hooks: v4.3.0 →
v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.6.0) -
[github.com/pre-commit/mirrors-prettier: v2.7.1 →
v4.0.0-alpha.8](https://github.com/pre-commit/mirrors-prettier/compare/v2.7.1...v4.0.0-alpha.8) -
[github.com/asottile/pyupgrade: v2.37.1 →
v3.16.0](https://github.com/asottile/pyupgrade/compare/v2.37.1...v3.16.0) -
[github.com/PyCQA/isort: 5.12.0 → 5.13.2](https://github.com/PyCQA/isort/compare/5.12.0...5.13.2)
- [github.com/psf/black: 22.6.0 → 24.4.2](https://github.com/psf/black/compare/22.6.0...24.4.2) -
[github.com/codespell-project/codespell: v2.1.0 →
v2.3.0](https://github.com/codespell-project/codespell/compare/v2.1.0...v2.3.0) -
[github.com/PyCQA/flake8: 4.0.1 → 7.1.0](https://github.com/PyCQA/flake8/compare/4.0.1...7.1.0) -
[github.com/pre-commit/mirrors-mypy: v0.931 →
v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v0.931...v1.10.1) -
[github.com/PyCQA/bandit: 1.7.4 → 1.7.9](https://github.com/PyCQA/bandit/compare/1.7.4...1.7.9)
* chore(pre-commit.ci): auto fixes
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#46](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/46),
[`8995d2e`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/8995d2e550ffd3ade458b2b144fed7fa7b1dfc18))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
## v1.19.3 (2024-06-24)
### Bug Fixes
- Wheel builds on macos ([#44](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/44),
[`07f423b`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/07f423bc4ce88a357e7a06b5b48fb36dfc8b916a))
## v1.19.2 (2024-06-24)
### Bug Fixes
- Build cibuildwheel ([#43](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/43),
[`fc8c1a1`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/fc8c1a182b189f115a687173abc20e2fe1ba2219))
## v1.19.1 (2024-06-24)
### Bug Fixes
- Fix license classifier ([#42](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/42),
[`71d54c3`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/71d54c39e62413194a1ba65d2fe397603788e042))
## v1.19.0 (2023-12-21)
### Features
- Speed up to gap parser ([#41](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/41),
[`595c65a`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/595c65ac0fdaac27d286c1e893abe28c8b6bfe52))
## v1.18.0 (2023-12-13)
### Features
- Add mac_to_int util ([#40](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/40),
[`57aebfb`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/57aebfbcdf83754c8324890908d401718818d391))
## v1.17.0 (2023-12-03)
### Features
- Speed up int_to_bluetooth_address
([#39](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/39),
[`ac354ae`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/ac354ae68b7afedfe5242ec50a209a6241d33169))
## v1.16.0 (2023-12-01)
### Features
- Add cython monotonic_time_coarse implementation
([#38](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/38),
[`ae3abb8`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/ae3abb88023a998be5e4b75b32f0882ebb18dfd9))
## v1.15.0 (2023-11-24)
### Chores
- Add more benchmarks ([#36](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/36),
[`0a2df27`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/0a2df279e89d0dfe381d4188321819074622f315))
### Features
- Improve performance of gap parser
([#37](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/37),
[`05ea718`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/05ea718dcdf0a1d848d0eb98236439f8f0be07cf))
## v1.14.0 (2023-11-05)
### Features
- Speed up gap parser with a memory view
([#35](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/35),
[`35e132f`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/35e132f30b93e964c22806c54c5d19a23fc97383))
## v1.13.0 (2023-10-18)
### Bug Fixes
- Reduce size of wheels by excluding generated .c files
([#34](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/34),
[`1b56b6e`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/1b56b6e3f1579e9abb0b1d2aaf57fec39c8fc10b))
### Chores
- Add tests for esphome issue 4838
([#32](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/32),
[`ddf0e58`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/ddf0e583215f79abefb23a52e42a61b08608d879))
### Features
- Update cibuildwheel to build on final cpython release
([#33](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/33),
[`46781c1`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/46781c1b41eec31c5047e76a843077a3b02d6dca))
## v1.12.0 (2023-09-24)
### Features
- Small speedups to the gap parser
([#30](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/30),
[`87c0fcc`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/87c0fccacde8da59b2b046fcd2e94a83f30fc364))
## v1.11.0 (2023-09-01)
### Features
- Add helper for resolving a private address using an identity key
([#29](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/29),
[`b5e13cc`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/b5e13cc44711d4ef4aa306bc6e06666afde9968c))
## v1.10.0 (2023-09-01)
### Features
- Add calculate_distance_meters to estimate distance to a bluetooth device
([#28](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/28),
[`c6f0150`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/c6f0150f48d73e603bf69642e7cb7b03375f8393))
## v1.9.1 (2023-08-27)
### Bug Fixes
- Rebuild wheels with cython 3.0.2
([#27](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/27),
[`4634dfb`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/4634dfb8f47ab42787afecc596e864bf3ac11cbe))
## v1.9.0 (2023-08-23)
### Features
- Speed up the new parse_advertisement_data_tuple function
([#26](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/26),
[`1137a50`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/1137a5004a1e2a65ee0d37ab555d6ce300208237))
## v1.8.0 (2023-08-10)
### Features
- Make returned data from parse_advertisement_data readonly
([#25](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/25),
[`1a07397`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/1a073972bfc7dff35b4d8cc2d7394c4ad15f1109))
## v1.7.0 (2023-08-05)
### Build System
- Remove wheel from list of build dependencies
([#23](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/23),
[`5512c3d`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/5512c3d16947c4418c544977ae46fc69810b1b64))
### Features
- Remove the need to have a cpp compiler installed
([#24](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/24),
[`2a7ebac`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/2a7ebac86872407c7802a847b92ee739747cceaa))
## v1.6.1 (2023-07-24)
### Bug Fixes
- Pin python-semantic-release to fix release process
([#22](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/22),
[`957ad28`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/957ad28f576c33d075f1f72875de118d5ef8fd4c))
## v1.6.0 (2023-07-13)
### Features
- Improve performance when data is all unique
([#21](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/21),
[`60bff4b`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/60bff4b6f2da3cadce4305280fffc232a683122c))
## v1.5.0 (2023-07-13)
### Features
- Avoid tuple copy if data is already a tuple
([#20](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/20),
[`69829ba`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/69829bae919245bd50132affb4b7718e6dffae1d))
## v1.4.0 (2023-07-13)
### Features
- Cache overall parse ([#19](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/19),
[`5983718`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/5983718f6228e9f4428fa7843df4e37e3a7527bf))
## v1.3.0 (2023-06-29)
### Features
- Improve handling of corrupt data
([#18](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/18),
[`b70fdd4`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/b70fdd45afc295b7082d8fae019e28f357356bf0))
## v1.2.0 (2023-06-15)
### Chores
- Add benchmark ([#14](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/14),
[`46e68f4`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/46e68f427af4ee8e5b366543667814dadfd59092))
- Bump deps to fix CI ([#17](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/17),
[`fa2e104`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/fa2e10405b4692bbeff4926744a2fdc27322beb0))
### Features
- Optimize gap parser ([#13](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/13),
[`7df2658`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/7df26580c06cf38e2621e16a9a17a3fafb6978e4))
- Optimize gap parser ([#15](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/15),
[`c598c2d`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/c598c2ddc106da657bfec30864a65c2e2a36c5f3))
- Optimize gap parser ([#16](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/16),
[`5800d45`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/5800d4531400ee96f95cd4ee82677a4f32e23182))
## v1.1.0 (2023-06-14)
### Features
- Reduce string conversion overhead for bluetooth addresses
([#12](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/12),
[`558c93f`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/558c93f28ffcc205df4a34be0de963fbaeddfafe))
## v1.0.0 (2023-06-07)
### Features
- Speed up parsing advertisement data
([#11](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/11),
[`47e2519`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/47e251928d5d03d7978cd82f9a6173f98d0cbb68))
BREAKING CHANGE: The decode_advertisement_data function is no longer exposed
It is likely nobody was using it since it is internals for parse_advertisement_data, but it was
exposed. If this is a problem for you, please open an issue.
### Breaking Changes
- The decode_advertisement_data function is no longer exposed
## v0.4.0 (2023-04-15)
### Features
- Add cython implementation
([#10](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/10),
[`7fd349d`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/7fd349d0dd83bbcb51ade87ee8dc94fa2db67742))
## v0.3.1 (2022-12-19)
### Bug Fixes
- Handle zero padding in adv data
([#9](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/9),
[`65fb26b`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/65fb26b5197d6cf1bd262eab98d52b159f89db9f))
## v0.3.0 (2022-11-13)
### Chores
- Add python 3.11 to the CI ([#8](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/8),
[`07f114d`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/07f114df1b5f5f665001df8b681421dcf7acab1a))
- Bump python-semantic-release
([#7](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/7),
[`2b4fd66`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/2b4fd668f12ac6ef4d40e0347da623e587e2058a))
### Features
- Add gap parser ([#6](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/6),
[`dcb1d86`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/dcb1d86a15e9387385128ebaf32498d4af268963))
## v0.2.0 (2022-10-27)
### Features
- Add human_readable_name function
([#5](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/5),
[`bb408cd`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/bb408cddb5f043314ec802c8b6a8a306c84fa2a3))
## v0.1.2 (2022-08-13)
### Bug Fixes
- Ci release process ([#4](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/4),
[`1726d16`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/1726d1617ad0fceda2dee3945f12bf43768a3fe2))
## v0.1.1 (2022-08-12)
### Bug Fixes
- Release process ([#3](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/3),
[`0a2f45b`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/0a2f45b7cba9fe71f20e2030b7712d13c330072c))
## v0.1.0 (2022-08-12)
### Chores
- Initial commit
([`52bb085`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/52bb0853ec9fbcc9fa6c357ff88aeb2778ccbf28))
### Features
- Add short_address ([#2](https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/2),
[`f6eade3`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/f6eade36a4dab779d8b17a10971932ffa41f2501))
- Init repo
([`7a24a2d`](https://github.com/Bluetooth-Devices/bluetooth-data-tools/commit/7a24a2d3cc7319c85250a747fb91985e3ec3207c))
bluetooth-data-tools-1.26.1/CONTRIBUTING.md000066400000000000000000000074631476514471000201270ustar00rootroot00000000000000# Contributing
Contributions are welcome, and they are greatly appreciated! Every little helps, and credit will always be given.
You can contribute in many ways:
## Types of Contributions
### Report Bugs
Report bugs to [our issue page][gh-issues]. If you are reporting a bug, please include:
- Your operating system name and version.
- Any details about your local setup that might be helpful in troubleshooting.
- Detailed steps to reproduce the bug.
### Fix Bugs
Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement it.
### Implement Features
Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it.
### Write Documentation
Bluetooth Data Tools could always use more documentation, whether as part of the official Bluetooth Data Tools docs, in docstrings, or even on the web in blog posts, articles, and such.
### Submit Feedback
The best way to send feedback [our issue page][gh-issues] on GitHub. If you are proposing a feature:
- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that this is a volunteer-driven project, and that contributions are welcome 😊
## Get Started!
Ready to contribute? Here's how to set yourself up for local development.
1. Fork the repo on GitHub.
2. Clone your fork locally:
```shell
$ git clone git@github.com:your_name_here/bluetooth-data-tools.git
```
3. Install the project dependencies with [Poetry](https://python-poetry.org):
```shell
$ poetry install
```
4. Create a branch for local development:
```shell
$ git checkout -b name-of-your-bugfix-or-feature
```
Now you can make your changes locally.
5. When you're done making changes, check that your changes pass our tests:
```shell
$ poetry run pytest
```
6. Linting is done through [pre-commit](https://pre-commit.com). Provided you have the tool installed globally, you can run them all as one-off:
```shell
$ pre-commit run -a
```
Or better, install the hooks once and have them run automatically each time you commit:
```shell
$ pre-commit install
```
7. Commit your changes and push your branch to GitHub:
```shell
$ git add .
$ git commit -m "feat(something): your detailed description of your changes"
$ git push origin name-of-your-bugfix-or-feature
```
Note: the commit message should follow [the conventional commits](https://www.conventionalcommits.org). We run [`commitlint` on CI](https://github.com/marketplace/actions/commit-linter) to validate it, and if you've installed pre-commit hooks at the previous step, the message will be checked at commit time.
8. Submit a pull request through the GitHub website or using the GitHub CLI (if you have it installed):
```shell
$ gh pr create --fill
```
## Pull Request Guidelines
We like to have the pull request open as soon as possible, that's a great place to discuss any piece of work, even unfinished. You can use draft pull request if it's still a work in progress. Here are a few guidelines to follow:
1. Include tests for feature or bug fixes.
2. Update the documentation for significant features.
3. Ensure tests are passing on CI.
## Tips
To run a subset of tests:
```shell
$ pytest tests
```
## Making a new release
The deployment should be automated and can be triggered from the Semantic Release workflow in GitHub. The next version will be based on [the commit logs](https://python-semantic-release.readthedocs.io/en/latest/commit-log-parsing.html#commit-log-parsing). This is done by [python-semantic-release](https://python-semantic-release.readthedocs.io/en/latest/index.html) via a GitHub action.
[gh-issues]: https://github.com/bdraco/bluetooth-data-tools/issues
bluetooth-data-tools-1.26.1/LICENSE000066400000000000000000000261211476514471000166730ustar00rootroot00000000000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2022 J. Nick Koston
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
bluetooth-data-tools-1.26.1/README.md000066400000000000000000000074161476514471000171530ustar00rootroot00000000000000# Bluetooth Data Tools
Tools for converting bluetooth data and packets
## Installation
Install this via pip (or your favourite package manager):
`pip install bluetooth-data-tools`
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
## Credits
This package was created with
[Cookiecutter](https://github.com/audreyr/cookiecutter) and the
[browniebroke/cookiecutter-pypackage](https://github.com/browniebroke/cookiecutter-pypackage)
project template.
bluetooth-data-tools-1.26.1/bench/000077500000000000000000000000001476514471000167435ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/bench/__init__.py000066400000000000000000000000001476514471000210420ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/bench/test_int_to_bluetooth_address.py000066400000000000000000000005131476514471000254410ustar00rootroot00000000000000from bluetooth_data_tools.utils import (
_int_to_bluetooth_address,
int_to_bluetooth_address,
)
def test_parse_int_to_bluetooth_address_uncached(benchmark):
benchmark(lambda: _int_to_bluetooth_address(0))
def test_parse_int_to_bluetooth_address_cached(benchmark):
benchmark(lambda: int_to_bluetooth_address(0))
bluetooth-data-tools-1.26.1/bench/test_parse_gap.py000066400000000000000000000130451476514471000223200ustar00rootroot00000000000000from bluetooth_data_tools import parse_advertisement_data
# cythonize -X language_level=3 -a -i src/bluetooth_data_tools/gap.py
advs = [
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33163\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x1a\x02\n\x07\n\xffL\x00\x10\x05\x1f\x1c8#\xe2",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B4\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80n\xe0\x9d\x13\x99\x0f\xf3\xe2\x9d\x13\x99\x0f\xf2\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x1a\x02\n\x06\x11\xffL\x00\x0f\x08\xd0\n\x7fnt\x00\x08\x0c\x10\x02#\x04",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x13\xc0\xa8k\xef",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b'\x02\x01\x04\x03\x03\x07\xfe\x10\xff\xa7\x05\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02"\x00\xca\x03\x19\x00\x00\x02\n\x00',
b'\x02\x01\x06\x16\xffL\x00\x061\x00\xcdnI\xa1\x91\xb2\x06\x007\x00\x04\x02\xcb\xd7,"\x04\x08LF0',
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x13\xc0\xa8k\xef",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x02\x01\x06\x05\tRZSS",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x1e\xff\x01ZR\x02\xb4\xe8B\xdb\xcf\xd4\x00\x97\x0c\x03\x01\x02\x03\x04\x05\x06\x07\x08\t\xa1\xa2\xa3\xa4\xa5\xa6",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F331F5\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\t\xffY\x00\xd8.\xad\xcd\r\x85",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33231\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03+\xc0\xa8kZ",
b"\x1b\xffu\x00B\x04\x01\x80n\xe0\x9d\x13\x99\x0f\xf3\xe2\x9d\x13\x99\x0f\xf2\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x05\tRZSS",
b"\x1b\xffu\x00B\x04\x01\x80\xa0\xf8\x04.\xe1\x9f\x19\xfa\x04.\xe1\x9f\x18\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33105\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x16\xffL\x00\x061\x00\x8aO\xa3\xed?\x1e\x05\x00H\x04\x01\x02\xd8E\xf3\xc6\x04\x08Nan",
b"\x02\x01\x1a\x02\n\x06\x0e\xffL\x00\x0f\x05\x90\x00QB\n\x10\x02#\x04",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33231\x03\x19\xc1\x03",
b"\x02\x01\x06\t\xffY\x00\xc5-^\xabRv",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B4\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x1a\x02\n\x06\x0e\xffL\x00\x0f\x05\x90\x00QB\n\x10\x02#\x04",
b"\x02\x01\x06\x16\xffL\x00\x061\x00\x8aO\xa3\xed?\x1e\x05\x00H\x04\x01\x02\xd8E\xf3\xc6\x04\x08Nan",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x89\xc0\xa8j(",
b"\x02\x01\x06\t\xffY\x00\xc5-^\xabRv",
b"\x02\x01\x06\x03\x02$\xfe\x04\xff\xd1\x01\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x02$\xfe\x04\xff\xd1\x01\x00",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b'\x02\x01\x02\x10\xff\xa7\x05\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02"\x00\xca\x02\n\t\x03\x03\x07\xfe',
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x1a\x02\n\x0c\n\xffL\x00\x10\x05\x1b\x1c\x03\xc5\xbe",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F331F5\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33163\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03+\xc0\xa8kZ",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x1a\x02\n\x0c\n\xffL\x00\x10\x05\x1b\x1c\x03\xc5\xbe",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
]
def test_parse_advertisement_data(benchmark):
benchmark(lambda: parse_advertisement_data(advs))
bluetooth-data-tools-1.26.1/bench/test_parse_gap_tuple.py000066400000000000000000000133761476514471000235400ustar00rootroot00000000000000from bluetooth_data_tools import parse_advertisement_data_tuple
from bluetooth_data_tools.gap import _uncached_parse_advertisement_data
# cythonize -X language_level=3 -a -i src/bluetooth_data_tools/gap.py
advs = (
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33163\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x1a\x02\n\x07\n\xffL\x00\x10\x05\x1f\x1c8#\xe2",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B4\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80n\xe0\x9d\x13\x99\x0f\xf3\xe2\x9d\x13\x99\x0f\xf2\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x1a\x02\n\x06\x11\xffL\x00\x0f\x08\xd0\n\x7fnt\x00\x08\x0c\x10\x02#\x04",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x13\xc0\xa8k\xef",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b'\x02\x01\x04\x03\x03\x07\xfe\x10\xff\xa7\x05\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02"\x00\xca\x03\x19\x00\x00\x02\n\x00',
b'\x02\x01\x06\x16\xffL\x00\x061\x00\xcdnI\xa1\x91\xb2\x06\x007\x00\x04\x02\xcb\xd7,"\x04\x08LF0',
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x13\xc0\xa8k\xef",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x02\x01\x06\x05\tRZSS",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x1e\xff\x01ZR\x02\xb4\xe8B\xdb\xcf\xd4\x00\x97\x0c\x03\x01\x02\x03\x04\x05\x06\x07\x08\t\xa1\xa2\xa3\xa4\xa5\xa6",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F331F5\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\t\xffY\x00\xd8.\xad\xcd\r\x85",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33231\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03+\xc0\xa8kZ",
b"\x1b\xffu\x00B\x04\x01\x80n\xe0\x9d\x13\x99\x0f\xf3\xe2\x9d\x13\x99\x0f\xf2\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x05\tRZSS",
b"\x1b\xffu\x00B\x04\x01\x80\xa0\xf8\x04.\xe1\x9f\x19\xfa\x04.\xe1\x9f\x18\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33105\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x16\xffL\x00\x061\x00\x8aO\xa3\xed?\x1e\x05\x00H\x04\x01\x02\xd8E\xf3\xc6\x04\x08Nan",
b"\x02\x01\x1a\x02\n\x06\x0e\xffL\x00\x0f\x05\x90\x00QB\n\x10\x02#\x04",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33231\x03\x19\xc1\x03",
b"\x02\x01\x06\t\xffY\x00\xc5-^\xabRv",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B4\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x1a\x02\n\x06\x0e\xffL\x00\x0f\x05\x90\x00QB\n\x10\x02#\x04",
b"\x02\x01\x06\x16\xffL\x00\x061\x00\x8aO\xa3\xed?\x1e\x05\x00H\x04\x01\x02\xd8E\xf3\xc6\x04\x08Nan",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x89\xc0\xa8j(",
b"\x02\x01\x06\t\xffY\x00\xc5-^\xabRv",
b"\x02\x01\x06\x03\x02$\xfe\x04\xff\xd1\x01\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x02$\xfe\x04\xff\xd1\x01\x00",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b'\x02\x01\x02\x10\xff\xa7\x05\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02"\x00\xca\x02\n\t\x03\x03\x07\xfe',
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x1a\x02\n\x0c\n\xffL\x00\x10\x05\x1b\x1c\x03\xc5\xbe",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F331F5\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33163\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03+\xc0\xa8kZ",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x1a\x02\n\x0c\n\xffL\x00\x10\x05\x1b\x1c\x03\xc5\xbe",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
)
def test_parse_advertisement_data_tuple(benchmark):
benchmark(lambda: parse_advertisement_data_tuple(advs))
def test_parse_advertisement_data_tuple_uncached(benchmark):
benchmark(lambda: _uncached_parse_advertisement_data(advs))
bluetooth-data-tools-1.26.1/build_ext.py000066400000000000000000000036161476514471000202230ustar00rootroot00000000000000"""Build optional cython modules."""
import os
from distutils.command.build_ext import build_ext
from os.path import join
from typing import Any
try:
from setuptools import Extension
except ImportError:
from distutils.core import Extension
utils_module = Extension(
"bluetooth_data_tools._utils_impl",
[
join("src", "bluetooth_data_tools", "_utils_impl.pyx"),
],
language="c",
extra_compile_args=["-O3", "-g0"],
)
time_module = Extension(
"bluetooth_data_tools._time_impl",
[
join("src", "bluetooth_data_tools", "_time_impl.pyx"),
],
language="c",
extra_compile_args=["-O3", "-g0"],
)
TO_CYTHONIZE = [
"src/bluetooth_data_tools/gap.py",
"src/bluetooth_data_tools/utils.py",
]
EXTENSIONS = [
Extension(
ext.removeprefix("src/").removesuffix(".py").replace("/", "."),
[ext],
language="c",
extra_compile_args=["-O3", "-g0"],
)
for ext in TO_CYTHONIZE
]
class BuildExt(build_ext):
def build_extensions(self) -> None:
try:
super().build_extensions()
except Exception: # noqa: S110
pass
def build(setup_kwargs: Any) -> None:
if os.environ.get("SKIP_CYTHON", False):
return
try:
from Cython.Build import cythonize
setup_kwargs.update(
dict(
ext_modules=cythonize(
[
time_module,
utils_module,
*EXTENSIONS,
],
compiler_directives={"language_level": "3"}, # Python 3
),
cmdclass=dict(build_ext=BuildExt),
)
)
setup_kwargs["exclude_package_data"] = {
pkg: ["*.c"] for pkg in setup_kwargs["packages"]
}
except Exception:
if os.environ.get("REQUIRE_CYTHON"):
raise
pass
bluetooth-data-tools-1.26.1/commitlint.config.mjs000066400000000000000000000003621476514471000220230ustar00rootroot00000000000000export default {
extends: ["@commitlint/config-conventional"],
rules: {
"header-max-length": [0, "always", Infinity],
"body-max-line-length": [0, "always", Infinity],
"footer-max-line-length": [0, "always", Infinity],
},
};
bluetooth-data-tools-1.26.1/docs/000077500000000000000000000000001476514471000166145ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/docs/Makefile000066400000000000000000000013721476514471000202570ustar00rootroot00000000000000# 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 = .
BUILDDIR = _build
.PHONY: help livehtml Makefile
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
# Build, watch and serve docs with live reload
livehtml:
sphinx-autobuild -b html -c . $(SOURCEDIR) $(BUILDDIR)/html
# 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)
bluetooth-data-tools-1.26.1/docs/_static/000077500000000000000000000000001476514471000202425ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/docs/_static/.gitkeep000066400000000000000000000000001476514471000216610ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/docs/changelog.md000066400000000000000000000000601476514471000210610ustar00rootroot00000000000000(changelog)=
```{include} ../CHANGELOG.md
```
bluetooth-data-tools-1.26.1/docs/conf.py000066400000000000000000000012311476514471000201100ustar00rootroot00000000000000# 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
# Project information
project = "bluetooth-data-tools"
copyright = "2023, J. Nick Koston"
author = "J. Nick Koston"
release = "1.26.1"
# General configuration
extensions = [
"myst_parser",
]
# The suffix of source filenames.
source_suffix = [
".rst",
".md",
]
templates_path = [
"_templates",
]
exclude_patterns = [
"_build",
"Thumbs.db",
".DS_Store",
]
# Options for HTML output
html_theme = "furo"
html_static_path = ["_static"]
bluetooth-data-tools-1.26.1/docs/contributing.md000066400000000000000000000000661476514471000216470ustar00rootroot00000000000000(contributing)=
```{include} ../CONTRIBUTING.md
```
bluetooth-data-tools-1.26.1/docs/index.md000066400000000000000000000003611476514471000202450ustar00rootroot00000000000000# Welcome to bluetooth-data-tools documentation!
```{toctree}
:caption: Installation & Usage
:maxdepth: 2
installation
usage
```
```{toctree}
:caption: Project Info
:maxdepth: 2
changelog
contributing
```
```{include} ../README.md
```
bluetooth-data-tools-1.26.1/docs/installation.md000066400000000000000000000004371476514471000216430ustar00rootroot00000000000000(installation)=
# Installation
The package is published on [PyPI](https://pypi.org/project/bluetooth-data-tools/) and can be installed with `pip` (or any equivalent):
```bash
pip install bluetooth-data-tools
```
Next, see the {ref}`section about usage ` to see how to use it.
bluetooth-data-tools-1.26.1/docs/make.bat000066400000000000000000000013751476514471000202270ustar00rootroot00000000000000@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
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
bluetooth-data-tools-1.26.1/docs/usage.md000066400000000000000000000003371476514471000202450ustar00rootroot00000000000000(usage)=
# Usage
Assuming that you've followed the {ref}`installations steps `, you're now ready to use this package.
Start by importing it:
```python
import bluetooth_data_tools
```
TODO: Document usage
bluetooth-data-tools-1.26.1/poetry.lock000066400000000000000000003566161476514471000201010ustar00rootroot00000000000000# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
[[package]]
name = "alabaster"
version = "1.0.0"
description = "A light, configurable Sphinx theme"
optional = false
python-versions = ">=3.10"
groups = ["main", "docs"]
files = [
{file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"},
{file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"},
]
[[package]]
name = "anyio"
version = "4.8.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"},
{file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"},
]
[package.dependencies]
exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
idna = ">=2.8"
sniffio = ">=1.1"
typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras]
doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"]
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"]
trio = ["trio (>=0.26.1)"]
[[package]]
name = "babel"
version = "2.17.0"
description = "Internationalization utilities"
optional = false
python-versions = ">=3.8"
groups = ["main", "docs"]
files = [
{file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"},
{file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"},
]
[package.extras]
dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"]
[[package]]
name = "beautifulsoup4"
version = "4.13.3"
description = "Screen-scraping library"
optional = false
python-versions = ">=3.7.0"
groups = ["docs"]
files = [
{file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"},
{file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"},
]
[package.dependencies]
soupsieve = ">1.2"
typing-extensions = ">=4.0.0"
[package.extras]
cchardet = ["cchardet"]
chardet = ["chardet"]
charset-normalizer = ["charset-normalizer"]
html5lib = ["html5lib"]
lxml = ["lxml"]
[[package]]
name = "certifi"
version = "2025.1.31"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
groups = ["main", "docs"]
files = [
{file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
{file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
]
[[package]]
name = "cffi"
version = "1.17.1"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
{file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
{file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
{file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
{file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
{file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
{file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
{file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
{file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
{file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
{file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
{file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
{file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
{file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
{file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
{file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
{file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
{file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
{file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
{file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
{file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
]
markers = {main = "platform_python_implementation != \"PyPy\""}
[package.dependencies]
pycparser = "*"
[[package]]
name = "charset-normalizer"
version = "3.4.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7"
groups = ["main", "docs"]
files = [
{file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"},
{file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"},
{file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"},
{file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"},
{file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"},
{file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"},
{file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"},
{file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"},
{file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"},
{file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"},
{file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"},
{file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"},
{file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"},
{file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"},
{file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"},
{file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"},
{file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"},
{file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"},
{file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"},
{file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"},
]
[[package]]
name = "click"
version = "8.1.8"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.7"
groups = ["docs"]
files = [
{file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
{file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["main", "benchmark", "dev", "docs"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
markers = {main = "sys_platform == \"win32\"", benchmark = "sys_platform == \"win32\"", dev = "sys_platform == \"win32\""}
[[package]]
name = "coverage"
version = "7.6.12"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"},
{file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"},
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe"},
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674"},
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb"},
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c"},
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c"},
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e"},
{file = "coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425"},
{file = "coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa"},
{file = "coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015"},
{file = "coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45"},
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702"},
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0"},
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f"},
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f"},
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d"},
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba"},
{file = "coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f"},
{file = "coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558"},
{file = "coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad"},
{file = "coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3"},
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574"},
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985"},
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750"},
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea"},
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3"},
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a"},
{file = "coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95"},
{file = "coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288"},
{file = "coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1"},
{file = "coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd"},
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9"},
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e"},
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4"},
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6"},
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3"},
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc"},
{file = "coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3"},
{file = "coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef"},
{file = "coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e"},
{file = "coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703"},
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0"},
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924"},
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b"},
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d"},
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827"},
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9"},
{file = "coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3"},
{file = "coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f"},
{file = "coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d"},
{file = "coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929"},
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87"},
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c"},
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2"},
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd"},
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"},
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86"},
{file = "coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31"},
{file = "coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57"},
{file = "coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf"},
{file = "coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953"},
{file = "coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2"},
]
[package.dependencies]
tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
[package.extras]
toml = ["tomli"]
[[package]]
name = "cryptography"
version = "44.0.2"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = "!=3.9.0,!=3.9.1,>=3.7"
groups = ["main"]
files = [
{file = "cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"},
{file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"},
{file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb"},
{file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843"},
{file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5"},
{file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c"},
{file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a"},
{file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"},
{file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688"},
{file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7"},
{file = "cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79"},
{file = "cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa"},
{file = "cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3"},
{file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639"},
{file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd"},
{file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181"},
{file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea"},
{file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699"},
{file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9"},
{file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23"},
{file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922"},
{file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4"},
{file = "cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5"},
{file = "cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6"},
{file = "cryptography-44.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb"},
{file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41"},
{file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562"},
{file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5"},
{file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa"},
{file = "cryptography-44.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d"},
{file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d"},
{file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471"},
{file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615"},
{file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390"},
{file = "cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0"},
]
[package.dependencies]
cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
[package.extras]
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"]
docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"]
nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"]
pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"]
sdist = ["build (>=1.0.0)"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
test-randomorder = ["pytest-randomly"]
[[package]]
name = "docutils"
version = "0.21.2"
description = "Docutils -- Python Documentation Utilities"
optional = false
python-versions = ">=3.9"
groups = ["main", "docs"]
files = [
{file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"},
{file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"},
]
[[package]]
name = "exceptiongroup"
version = "1.2.2"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["benchmark", "dev", "docs"]
markers = "python_version < \"3.11\""
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
]
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "furo"
version = "2024.8.6"
description = "A clean customisable Sphinx documentation theme."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "furo-2024.8.6-py3-none-any.whl", hash = "sha256:6cd97c58b47813d3619e63e9081169880fbe331f0ca883c871ff1f3f11814f5c"},
{file = "furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01"},
]
[package.dependencies]
beautifulsoup4 = "*"
pygments = ">=2.7"
sphinx = ">=6.0,<9.0"
sphinx-basic-ng = ">=1.0.0.beta2"
[[package]]
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
optional = false
python-versions = ">=3.7"
groups = ["docs"]
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
]
[[package]]
name = "idna"
version = "3.10"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.6"
groups = ["main", "docs"]
files = [
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
]
[package.extras]
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
[[package]]
name = "imagesize"
version = "1.4.1"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
groups = ["main", "docs"]
files = [
{file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
{file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
]
[[package]]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
groups = ["benchmark", "dev"]
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "jinja2"
version = "3.1.6"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
groups = ["main", "docs"]
files = [
{file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
{file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
]
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev", "docs"]
files = [
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
{file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
code-style = ["pre-commit (>=3.0,<4.0)"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "markupsafe"
version = "3.0.2"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.9"
groups = ["main", "docs"]
files = [
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
{file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
{file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
{file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
{file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
{file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
{file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
{file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
{file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
{file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
{file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
{file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
{file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
{file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
{file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
{file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
{file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
{file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
{file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
{file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
]
[[package]]
name = "mdit-py-plugins"
version = "0.4.2"
description = "Collection of plugins for markdown-it-py"
optional = false
python-versions = ">=3.8"
groups = ["main", "docs"]
files = [
{file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"},
{file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"},
]
[package.dependencies]
markdown-it-py = ">=1.0.0,<4.0.0"
[package.extras]
code-style = ["pre-commit"]
rtd = ["myst-parser", "sphinx-book-theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
python-versions = ">=3.7"
groups = ["main", "dev", "docs"]
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]]
name = "myst-parser"
version = "4.0.1"
description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser,"
optional = false
python-versions = ">=3.10"
groups = ["main", "docs"]
files = [
{file = "myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d"},
{file = "myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4"},
]
[package.dependencies]
docutils = ">=0.19,<0.22"
jinja2 = "*"
markdown-it-py = ">=3.0,<4.0"
mdit-py-plugins = ">=0.4.1,<1.0"
pyyaml = "*"
sphinx = ">=7,<9"
[package.extras]
code-style = ["pre-commit (>=4.0,<5.0)"]
linkify = ["linkify-it-py (>=2.0,<3.0)"]
rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"]
testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pygments (<2.19)", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"]
testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"]
[[package]]
name = "packaging"
version = "24.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
groups = ["main", "benchmark", "dev", "docs"]
files = [
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
]
[[package]]
name = "pluggy"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
groups = ["benchmark", "dev"]
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "py-cpuinfo"
version = "9.0.0"
description = "Get CPU info with pure Python"
optional = false
python-versions = "*"
groups = ["benchmark"]
files = [
{file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"},
{file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"},
]
[[package]]
name = "pycparser"
version = "2.22"
description = "C parser in Python"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
markers = {main = "platform_python_implementation != \"PyPy\""}
[[package]]
name = "pygments"
version = "2.19.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
groups = ["main", "dev", "docs"]
files = [
{file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
{file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
]
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pytest"
version = "8.3.5"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
groups = ["benchmark", "dev"]
files = [
{file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"},
{file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=1.5,<2"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-benchmark"
version = "5.1.0"
description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer."
optional = false
python-versions = ">=3.9"
groups = ["benchmark"]
files = [
{file = "pytest-benchmark-5.1.0.tar.gz", hash = "sha256:9ea661cdc292e8231f7cd4c10b0319e56a2118e2c09d9f50e1b3d150d2aca105"},
{file = "pytest_benchmark-5.1.0-py3-none-any.whl", hash = "sha256:922de2dfa3033c227c96da942d1878191afa135a29485fb942e85dff1c592c89"},
]
[package.dependencies]
py-cpuinfo = "*"
pytest = ">=8.1"
[package.extras]
aspect = ["aspectlib"]
elasticsearch = ["elasticsearch"]
histogram = ["pygal", "pygaljs", "setuptools"]
[[package]]
name = "pytest-codspeed"
version = "3.2.0"
description = "Pytest plugin to create CodSpeed benchmarks"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pytest_codspeed-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5165774424c7ab8db7e7acdb539763a0e5657996effefdf0664d7fd95158d34"},
{file = "pytest_codspeed-3.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bd55f92d772592c04a55209950c50880413ae46876e66bd349ef157075ca26c"},
{file = "pytest_codspeed-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf6f56067538f4892baa8d7ab5ef4e45bb59033be1ef18759a2c7fc55b32035"},
{file = "pytest_codspeed-3.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:39a687b05c3d145642061b45ea78e47e12f13ce510104d1a2cda00eee0e36f58"},
{file = "pytest_codspeed-3.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46a1afaaa1ac4c2ca5b0700d31ac46d80a27612961d031067d73c6ccbd8d3c2b"},
{file = "pytest_codspeed-3.2.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c48ce3af3dfa78413ed3d69d1924043aa1519048dbff46edccf8f35a25dab3c2"},
{file = "pytest_codspeed-3.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66692506d33453df48b36a84703448cb8b22953eea51f03fbb2eb758dc2bdc4f"},
{file = "pytest_codspeed-3.2.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:479774f80d0bdfafa16112700df4dbd31bf2a6757fac74795fd79c0a7b3c389b"},
{file = "pytest_codspeed-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:109f9f4dd1088019c3b3f887d003b7d65f98a7736ca1d457884f5aa293e8e81c"},
{file = "pytest_codspeed-3.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2f69a03b52c9bb041aec1b8ee54b7b6c37a6d0a948786effa4c71157765b6da"},
{file = "pytest_codspeed-3.2.0-py3-none-any.whl", hash = "sha256:54b5c2e986d6a28e7b0af11d610ea57bd5531cec8326abe486f1b55b09d91c39"},
{file = "pytest_codspeed-3.2.0.tar.gz", hash = "sha256:f9d1b1a3b2c69cdc0490a1e8b1ced44bffbd0e8e21d81a7160cfdd923f6e8155"},
]
[package.dependencies]
cffi = ">=1.17.1"
pytest = ">=3.8"
rich = ">=13.8.1"
[package.extras]
compat = ["pytest-benchmark (>=5.0.0,<5.1.0)", "pytest-xdist (>=3.6.1,<3.7.0)"]
lint = ["mypy (>=1.11.2,<1.12.0)", "ruff (>=0.6.5,<0.7.0)"]
test = ["pytest (>=7.0,<8.0)", "pytest-cov (>=4.0.0,<4.1.0)"]
[[package]]
name = "pytest-cov"
version = "6.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"},
{file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"},
]
[package.dependencies]
coverage = {version = ">=7.5", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
[[package]]
name = "pyyaml"
version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
groups = ["main", "docs"]
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
name = "requests"
version = "2.32.3"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.8"
groups = ["main", "docs"]
files = [
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "rich"
version = "13.9.4"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
python-versions = ">=3.8.0"
groups = ["dev"]
files = [
{file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"},
{file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"},
]
[package.dependencies]
markdown-it-py = ">=2.2.0"
pygments = ">=2.13.0,<3.0.0"
typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""}
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "sniffio"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
optional = false
python-versions = ">=3.7"
groups = ["docs"]
files = [
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
]
[[package]]
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
optional = false
python-versions = "*"
groups = ["main", "docs"]
files = [
{file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
]
[[package]]
name = "soupsieve"
version = "2.6"
description = "A modern CSS selector implementation for Beautiful Soup."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"},
{file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"},
]
[[package]]
name = "sphinx"
version = "8.1.3"
description = "Python documentation generator"
optional = false
python-versions = ">=3.10"
groups = ["main", "docs"]
files = [
{file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"},
{file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"},
]
[package.dependencies]
alabaster = ">=0.7.14"
babel = ">=2.13"
colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""}
docutils = ">=0.20,<0.22"
imagesize = ">=1.3"
Jinja2 = ">=3.1"
packaging = ">=23.0"
Pygments = ">=2.17"
requests = ">=2.30.0"
snowballstemmer = ">=2.2"
sphinxcontrib-applehelp = ">=1.0.7"
sphinxcontrib-devhelp = ">=1.0.6"
sphinxcontrib-htmlhelp = ">=2.0.6"
sphinxcontrib-jsmath = ">=1.0.1"
sphinxcontrib-qthelp = ">=1.0.6"
sphinxcontrib-serializinghtml = ">=1.1.9"
tomli = {version = ">=2", markers = "python_version < \"3.11\""}
[package.extras]
docs = ["sphinxcontrib-websupport"]
lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"]
test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"]
[[package]]
name = "sphinx-autobuild"
version = "2024.10.3"
description = "Rebuild Sphinx documentation on changes, with hot reloading in the browser."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa"},
{file = "sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1"},
]
[package.dependencies]
colorama = ">=0.4.6"
sphinx = "*"
starlette = ">=0.35"
uvicorn = ">=0.25"
watchfiles = ">=0.20"
websockets = ">=11"
[package.extras]
test = ["httpx", "pytest (>=6)"]
[[package]]
name = "sphinx-basic-ng"
version = "1.0.0b2"
description = "A modern skeleton for Sphinx themes."
optional = false
python-versions = ">=3.7"
groups = ["docs"]
files = [
{file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"},
{file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"},
]
[package.dependencies]
sphinx = ">=4.0"
[package.extras]
docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"]
[[package]]
name = "sphinx-rtd-theme"
version = "3.0.2"
description = "Read the Docs theme for Sphinx"
optional = true
python-versions = ">=3.8"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13"},
{file = "sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85"},
]
[package.dependencies]
docutils = ">0.18,<0.22"
sphinx = ">=6,<9"
sphinxcontrib-jquery = ">=4,<5"
[package.extras]
dev = ["bump2version", "transifex-client", "twine", "wheel"]
[[package]]
name = "sphinxcontrib-applehelp"
version = "2.0.0"
description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
optional = false
python-versions = ">=3.9"
groups = ["main", "docs"]
files = [
{file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"},
{file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-devhelp"
version = "2.0.0"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents"
optional = false
python-versions = ">=3.9"
groups = ["main", "docs"]
files = [
{file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"},
{file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-htmlhelp"
version = "2.1.0"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
optional = false
python-versions = ">=3.9"
groups = ["main", "docs"]
files = [
{file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"},
{file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["html5lib", "pytest"]
[[package]]
name = "sphinxcontrib-jquery"
version = "4.1"
description = "Extension to include jQuery on newer Sphinx releases"
optional = true
python-versions = ">=2.7"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"},
{file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"},
]
[package.dependencies]
Sphinx = ">=1.8"
[[package]]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
description = "A sphinx extension which renders display math in HTML via JavaScript"
optional = false
python-versions = ">=3.5"
groups = ["main", "docs"]
files = [
{file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
{file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
]
[package.extras]
test = ["flake8", "mypy", "pytest"]
[[package]]
name = "sphinxcontrib-qthelp"
version = "2.0.0"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents"
optional = false
python-versions = ">=3.9"
groups = ["main", "docs"]
files = [
{file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"},
{file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["defusedxml (>=0.7.1)", "pytest"]
[[package]]
name = "sphinxcontrib-serializinghtml"
version = "2.0.0"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)"
optional = false
python-versions = ">=3.9"
groups = ["main", "docs"]
files = [
{file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"},
{file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "starlette"
version = "0.46.1"
description = "The little ASGI library that shines."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227"},
{file = "starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230"},
]
[package.dependencies]
anyio = ">=3.6.2,<5"
[package.extras]
full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"]
[[package]]
name = "tomli"
version = "2.2.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
groups = ["main", "benchmark", "dev", "docs"]
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
{file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
{file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
{file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
{file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
{file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
{file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
{file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
{file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
{file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
{file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
{file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
{file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
]
markers = {main = "python_version < \"3.11\"", benchmark = "python_version < \"3.11\"", dev = "python_full_version <= \"3.11.0a6\"", docs = "python_version < \"3.11\""}
[[package]]
name = "typing-extensions"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
groups = ["dev", "docs"]
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
markers = {dev = "python_version < \"3.11\""}
[[package]]
name = "urllib3"
version = "2.3.0"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.9"
groups = ["main", "docs"]
files = [
{file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"},
{file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uvicorn"
version = "0.34.0"
description = "The lightning-fast ASGI server."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"},
{file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"},
]
[package.dependencies]
click = ">=7.0"
h11 = ">=0.8"
typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
[package.extras]
standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
[[package]]
name = "watchfiles"
version = "1.0.4"
description = "Simple, modern and high performance file watching and code reload in python."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "watchfiles-1.0.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ba5bb3073d9db37c64520681dd2650f8bd40902d991e7b4cfaeece3e32561d08"},
{file = "watchfiles-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f25d0ba0fe2b6d2c921cf587b2bf4c451860086534f40c384329fb96e2044d1"},
{file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47eb32ef8c729dbc4f4273baece89398a4d4b5d21a1493efea77a17059f4df8a"},
{file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:076f293100db3b0b634514aa0d294b941daa85fc777f9c698adb1009e5aca0b1"},
{file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1eacd91daeb5158c598fe22d7ce66d60878b6294a86477a4715154990394c9b3"},
{file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13c2ce7b72026cfbca120d652f02c7750f33b4c9395d79c9790b27f014c8a5a2"},
{file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90192cdc15ab7254caa7765a98132a5a41471cf739513cc9bcf7d2ffcc0ec7b2"},
{file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278aaa395f405972e9f523bd786ed59dfb61e4b827856be46a42130605fd0899"},
{file = "watchfiles-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a462490e75e466edbb9fc4cd679b62187153b3ba804868452ef0577ec958f5ff"},
{file = "watchfiles-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8d0d0630930f5cd5af929040e0778cf676a46775753e442a3f60511f2409f48f"},
{file = "watchfiles-1.0.4-cp310-cp310-win32.whl", hash = "sha256:cc27a65069bcabac4552f34fd2dce923ce3fcde0721a16e4fb1b466d63ec831f"},
{file = "watchfiles-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:8b1f135238e75d075359cf506b27bf3f4ca12029c47d3e769d8593a2024ce161"},
{file = "watchfiles-1.0.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2a9f93f8439639dc244c4d2902abe35b0279102bca7bbcf119af964f51d53c19"},
{file = "watchfiles-1.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9eea33ad8c418847dd296e61eb683cae1c63329b6d854aefcd412e12d94ee235"},
{file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31f1a379c9dcbb3f09cf6be1b7e83b67c0e9faabed0471556d9438a4a4e14202"},
{file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab594e75644421ae0a2484554832ca5895f8cab5ab62de30a1a57db460ce06c6"},
{file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc2eb5d14a8e0d5df7b36288979176fbb39672d45184fc4b1c004d7c3ce29317"},
{file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f68d8e9d5a321163ddacebe97091000955a1b74cd43724e346056030b0bacee"},
{file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9ce064e81fe79faa925ff03b9f4c1a98b0bbb4a1b8c1b015afa93030cb21a49"},
{file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b77d5622ac5cc91d21ae9c2b284b5d5c51085a0bdb7b518dba263d0af006132c"},
{file = "watchfiles-1.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1941b4e39de9b38b868a69b911df5e89dc43767feeda667b40ae032522b9b5f1"},
{file = "watchfiles-1.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f8c4998506241dedf59613082d1c18b836e26ef2a4caecad0ec41e2a15e4226"},
{file = "watchfiles-1.0.4-cp311-cp311-win32.whl", hash = "sha256:4ebbeca9360c830766b9f0df3640b791be569d988f4be6c06d6fae41f187f105"},
{file = "watchfiles-1.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:05d341c71f3d7098920f8551d4df47f7b57ac5b8dad56558064c3431bdfc0b74"},
{file = "watchfiles-1.0.4-cp311-cp311-win_arm64.whl", hash = "sha256:32b026a6ab64245b584acf4931fe21842374da82372d5c039cba6bf99ef722f3"},
{file = "watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2"},
{file = "watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9"},
{file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712"},
{file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12"},
{file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844"},
{file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733"},
{file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af"},
{file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a"},
{file = "watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff"},
{file = "watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e"},
{file = "watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94"},
{file = "watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c"},
{file = "watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90"},
{file = "watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9"},
{file = "watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60"},
{file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407"},
{file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d"},
{file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d"},
{file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b"},
{file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590"},
{file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902"},
{file = "watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1"},
{file = "watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303"},
{file = "watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80"},
{file = "watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc"},
{file = "watchfiles-1.0.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:d3452c1ec703aa1c61e15dfe9d482543e4145e7c45a6b8566978fbb044265a21"},
{file = "watchfiles-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7b75fee5a16826cf5c46fe1c63116e4a156924d668c38b013e6276f2582230f0"},
{file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e997802d78cdb02623b5941830ab06f8860038faf344f0d288d325cc9c5d2ff"},
{file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0611d244ce94d83f5b9aff441ad196c6e21b55f77f3c47608dcf651efe54c4a"},
{file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9745a4210b59e218ce64c91deb599ae8775c8a9da4e95fb2ee6fe745fc87d01a"},
{file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4810ea2ae622add560f4aa50c92fef975e475f7ac4900ce5ff5547b2434642d8"},
{file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:740d103cd01458f22462dedeb5a3382b7f2c57d07ff033fbc9465919e5e1d0f3"},
{file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbd912a61543a36aef85e34f212e5d2486e7c53ebfdb70d1e0b060cc50dd0bf"},
{file = "watchfiles-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0bc80d91ddaf95f70258cf78c471246846c1986bcc5fd33ccc4a1a67fcb40f9a"},
{file = "watchfiles-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab0311bb2ffcd9f74b6c9de2dda1612c13c84b996d032cd74799adb656af4e8b"},
{file = "watchfiles-1.0.4-cp39-cp39-win32.whl", hash = "sha256:02a526ee5b5a09e8168314c905fc545c9bc46509896ed282aeb5a8ba9bd6ca27"},
{file = "watchfiles-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:a5ae5706058b27c74bac987d615105da17724172d5aaacc6c362a40599b6de43"},
{file = "watchfiles-1.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdcc92daeae268de1acf5b7befcd6cfffd9a047098199056c72e4623f531de18"},
{file = "watchfiles-1.0.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8d3d9203705b5797f0af7e7e5baa17c8588030aaadb7f6a86107b7247303817"},
{file = "watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdef5a1be32d0b07dcea3318a0be95d42c98ece24177820226b56276e06b63b0"},
{file = "watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342622287b5604ddf0ed2d085f3a589099c9ae8b7331df3ae9845571586c4f3d"},
{file = "watchfiles-1.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9fe37a2de80aa785d340f2980276b17ef697ab8db6019b07ee4fd28a8359d2f3"},
{file = "watchfiles-1.0.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9d1ef56b56ed7e8f312c934436dea93bfa3e7368adfcf3df4c0da6d4de959a1e"},
{file = "watchfiles-1.0.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b42cac65beae3a362629950c444077d1b44f1790ea2772beaea95451c086bb"},
{file = "watchfiles-1.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e0227b8ed9074c6172cf55d85b5670199c99ab11fd27d2c473aa30aec67ee42"},
{file = "watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205"},
]
[package.dependencies]
anyio = ">=3.0.0"
[[package]]
name = "websockets"
version = "15.0.1"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b"},
{file = "websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205"},
{file = "websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a"},
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e"},
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf"},
{file = "websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb"},
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d"},
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9"},
{file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c"},
{file = "websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256"},
{file = "websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41"},
{file = "websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431"},
{file = "websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57"},
{file = "websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905"},
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562"},
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792"},
{file = "websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413"},
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8"},
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3"},
{file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf"},
{file = "websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85"},
{file = "websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065"},
{file = "websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3"},
{file = "websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665"},
{file = "websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2"},
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215"},
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5"},
{file = "websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65"},
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe"},
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4"},
{file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597"},
{file = "websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9"},
{file = "websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7"},
{file = "websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931"},
{file = "websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675"},
{file = "websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151"},
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22"},
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f"},
{file = "websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8"},
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375"},
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d"},
{file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4"},
{file = "websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa"},
{file = "websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561"},
{file = "websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5"},
{file = "websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a"},
{file = "websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b"},
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770"},
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb"},
{file = "websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054"},
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee"},
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed"},
{file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880"},
{file = "websockets-15.0.1-cp39-cp39-win32.whl", hash = "sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411"},
{file = "websockets-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04"},
{file = "websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f"},
{file = "websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123"},
{file = "websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f"},
{file = "websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee"},
]
[extras]
docs = ["Sphinx", "myst-parser", "sphinx-rtd-theme"]
[metadata]
lock-version = "2.1"
python-versions = "^3.10"
content-hash = "574c44733b8a8286928139c1f3c4800041b24b0dbc45b36b7d7daaf7894841d6"
bluetooth-data-tools-1.26.1/pyproject.toml000066400000000000000000000065311476514471000206050ustar00rootroot00000000000000[project]
name = "bluetooth-data-tools"
version = "1.26.1"
license.text = "Apache-2.0"
description = "Tools for converting bluetooth data and packets"
authors = [{ name = "J. Nick Koston", email = "nick@koston.org" }]
readme = "README.md"
requires-python = ">=3.10"
[project.urls]
"Repository" = "https://github.com/bdraco/bluetooth-data-tools"
"Documentation" = "https://bluetooth-data-tools.readthedocs.io"
"Bug Tracker" = "https://github.com/bdraco/bluetooth-data-tools/issues"
"Changelog" = "https://github.com/bdraco/bluetooth-data-tools/blob/main/CHANGELOG.md"
[tool.poetry]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Natural Language :: English",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries",
]
packages = [
{ include = "bluetooth_data_tools", from = "src" },
]
[tool.poetry.build]
generate-setup-file = true
script = "build_ext.py"
[tool.poetry.dependencies]
python = "^3.10"
# Documentation Dependencies
Sphinx = {version = ">=5,<9", optional = true}
sphinx-rtd-theme = {version = ">=1,<4", optional = true}
myst-parser = {version = ">=0.18,<4.1", optional = true}
cryptography = ">=41.0.3"
[tool.poetry.extras]
docs = [
"myst-parser",
"sphinx",
"sphinx-rtd-theme",
]
[tool.poetry.group.benchmark.dependencies]
pytest-benchmark = ">=4,<6"
[tool.poetry.group.dev.dependencies]
pytest-codspeed = "^3.1.2"
pytest = "^8.3"
pytest-cov = "^6.0"
[tool.poetry.group.docs]
optional = true
[tool.poetry.group.docs.dependencies]
myst-parser = ">=0.16"
sphinx = ">=4.0"
furo = ">=2023.5.20"
sphinx-autobuild = ">=2021.3.14"
[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
version_variables = [
"src/bluetooth_data_tools/__init__.py:__version__",
"docs/conf.py:release",
]
build_command = "pip install poetry && poetry build"
branch = "main"
[tool.pytest.ini_options]
pythonpath = ["src"]
[tool.coverage.run]
branch = true
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"@overload",
"if TYPE_CHECKING",
"raise NotImplementedError",
]
[tool.mypy]
check_untyped_defs = true
disallow_any_generics = true
disallow_incomplete_defs = true
disallow_untyped_defs = true
mypy_path = "src/"
no_implicit_optional = true
show_error_codes = true
warn_unreachable = true
warn_unused_ignores = true
exclude = [
'docs/.*',
'setup.py',
]
[[tool.mypy.overrides]]
module = "tests.*"
allow_untyped_defs = true
[[tool.mypy.overrides]]
module = "bench.*"
allow_untyped_defs = true
[[tool.mypy.overrides]]
module = "docs.*"
ignore_errors = true
[build-system]
requires = ['setuptools>=75.8.0', 'Cython', "poetry-core>=2.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.ruff]
exclude = ["docs"]
line-length = 88
target-version = "py310"
[tool.ruff.lint]
extend-select = [
"B",
"E",
"F",
"I", # isort
"S", # security (bandit)
"W",
"UP", # pyupgrade
]
extend-ignore = [
"E501", # formatter will take care of line lengths
]
[tool.ruff.lint.extend-per-file-ignores]
"tests/*" = [
"S101", # assert ok in tests
]
[tool.ruff.lint.isort]
known-first-party = ["bluetooth_data_tools", "tests"]
[tool.ruff.per-file-ignores]
"tests/**/*" = [
"D100",
"D101",
"D102",
"D103",
"D104",
"S101",
]
"setup.py" = ["D100"]
"conftest.py" = ["D100"]
"docs/conf.py" = ["D100"]
bluetooth-data-tools-1.26.1/renovate.json000066400000000000000000000001011476514471000203720ustar00rootroot00000000000000{
"extends": ["github>browniebroke/renovate-configs:python"]
}
bluetooth-data-tools-1.26.1/src/000077500000000000000000000000001476514471000164535ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/000077500000000000000000000000001476514471000226715ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/__init__.py000066400000000000000000000017511476514471000250060ustar00rootroot00000000000000"""Bluetooth data tools."""
from __future__ import annotations
from .distance import calculate_distance_meters
from .gap import (
BLEGAPAdvertisement,
BLEGAPType,
parse_advertisement_data,
parse_advertisement_data_tuple,
)
from .privacy import get_cipher_for_irk, resolve_private_address
from .time import monotonic_time_coarse
from .utils import (
address_to_bytes,
human_readable_name,
int_to_bluetooth_address,
mac_to_int,
manufacturer_data_to_raw,
newest_manufacturer_data,
short_address,
)
__version__ = "1.26.1"
__all__ = [
"address_to_bytes",
"manufacturer_data_to_raw",
"newest_manufacturer_data",
"human_readable_name",
"int_to_bluetooth_address",
"short_address",
"BLEGAPType",
"BLEGAPAdvertisement",
"parse_advertisement_data",
"parse_advertisement_data_tuple",
"calculate_distance_meters",
"get_cipher_for_irk",
"resolve_private_address",
"monotonic_time_coarse",
"mac_to_int",
]
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/_time_impl.pyx000066400000000000000000000003561476514471000255550ustar00rootroot00000000000000import cython
from posix.time cimport clock_gettime, timespec
def _monotonic_time_coarse():
cdef timespec ts
cdef double current
clock_gettime(6, &ts)
current = ts.tv_sec + (ts.tv_nsec / 1000000000.)
return current
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/_utils_impl.pyx000066400000000000000000000005511476514471000257540ustar00rootroot00000000000000# cython: language_level=3, c_string_type=str, c_string_encoding=ascii
from libc.stdint cimport uint64_t
cdef extern from "utils_wrapper.h":
void _uint64_to_bdaddr(uint64_t address, char bdaddr[17]) nogil
def _int_to_bluetooth_address(addr: int) -> str:
cdef char bdaddr[17]
_uint64_to_bdaddr(addr, bdaddr)
return bdaddr[:17]
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/distance.py000066400000000000000000000007251476514471000250410ustar00rootroot00000000000000from typing import cast
MAX_THEORETICAL_DISTANCE = 400.0
def calculate_distance_meters(power: int, rssi: int) -> float | None:
"""Calculate the distance in meters between the scanner and the device."""
if rssi == 0 or power == 0:
return None
if (ratio := rssi * 1.0 / power) < 1.0:
distance = pow(ratio, 10)
else:
distance = cast(float, 0.89976 * pow(ratio, 7.7095) + 0.111)
return min(distance, MAX_THEORETICAL_DISTANCE)
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/gap.pxd000066400000000000000000000030541476514471000241570ustar00rootroot00000000000000import cython
cdef str BLE_UUID
cdef object from_bytes
cdef object from_bytes_little
cdef object from_bytes_signed
cdef object _cached_uint16_bytes_as_uuid
cdef object _cached_uint32_bytes_as_uuid
cdef object _cached_uint128_bytes_as_uuid
cdef object _cached_parse_advertisement_data
cdef object _cached_parse_advertisement_data_tuple
cdef object _cached_manufacturer_id_bytes_to_int
cdef object _cached_from_bytes_signed
cdef object _LOGGER
cdef class BLEGAPAdvertisement:
cdef readonly object local_name
cdef readonly object service_uuids
cdef readonly object service_data
cdef readonly object manufacturer_data
cdef readonly object tx_power
cdef cython.uint TYPE_SHORT_LOCAL_NAME
cdef cython.uint TYPE_COMPLETE_LOCAL_NAME
cdef cython.uint TYPE_MANUFACTURER_SPECIFIC_DATA
cdef cython.uint TYPE_16BIT_SERVICE_UUID_COMPLETE
cdef cython.uint TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE
cdef cython.uint TYPE_128BIT_SERVICE_UUID_COMPLETE
cdef cython.uint TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE
cdef cython.uint TYPE_SERVICE_DATA
cdef cython.uint TYPE_SERVICE_DATA_32BIT_UUID
cdef cython.uint TYPE_SERVICE_DATA_128BIT_UUID
cdef cython.uint TYPE_TX_POWER_LEVEL
cpdef parse_advertisement_data(object data)
@cython.locals(
gap_bytes="bytes",
gap_data="const unsigned char *",
gap_value=cython.bytes,
gap_type_num="unsigned char",
total_length=cython.uint,
length="unsigned char",
offset=cython.uint,
start=cython.uint,
end=cython.uint,
tx_power_int="unsigned char"
)
cpdef _uncached_parse_advertisement_data(tuple data)
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/gap.py000066400000000000000000000222331476514471000240140ustar00rootroot00000000000000"""GATT Advertisement and Scan Response Data (GAP)."""
import logging
from collections.abc import Iterable
from enum import IntEnum
from functools import lru_cache, partial
from typing import TYPE_CHECKING
BLE_UUID = "0000-1000-8000-00805f9b34fb"
_LOGGER = logging.getLogger(__name__)
class BLEGAPAdvertisement:
"""GATT Advertisement and Scan Response Data (GAP)."""
__slots__ = (
"local_name",
"service_uuids",
"service_data",
"manufacturer_data",
"tx_power",
)
def __init__(
self,
local_name: str | None,
service_uuids: list[str],
service_data: dict[str, bytes],
manufacturer_data: dict[int, bytes],
tx_power: int | None,
) -> None:
"""Initialize GAP Advertisement."""
self.local_name = local_name
self.service_uuids = service_uuids
self.service_data = service_data
self.manufacturer_data = manufacturer_data
self.tx_power = tx_power
class BLEGAPType(IntEnum):
"""Advertising data types."""
TYPE_UNKNOWN = 0x00
TYPE_FLAGS = 0x01
TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE = 0x02
TYPE_16BIT_SERVICE_UUID_COMPLETE = 0x03
TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE = 0x04
TYPE_32BIT_SERVICE_UUID_COMPLETE = 0x05
TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE = 0x06
TYPE_128BIT_SERVICE_UUID_COMPLETE = 0x07
TYPE_SHORT_LOCAL_NAME = 0x08
TYPE_COMPLETE_LOCAL_NAME = 0x09
TYPE_TX_POWER_LEVEL = 0x0A
TYPE_CLASS_OF_DEVICE = 0x0D
TYPE_SIMPLE_PAIRING_HASH_C = 0x0E
TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F
TYPE_SECURITY_MANAGER_TK_VALUE = 0x10
TYPE_SECURITY_MANAGER_OOB_FLAGS = 0x11
TYPE_SLAVE_CONNECTION_INTERVAL_RANGE = 0x12
TYPE_SOLICITED_SERVICE_UUIDS_16BIT = 0x14
TYPE_SOLICITED_SERVICE_UUIDS_128BIT = 0x15
TYPE_SERVICE_DATA = 0x16
TYPE_PUBLIC_TARGET_ADDRESS = 0x17
TYPE_RANDOM_TARGET_ADDRESS = 0x18
TYPE_APPEARANCE = 0x19
TYPE_ADVERTISING_INTERVAL = 0x1A
TYPE_LE_BLUETOOTH_DEVICE_ADDRESS = 0x1B
TYPE_LE_ROLE = 0x1C
TYPE_SIMPLE_PAIRING_HASH_C256 = 0x1D
TYPE_SIMPLE_PAIRING_RANDOMIZER_R256 = 0x1E
TYPE_SERVICE_DATA_32BIT_UUID = 0x20
TYPE_SERVICE_DATA_128BIT_UUID = 0x21
TYPE_URI = 0x24
TYPE_3D_INFORMATION_DATA = 0x3D
TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF
from_bytes = int.from_bytes
from_bytes_little = partial(from_bytes, byteorder="little")
from_bytes_signed = partial(from_bytes, byteorder="little", signed=True)
TYPE_SHORT_LOCAL_NAME = BLEGAPType.TYPE_SHORT_LOCAL_NAME.value
TYPE_COMPLETE_LOCAL_NAME = BLEGAPType.TYPE_COMPLETE_LOCAL_NAME.value
TYPE_MANUFACTURER_SPECIFIC_DATA = BLEGAPType.TYPE_MANUFACTURER_SPECIFIC_DATA.value
TYPE_16BIT_SERVICE_UUID_COMPLETE = BLEGAPType.TYPE_16BIT_SERVICE_UUID_COMPLETE.value
TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE = (
BLEGAPType.TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE.value
)
TYPE_128BIT_SERVICE_UUID_COMPLETE = BLEGAPType.TYPE_128BIT_SERVICE_UUID_COMPLETE.value
TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE = (
BLEGAPType.TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE.value
)
TYPE_SERVICE_DATA = BLEGAPType.TYPE_SERVICE_DATA.value
TYPE_SERVICE_DATA_32BIT_UUID = BLEGAPType.TYPE_SERVICE_DATA_32BIT_UUID.value
TYPE_SERVICE_DATA_128BIT_UUID = BLEGAPType.TYPE_SERVICE_DATA_128BIT_UUID.value
TYPE_TX_POWER_LEVEL = BLEGAPType.TYPE_TX_POWER_LEVEL.value
bytes_ = bytes
BLEGAPAdvertisementTupleType = tuple[
str | None, list[str], dict[str, bytes], dict[int, bytes], int | None
]
_cached_from_bytes_signed = lru_cache(maxsize=256)(from_bytes_signed)
@lru_cache(maxsize=256)
def _uint128_bytes_as_uuid(uint128_bytes: bytes_) -> str:
"""Convert an integer to a UUID str."""
int_value = from_bytes_little(uint128_bytes)
hex = f"{int_value:032x}"
return f"{hex[:8]}-{hex[8:12]}-{hex[12:16]}-{hex[16:20]}-{hex[20:]}"
_cached_uint128_bytes_as_uuid = _uint128_bytes_as_uuid
@lru_cache(maxsize=256)
def _uint16_bytes_as_uuid(uuid16_bytes: bytes_) -> str:
"""Convert a 16-bit UUID to a UUID str."""
return f"0000{from_bytes_little(uuid16_bytes):04x}-{BLE_UUID}"
_cached_uint16_bytes_as_uuid = _uint16_bytes_as_uuid
@lru_cache(maxsize=256)
def _uint32_bytes_as_uuid(uuid32_bytes: bytes_) -> str:
"""Convert a 32-bit UUID to a UUID str."""
return f"{from_bytes_little(uuid32_bytes):08x}-{BLE_UUID}"
_cached_uint32_bytes_as_uuid = _uint32_bytes_as_uuid
_cached_manufacturer_id_bytes_to_int = lru_cache(maxsize=256)(from_bytes_little)
@lru_cache(maxsize=256)
def _parse_advertisement_data(
data: tuple[bytes, ...],
) -> BLEGAPAdvertisement:
"""Parse advertisement data and return a BLEGAPAdvertisement."""
return BLEGAPAdvertisement(*_uncached_parse_advertisement_data(data))
_cached_parse_advertisement_data = _parse_advertisement_data
def parse_advertisement_data(
data: Iterable[bytes],
) -> BLEGAPAdvertisement:
"""Parse advertisement data and return a BLEGAPAdvertisement."""
if type(data) is tuple:
return _cached_parse_advertisement_data(data)
return _cached_parse_advertisement_data(tuple(data))
def _uncached_parse_advertisement_data(
data: tuple[bytes, ...],
) -> BLEGAPAdvertisementTupleType:
manufacturer_data: dict[int, bytes] = {}
service_data: dict[str, bytes] = {}
service_uuids: list[str] = []
local_name: str | None = None
tx_power: int | None = None
for gap_bytes in data:
offset = 0
total_length = len(gap_bytes)
gap_data = gap_bytes
# IMPORTANT: All data must be manually bounds checked
# because the data is untrusted and can be malformed.
while offset + 2 < total_length:
if not (length := gap_data[offset]):
offset += 1 # Handle zero padding
continue
if not (gap_type_num := gap_data[offset + 1]):
offset += 1 + length # Skip empty type
continue
start = offset + 2
end = start + length - 1
if end > total_length or end - start <= 0:
_LOGGER.debug(
"Invalid BLE GAP AD structure at offset %s: %s (%s)",
offset,
gap_bytes,
)
offset += 1 + length
continue
offset += 1 + length
if gap_type_num == TYPE_SHORT_LOCAL_NAME and local_name is None:
local_name = gap_data[start:end].decode("utf-8", "replace")
elif gap_type_num == TYPE_COMPLETE_LOCAL_NAME:
local_name = gap_data[start:end].decode("utf-8", "replace")
elif gap_type_num == TYPE_MANUFACTURER_SPECIFIC_DATA:
if start + 2 >= total_length:
break
manufacturer_data[
_cached_manufacturer_id_bytes_to_int(gap_data[start : start + 2])
] = gap_data[start + 2 : end]
elif gap_type_num in {
TYPE_16BIT_SERVICE_UUID_COMPLETE,
TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE,
}:
service_uuids.append(_cached_uint16_bytes_as_uuid(gap_data[start:end]))
elif gap_type_num in {
TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE,
TYPE_128BIT_SERVICE_UUID_COMPLETE,
}:
service_uuids.append(_cached_uint128_bytes_as_uuid(gap_data[start:end]))
elif gap_type_num == TYPE_SERVICE_DATA:
if start + 2 >= total_length:
break
service_data[
_cached_uint16_bytes_as_uuid(gap_data[start : start + 2])
] = gap_data[start + 2 : end]
elif gap_type_num == TYPE_SERVICE_DATA_32BIT_UUID:
if start + 4 >= total_length:
break
service_data[
_cached_uint32_bytes_as_uuid(gap_data[start : start + 4])
] = gap_data[start + 4 : end]
elif gap_type_num == TYPE_SERVICE_DATA_128BIT_UUID:
if start + 16 >= total_length:
break
service_data[
_cached_uint128_bytes_as_uuid(gap_data[start : start + 16])
] = gap_data[start + 16 : end]
elif gap_type_num == TYPE_TX_POWER_LEVEL:
tx_power = _cached_from_bytes_signed(gap_data[start:end])
return (local_name, service_uuids, service_data, manufacturer_data, tx_power)
if TYPE_CHECKING:
@lru_cache(maxsize=256)
def parse_advertisement_data_tuple(
data: tuple[bytes, ...],
) -> BLEGAPAdvertisementTupleType:
"""Parse a tuple of raw advertisement data and return a tuple of BLEGAPAdvertisementTupleType.
The format of the tuple is:
(local_name, service_uuids, service_data, manufacturer_data, tx_power)
This is tightly coupled to bleak. If you are not using bleak
it is recommended to use parse_advertisement_data instead.
local_name: str | None
service_uuids: list[str]
service_data: dict[str, bytes]
manufacturer_data: dict[int, bytes]
tx_power: int | None
"""
return _uncached_parse_advertisement_data(data)
else:
parse_advertisement_data_tuple = lru_cache(maxsize=1024)(
_uncached_parse_advertisement_data
)
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/privacy.py000066400000000000000000000017521476514471000247250ustar00rootroot00000000000000"""Helpers for resolving a private address if you know its identity resolving key.
This process uses 128bit IRK as encryption key for ECB AES.
One half of address is used to store a random 24bit number
(prand). This is encrypted to produce a "hash". The top 24 bits
of the hash should math the other half of the MAC address.
See https://www.mdpi.com/2227-7390/10/22/4346
"""
import binascii
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
PADDING = b"\x00" * 13
def get_cipher_for_irk(irk: bytes) -> Cipher:
return Cipher(algorithms.AES(irk), modes.ECB()) # noqa: S305
def resolve_private_address(
cipher: Cipher,
address: str,
) -> bool:
rpa = binascii.unhexlify(address.replace(":", ""))
if rpa[0] & 0xC0 != 0x40:
# Not an RPA
return False
pt = PADDING + rpa[:3]
encryptor = cipher.encryptor()
ct = encryptor.update(pt) + encryptor.finalize()
if ct[13:] != rpa[3:]:
return False
return True
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/py.typed000066400000000000000000000000001476514471000243560ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/time.py000066400000000000000000000027701476514471000242070ustar00rootroot00000000000000"""Bluetooth time utils."""
from __future__ import annotations
import platform
import time
from collections.abc import Callable
from contextlib import suppress
from functools import partial
CLOCK_MONOTONIC_COARSE = 6
def __gen_monotonic_time_coarse() -> partial[float]:
"""Return a function that provides monotonic time in seconds.
This is the coarse version of time_monotonic, which is faster but less accurate.
Since many arm64 and 32-bit platforms don't support VDSO with time.monotonic
because of errata, we can't rely on the kernel to provide a fast
monotonic time.
https://lore.kernel.org/lkml/20170404171826.25030-1-marc.zyngier@arm.com/
"""
# We use a partial here since its implementation is in native code
# which allows us to avoid the overhead of the global lookup
# of CLOCK_MONOTONIC_COARSE.
return partial(time.clock_gettime, CLOCK_MONOTONIC_COARSE)
monotonic_time_coarse: Callable[[], float] = time.monotonic
_USE_COARSE_MONOTONIC_TIME = False
with suppress(Exception):
if (
platform.system() == "Linux"
and abs(time.monotonic() - __gen_monotonic_time_coarse()()) < 1
):
monotonic_time_coarse = __gen_monotonic_time_coarse()
_USE_COARSE_MONOTONIC_TIME = True
if _USE_COARSE_MONOTONIC_TIME:
with suppress(ImportError):
from ._time_impl import ( # type: ignore[no-redef] # noqa: F811 F401
_monotonic_time_coarse as monotonic_time_coarse,
)
__all__ = [
"monotonic_time_coarse",
]
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/utils.pxd000066400000000000000000000000641476514471000245460ustar00rootroot00000000000000cdef object L_PACK
cpdef object mac_to_int(str mac)
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/utils.py000066400000000000000000000037241476514471000244110ustar00rootroot00000000000000"""Bluetooth utils."""
from __future__ import annotations
from functools import lru_cache
from struct import Struct
L_PACK = Struct(">L")
try:
from ._utils_impl import _int_to_bluetooth_address # noqa: F811 F401
except ImportError:
def _int_to_bluetooth_address(address: int) -> str:
"""Convert an integer to a bluetooth address."""
mac_hex = f"{address:012X}"
return f"{mac_hex[0:2]}:{mac_hex[2:4]}:{mac_hex[4:6]}:{mac_hex[6:8]}:{mac_hex[8:10]}:{mac_hex[10:12]}" # noqa: E501
int_to_bluetooth_address = lru_cache(maxsize=256)(_int_to_bluetooth_address)
def mac_to_int(address: str) -> int:
"""Convert a mac address to an integer."""
return int(address.replace(":", ""), 16)
def short_address(address: str) -> str:
"""Convert a Bluetooth address to a short address."""
results = address.replace("-", ":").split(":")
last: str = results[-1]
second_last: str = results[-2]
return f"{second_last.upper()}{last.upper()}"[-4:]
def human_readable_name(name: str | None, local_name: str, address: str) -> str:
"""Return a human readable name for the given name, local_name, and address."""
return f"{name or local_name} ({short_address(address)})"
def newest_manufacturer_data(manufacturer_data: dict[int, bytes]) -> bytes | None:
"""Return the raw data from manufacturer data."""
if manufacturer_data and (last_id := list(manufacturer_data)[-1]):
return manufacturer_data[last_id]
return None
def address_to_bytes(address: str) -> bytes:
"""Return the address as bytes."""
if ":" not in address:
address_as_int = 0
else:
address_as_int = mac_to_int(address)
return L_PACK.pack(address_as_int)
def manufacturer_data_to_raw(manufacturer_id: int, manufacturer_data: bytes) -> bytes:
"""Return the raw data from manufacturer data."""
init_bytes: bytes = int(manufacturer_id).to_bytes(2, byteorder="little")
return b"\x00" * 2 + init_bytes + manufacturer_data
bluetooth-data-tools-1.26.1/src/bluetooth_data_tools/utils_wrapper.h000066400000000000000000000020121476514471000257350ustar00rootroot00000000000000#include
/**
* Convert the given integer bluetooth address to its hexadecimal string representation.
* The buffer passed in must accept at least 17 bytes. It will NOT be null-terminated.
*/
void _uint64_to_bdaddr(uint64_t address, char bdaddr[17]) {
static const char hex_table[] = "0123456789ABCDEF";
bdaddr[0] = hex_table[(address >> 44) & 0x0F];
bdaddr[1] = hex_table[(address >> 40) & 0x0F];
bdaddr[2] = ':';
bdaddr[3] = hex_table[(address >> 36) & 0x0F];
bdaddr[4] = hex_table[(address >> 32) & 0x0F];
bdaddr[5] = ':';
bdaddr[6] = hex_table[(address >> 28) & 0x0F];
bdaddr[7] = hex_table[(address >> 24) & 0x0F];
bdaddr[8] = ':';
bdaddr[9] = hex_table[(address >> 20) & 0x0F];
bdaddr[10] = hex_table[(address >> 16) & 0x0F];
bdaddr[11] = ':';
bdaddr[12] = hex_table[(address >> 12) & 0x0F];
bdaddr[13] = hex_table[(address >> 8) & 0x0F];
bdaddr[14] = ':';
bdaddr[15] = hex_table[(address >> 4) & 0x0F];
bdaddr[16] = hex_table[address & 0x0F];
}
bluetooth-data-tools-1.26.1/tests/000077500000000000000000000000001476514471000170265ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/tests/__init__.py000066400000000000000000000000001476514471000211250ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/tests/benchmarks/000077500000000000000000000000001476514471000211435ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/tests/benchmarks/__init__.py000066400000000000000000000000001476514471000232420ustar00rootroot00000000000000bluetooth-data-tools-1.26.1/tests/benchmarks/test_int_to_bluetooth_address.py000066400000000000000000000006551476514471000276500ustar00rootroot00000000000000from pytest_codspeed import BenchmarkFixture
from bluetooth_data_tools.utils import (
_int_to_bluetooth_address,
int_to_bluetooth_address,
)
def test_parse_int_to_bluetooth_address_uncached(benchmark: BenchmarkFixture) -> None:
benchmark(lambda: _int_to_bluetooth_address(0))
def test_parse_int_to_bluetooth_address_cached(benchmark: BenchmarkFixture) -> None:
benchmark(lambda: int_to_bluetooth_address(0))
bluetooth-data-tools-1.26.1/tests/benchmarks/test_parse_gap.py000066400000000000000000000131541476514471000245210ustar00rootroot00000000000000# cythonize -X language_level=3 -a -i src/bluetooth_data_tools/gap.py
from pytest_codspeed import BenchmarkFixture
from bluetooth_data_tools import parse_advertisement_data
advs = [
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33163\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x1a\x02\n\x07\n\xffL\x00\x10\x05\x1f\x1c8#\xe2",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B4\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80n\xe0\x9d\x13\x99\x0f\xf3\xe2\x9d\x13\x99\x0f\xf2\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x1a\x02\n\x06\x11\xffL\x00\x0f\x08\xd0\n\x7fnt\x00\x08\x0c\x10\x02#\x04",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x13\xc0\xa8k\xef",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b'\x02\x01\x04\x03\x03\x07\xfe\x10\xff\xa7\x05\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02"\x00\xca\x03\x19\x00\x00\x02\n\x00',
b'\x02\x01\x06\x16\xffL\x00\x061\x00\xcdnI\xa1\x91\xb2\x06\x007\x00\x04\x02\xcb\xd7,"\x04\x08LF0',
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x13\xc0\xa8k\xef",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x02\x01\x06\x05\tRZSS",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x1e\xff\x01ZR\x02\xb4\xe8B\xdb\xcf\xd4\x00\x97\x0c\x03\x01\x02\x03\x04\x05\x06\x07\x08\t\xa1\xa2\xa3\xa4\xa5\xa6",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F331F5\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\t\xffY\x00\xd8.\xad\xcd\r\x85",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33231\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03+\xc0\xa8kZ",
b"\x1b\xffu\x00B\x04\x01\x80n\xe0\x9d\x13\x99\x0f\xf3\xe2\x9d\x13\x99\x0f\xf2\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x05\tRZSS",
b"\x1b\xffu\x00B\x04\x01\x80\xa0\xf8\x04.\xe1\x9f\x19\xfa\x04.\xe1\x9f\x18\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33105\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x16\xffL\x00\x061\x00\x8aO\xa3\xed?\x1e\x05\x00H\x04\x01\x02\xd8E\xf3\xc6\x04\x08Nan",
b"\x02\x01\x1a\x02\n\x06\x0e\xffL\x00\x0f\x05\x90\x00QB\n\x10\x02#\x04",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33231\x03\x19\xc1\x03",
b"\x02\x01\x06\t\xffY\x00\xc5-^\xabRv",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B4\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x1a\x02\n\x06\x0e\xffL\x00\x0f\x05\x90\x00QB\n\x10\x02#\x04",
b"\x02\x01\x06\x16\xffL\x00\x061\x00\x8aO\xa3\xed?\x1e\x05\x00H\x04\x01\x02\xd8E\xf3\xc6\x04\x08Nan",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x89\xc0\xa8j(",
b"\x02\x01\x06\t\xffY\x00\xc5-^\xabRv",
b"\x02\x01\x06\x03\x02$\xfe\x04\xff\xd1\x01\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x02$\xfe\x04\xff\xd1\x01\x00",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b'\x02\x01\x02\x10\xff\xa7\x05\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02"\x00\xca\x02\n\t\x03\x03\x07\xfe',
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x1a\x02\n\x0c\n\xffL\x00\x10\x05\x1b\x1c\x03\xc5\xbe",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F331F5\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33163\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03+\xc0\xa8kZ",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x1a\x02\n\x0c\n\xffL\x00\x10\x05\x1b\x1c\x03\xc5\xbe",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
]
def test_parse_advertisement_data(benchmark: BenchmarkFixture) -> None:
benchmark(lambda: parse_advertisement_data(advs))
bluetooth-data-tools-1.26.1/tests/benchmarks/test_parse_gap_tuple.py000066400000000000000000000135371476514471000257370ustar00rootroot00000000000000# cythonize -X language_level=3 -a -i src/bluetooth_data_tools/gap.py
from pytest_codspeed import BenchmarkFixture
from bluetooth_data_tools import parse_advertisement_data_tuple
from bluetooth_data_tools.gap import _uncached_parse_advertisement_data
advs = (
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33163\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x1a\x02\n\x07\n\xffL\x00\x10\x05\x1f\x1c8#\xe2",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B4\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80n\xe0\x9d\x13\x99\x0f\xf3\xe2\x9d\x13\x99\x0f\xf2\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x1a\x02\n\x06\x11\xffL\x00\x0f\x08\xd0\n\x7fnt\x00\x08\x0c\x10\x02#\x04",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x13\xc0\xa8k\xef",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b'\x02\x01\x04\x03\x03\x07\xfe\x10\xff\xa7\x05\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02"\x00\xca\x03\x19\x00\x00\x02\n\x00',
b'\x02\x01\x06\x16\xffL\x00\x061\x00\xcdnI\xa1\x91\xb2\x06\x007\x00\x04\x02\xcb\xd7,"\x04\x08LF0',
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x13\xc0\xa8k\xef",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x02\x01\x06\x05\tRZSS",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x1e\xff\x01ZR\x02\xb4\xe8B\xdb\xcf\xd4\x00\x97\x0c\x03\x01\x02\x03\x04\x05\x06\x07\x08\t\xa1\xa2\xa3\xa4\xa5\xa6",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F331F5\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\t\xffY\x00\xd8.\xad\xcd\r\x85",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33231\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03+\xc0\xa8kZ",
b"\x1b\xffu\x00B\x04\x01\x80n\xe0\x9d\x13\x99\x0f\xf3\xe2\x9d\x13\x99\x0f\xf2\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x05\tRZSS",
b"\x1b\xffu\x00B\x04\x01\x80\xa0\xf8\x04.\xe1\x9f\x19\xfa\x04.\xe1\x9f\x18\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33105\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x16\xffL\x00\x061\x00\x8aO\xa3\xed?\x1e\x05\x00H\x04\x01\x02\xd8E\xf3\xc6\x04\x08Nan",
b"\x02\x01\x1a\x02\n\x06\x0e\xffL\x00\x0f\x05\x90\x00QB\n\x10\x02#\x04",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33231\x03\x19\xc1\x03",
b"\x02\x01\x06\t\xffY\x00\xc5-^\xabRv",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B4\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x1a\x02\n\x06\x0e\xffL\x00\x0f\x05\x90\x00QB\n\x10\x02#\x04",
b"\x02\x01\x06\x16\xffL\x00\x061\x00\x8aO\xa3\xed?\x1e\x05\x00H\x04\x01\x02\xd8E\xf3\xc6\x04\x08Nan",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03\x89\xc0\xa8j(",
b"\x02\x01\x06\t\xffY\x00\xc5-^\xabRv",
b"\x02\x01\x06\x03\x02$\xfe\x04\xff\xd1\x01\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x02$\xfe\x04\xff\xd1\x01\x00",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b'\x02\x01\x02\x10\xff\xa7\x05\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02"\x00\xca\x02\n\t\x03\x03\x07\xfe',
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x0f\xffi\t`U\xf97\x95\x06.\x00\x00<\x00\x00",
b"\x02\x01\x1a\x02\n\x0c\n\xffL\x00\x10\x05\x1b\x1c\x03\xc5\xbe",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F331F5\x03\x19\xc1\x03",
b"\x1b\xffu\x00B\x04\x01\x80f\x8c\xeaHM\x93;\x8e\xeaHM\x93:\x01\x00\x00\x00\x00\x00\x00",
b"\x02\x01\x06\x05\tRZSS",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330C2\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33208\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330F3\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F33163\x03\x19\xc1\x03",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F3320D\x03\x19\xc1\x03",
b"\x02\x01\x1a\x0b\xffL\x00\t\x06\x03+\xc0\xa8kZ",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
b"\x02\x01\x1a\x02\n\x0c\n\xffL\x00\x10\x05\x1b\x1c\x03\xc5\xbe",
b"\x02\x01\x06\x03\x03\x12\x18\x10\tLOOKin_98F330B5\x03\x19\xc1\x03",
)
def test_parse_advertisement_data_tuple(benchmark: BenchmarkFixture) -> None:
benchmark(lambda: parse_advertisement_data_tuple(advs))
def test_parse_advertisement_data_tuple_uncached(benchmark: BenchmarkFixture) -> None:
benchmark(lambda: _uncached_parse_advertisement_data(advs))
bluetooth-data-tools-1.26.1/tests/test_distance.py000066400000000000000000000012041476514471000222260ustar00rootroot00000000000000"""Test utils."""
from bluetooth_data_tools import calculate_distance_meters
def tests_calculate_distance_meters():
"""Test distance estimate calculation."""
assert calculate_distance_meters(-59, -60) == 1.1352362990362899
assert calculate_distance_meters(59, -60) == 1.183020818815412
assert calculate_distance_meters(12, -80) == 400.0
assert calculate_distance_meters(59, 0) is None
assert calculate_distance_meters(-3, -100) == 400.0
assert calculate_distance_meters(-3, -96) == 400.0
assert calculate_distance_meters(-3, -3) == 1.01076
assert calculate_distance_meters(-4, -3) == 0.056313514709472656
bluetooth-data-tools-1.26.1/tests/test_gap.py000066400000000000000000000401711476514471000212110ustar00rootroot00000000000000import base64
from bluetooth_data_tools import (
parse_advertisement_data,
parse_advertisement_data_tuple,
)
def test_parse_advertisement_data_Prodigio_D83567A4F5A5():
data = [
base64.b64decode("AgoEFglQcm9kaWdpb19EODM1NjdBNEY1QTU="),
base64.b64decode("AgEGEQYbxdWlAgCqneMRKvIQGaoGCf8CJUQJgAcAAg=="),
]
adv = parse_advertisement_data(data)
assert adv.local_name == "Prodigio_D83567A4F5A5"
assert adv.service_uuids == ["06aa1910-f22a-11e3-9daa-0002a5d5c51b"]
assert adv.service_data == {}
assert adv.manufacturer_data == {9474: b"D\t\x80\x07\x00\x02"}
assert adv.tx_power == 4
def test_parse_advertisement_data_unknown_apple_device():
data = [
base64.b64decode("AgEaAgoFCv9MABAFChx3+Vs="),
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {76: b"\x10\x05\n\x1cw\xf9["}
assert adv.tx_power == 5
def test_parse_advertisement_data_empty():
data = [
b"\x00",
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
def test_parse_advertisement_data_flags_only():
data = [
b"\x01\x01\x06",
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
def test_parse_advertisement_data_ignores_invalid():
data = [
b"\x02\x01\x1a\x02\n\x05\n\xffL\x00\x10\x05\n\x1cw\xf9[\x02\x01",
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {76: b"\x10\x05\n\x1cw\xf9["}
assert adv.tx_power == 5
def test_parse_advertisement_data_ignores_zero_type():
data = [
b"\x02\x01\x1a\x02\n\x05\n\xffL\x00\x10\x05\n\x1cw\xf9[\x02\x00",
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {76: b"\x10\x05\n\x1cw\xf9["}
assert adv.tx_power == 5
def test_parse_advertisement_data_unknown_fd3d():
data = [
base64.b64decode("AgEGD/9pCWBV+Tw02tgAEDEAAA=="),
base64.b64decode("BhY9/WcAZA=="),
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {"0000fd3d-0000-1000-8000-00805f9b34fb": b"g\x00d"}
assert adv.manufacturer_data == {2409: b"`U\xf9<4\xda\xd8\x00\x101\x00\x00"}
assert adv.tx_power is None
def test_parse_advertisement_data_moat():
data = [
base64.b64decode("AgEGAwMAEBUWABDfeeOmErMVUHBjVGIcb7kL//8="),
base64.b64decode("AgoAAwMAIAsWAFDfeeOmErO5CwgJTW9hdF9TMg=="),
]
adv = parse_advertisement_data(data)
assert adv.local_name == "Moat_S2"
assert adv.service_uuids == [
"00001000-0000-1000-8000-00805f9b34fb",
"00002000-0000-1000-8000-00805f9b34fb",
]
assert adv.service_data == {
"00001000-0000-1000-8000-00805f9b34fb": b"\xdfy\xe3\xa6\x12\xb3\x15PpcTb"
b"\x1co\xb9\x0b\xff\xff",
"00005000-0000-1000-8000-00805f9b34fb": b"\xdfy\xe3\xa6\x12\xb3\xb9\x0b",
}
assert adv.manufacturer_data == {}
assert adv.tx_power == 0
def test_parse_advertisement_data_unknown_apple_215():
data = [
base64.b64decode("AgEGGv9MAAIV1Ubfl0dXR+++CT4ty90MdxU2zcm1"),
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {
76: b"\x02\x15\xd5F\xdf\x97GWG\xef\xbe\t>-\xcb\xdd\x0cw\x156\xcd\xc9\xb5"
}
assert adv.tx_power is None
def test_parse_advertisement_data_oral_b_toothbrush():
data = [
base64.b64decode("AgEGDv/cAAYyawNSAAEECQAEAwIN/g=="),
base64.b64decode("EglPcmFsLUIgVG9vdGhicnVzaAUSEABQAAIKAA=="),
]
adv = parse_advertisement_data(data)
assert adv.local_name == "Oral-B Toothbrush"
assert adv.service_uuids == ["0000fe0d-0000-1000-8000-00805f9b34fb"]
assert adv.service_data == {}
assert adv.manufacturer_data == {220: b"\x062k\x03R\x00\x01\x04\t\x00\x04"}
assert adv.tx_power == 0
assert parse_advertisement_data_tuple(tuple(data)) == (
"Oral-B Toothbrush",
["0000fe0d-0000-1000-8000-00805f9b34fb"],
{},
{220: b"\x062k\x03R\x00\x01\x04\t\x00\x04"},
0,
)
def test_parse_advertisement_short_local_name():
data = [
base64.b64decode("AgEGFv9MAAYxAOTEm+77PgUADQABAmMRIGUECE5hbg=="),
]
adv = parse_advertisement_data(data)
assert adv.local_name == "Nan"
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {
76: b"\x061\x00\xe4\xc4\x9b\xee\xfb>\x05\x00\r\x00\x01\x02c\x11 e"
}
assert adv.tx_power is None
def test_parse_advertisement_data_32bit_service_data():
data = [
b"\x07\x20\x1a\x02\n\x05\n\xff",
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {"050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff"}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
def test_parse_advertisement_data_128bit_service_data():
data = [
b"\x12\x21\x1a\x02\n\x05\n\xff\x062k\x03R\x00\x01\x04\t\x00\x04",
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {"00090401-0052-036b-3206-ff0a050a021a": b"\x04"}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
def test_parse_advertisement_data_128bit_service_data_tuple():
data = (b"\x12\x21\x1a\x02\n\x05\n\xff\x062k\x03R\x00\x01\x04\t\x00\x04",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {"00090401-0052-036b-3206-ff0a050a021a": b"\x04"}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
def test_parse_advertisement_data_zero_padded():
data = [
bytes.fromhex(
"02.01.06.0E.FF.69.09.FA.62.0F.CF.2D.F2.DA.0F"
".00.22.04.00.09.16.3D.FD.63.C0.56.00.22.04".replace(".", "")
)
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {
"0000fd3d-0000-1000-8000-00805f9b34fb": b'c\xc0V\x00"\x04'
}
assert adv.manufacturer_data == {2409: b'\xfab\x0f\xcf-\xf2\xda\x0f\x00"\x04'}
assert adv.tx_power is None
def test_parse_advertisement_data_zero_padded_scan_included():
data = [
b"\x02\x01\x06\t\xffY\x00\xfe\x024\x9e\xa6\xba\x00\x00\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x07\x1b\xc5\xd5\xa5\x02\x00\xb8"
b'\x9f\xe6\x11M"\x00\r\xa2\xcb\x06\x16\x00\rH\x10\x00\x00\x00\x00\x00\x00\x00'
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == ["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
assert adv.service_data == {"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10\x00"}
assert adv.manufacturer_data == {89: b"\xfe\x024\x9e\xa6\xba"}
assert adv.tx_power is None
def test_parse_advertisement_data_recovers_from_corrupt_data():
data = [
b"\x03\x03\x9f\xfe\x17\x16\x9f\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\xf8"
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == ["0000fe9f-0000-1000-8000-00805f9b34fb"]
assert adv.service_data == {
"0000fe9f-0000-1000-8000-00805f9b34fb": b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
def test_parse_advertisement_data_recovers_from_corrupt_data_2():
data = [
b"\x1a\xff\xc3\x03_\xef5B\xb0I\x0f\xbb\x00&\x01\x00m*"
b"\xb2c\xd8\xb0\x02\n\x00\x00\xf2\x02\x01\x06 "
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {
963: b"_\xef5B\xb0I\x0f\xbb\x00&\x01\x00m*\xb2c\xd8\xb0\x02\n\x00\x00\xf2"
}
assert adv.tx_power is None
def test_parse_advertisement_data_recovers_from_corrupt_data_3():
data = [
b"\x03\x03\x9f\xfe\x17\x16\x9f\xfe\x00\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06 "
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == ["0000fe9f-0000-1000-8000-00805f9b34fb"]
assert adv.service_data == {
"0000fe9f-0000-1000-8000-00805f9b34fb": b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
def test_parse_advertisement_data_recovers_from_corrupt_data_43():
data = [
b"\x1a\xff\xc3\x03_\xef5B\xb3I\x0f\xbb\x00&\x01\x01m*"
b"\xb2c\xd8.\x02\n\x00\x00\xf2\x02\x01\xc6\xf8"
]
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {
963: b"_\xef5B\xb3I\x0f\xbb\x00&\x01\x01m*\xb2c\xd8.\x02\n\x00\x00\xf2"
}
assert adv.tx_power is None
def test_name_parser():
"""Test parsing name from https://github.com/esphome/issues/issues/4838."""
data = (
b"\t\tPineTime\002\001\006\021\007\236\312\334$\016\345\251(340\223\363\243\265\001\000@n",
)
adv = parse_advertisement_data(data)
assert adv.local_name == "PineTime"
assert adv.service_uuids == ["01b5a3f3-9330-3433-28a9-e50e24dcca9e"]
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
"PineTime",
["01b5a3f3-9330-3433-28a9-e50e24dcca9e"],
{},
{},
None,
)
def test_invalid_gap_num():
"""Test skip invalid gap type."""
data = (
b"\t\x00PineTime\002\001\006\021\007\236\312\334$\016\345\251(340\223\363\243\265\001\000@n",
)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == ["01b5a3f3-9330-3433-28a9-e50e24dcca9e"]
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
["01b5a3f3-9330-3433-28a9-e50e24dcca9e"],
{},
{},
None,
)
def test_out_of_bounds_length():
"""Test out of bound length."""
data = (
b"\xff\x00PineTime\002\001\006\021\007\236\312\334$\016\345\251(340\223\363\243\265\001\000@n",
)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
def test_out_of_bounds_length_by_one():
"""Test out of bound length by one."""
data = (b"\x01\x08",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
def test_manufacturer_data_short_by_one():
"""Test short manufacturer data."""
data = (b"\x02\xff\x01",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
def test_manufacturer_data_short_by_two():
"""Test short manufacturer data."""
data = (b"\x02\xff\x01\x01",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
def test_manufacturer_data_short_by_three():
"""Test short manufacturer data."""
data = (b"\x03\xff\x01\x01",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
def test_manufacturer_data_single_byte():
"""Test single byte manufacturer data."""
data = (b"\x04\xff\x01\x01\x01",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {257: b"\x01"}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{257: b"\x01"},
None,
)
def test_service_data_short():
"""Test short service data."""
data = (b"\x02\x16\x01",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
def test_32bit_uuid_short():
"""Test short 32bit uuid data."""
data = (b"\x02 \x01",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
def test_128bit_uuid_short():
"""Test short 128bit uuid data."""
data = (b"\x02!\x01",)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
def test_zero_padded_end():
"""Test name with zero padded end."""
data = (b"\x02\x08a\x00",)
adv = parse_advertisement_data(data)
assert adv.local_name == "a"
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
"a",
[],
{},
{},
None,
)
def test_zero_padded_out_of_bounds_length():
"""Test zero padded out of bound length."""
data = (
b"\x00\xff\x00PineTime\002\001\006\021\007\236\312\334$\016\345\251(340\223\363\243\265\001\000@n",
)
adv = parse_advertisement_data(data)
assert adv.local_name is None
assert adv.service_uuids == []
assert adv.service_data == {}
assert adv.manufacturer_data == {}
assert adv.tx_power is None
assert parse_advertisement_data_tuple(tuple(data)) == (
None,
[],
{},
{},
None,
)
bluetooth-data-tools-1.26.1/tests/test_privacy.py000066400000000000000000000006521476514471000221170ustar00rootroot00000000000000from bluetooth_data_tools import get_cipher_for_irk, resolve_private_address
def test_resolve_private_address():
cipher = get_cipher_for_irk(b"\x00" * 16)
assert resolve_private_address(cipher, "40:01:02:0a:c4:a6")
assert resolve_private_address(cipher, "40:02:03:d2:74:ce")
assert not resolve_private_address(cipher, "40:00:00:d2:74:ce")
assert not resolve_private_address(cipher, "00:01:ff:a0:3a:76")
bluetooth-data-tools-1.26.1/tests/test_utils.py000066400000000000000000000030001476514471000215700ustar00rootroot00000000000000from bluetooth_data_tools import (
address_to_bytes,
human_readable_name,
int_to_bluetooth_address,
mac_to_int,
manufacturer_data_to_raw,
newest_manufacturer_data,
short_address,
)
def test_int_to_bluetooth_address():
assert int_to_bluetooth_address(0) == "00:00:00:00:00:00"
assert int_to_bluetooth_address(1) == "00:00:00:00:00:01"
assert int_to_bluetooth_address(0xFFFFFFFFFFFF) == "FF:FF:FF:FF:FF:FF"
assert int_to_bluetooth_address(0x123456789ABC) == "12:34:56:78:9A:BC"
assert int_to_bluetooth_address(0xDEF012345678) == "DE:F0:12:34:56:78"
def test_newest_manufacturer_data():
data = {1: b"\x01\x02\x03\x04"}
assert newest_manufacturer_data(data) == b"\x01\x02\x03\x04"
assert newest_manufacturer_data({}) is None
def test_address_to_bytes():
assert address_to_bytes("00:00:00:00:00:00") == b"\x00\x00\x00\x00"
assert address_to_bytes("a-c-b") == b"\x00\x00\x00\x00"
def test_manufacturer_data_to_raw():
assert (
manufacturer_data_to_raw(1, b"\x01\x02\x03\x04")
== b"\x00\x00\x01\x00\x01\x02\x03\x04"
)
def test_short_address():
assert short_address("AA:BB:CC:DD:EE:FF") == "EEFF"
def test_human_readable_name():
assert (
human_readable_name("My Device", "Your Device", "AA:BB:CC:DD:EE:FF")
== "My Device (EEFF)"
)
def test_mac_to_int():
assert mac_to_int("00:00:00:00:00:00") == 0
assert mac_to_int("00:00:00:00:00:01") == 1
assert mac_to_int("FF:FF:FF:FF:FF:FF") == 0xFFFFFFFFFFFF