pax_global_header 0000666 0000000 0000000 00000000064 14514041106 0014506 g ustar 00root root 0000000 0000000 52 comment=c72076918d710c18733fa91a98d60fcd57e9f1de
pjanuario-pyecoforest-d2c4b4f/ 0000775 0000000 0000000 00000000000 14514041106 0016530 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/.all-contributorsrc 0000664 0000000 0000000 00000001103 14514041106 0022354 0 ustar 00root root 0000000 0000000 {
"projectName": "pyecoforest",
"projectOwner": "pjanuario",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 80,
"commit": true,
"commitConvention": "angular",
"contributors": [
{
"login": "pjanuario",
"name": "Pedro Januário",
"avatar_url": "https://avatars.githubusercontent.com/u/477458?v=4",
"profile": "http://shared-mind.tumblr.com/",
"contributions": [
"code",
"ideas",
"doc"
]
}
],
"contributorsPerLine": 7,
"skipCi": true
}
pjanuario-pyecoforest-d2c4b4f/.ci 0000664 0000000 0000000 00000000000 14514041106 0017112 0 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/.copier-answers.yml 0000664 0000000 0000000 00000001014 14514041106 0022266 0 ustar 00root root 0000000 0000000 # Changes here will be overwritten by Copier
_commit: 0f501ed
_src_path: gh:browniebroke/pypackage-template
add_me_as_contributor: true
copyright_year: '2023'
documentation: true
email: prnjanuario@gmail.com
full_name: Pedro Januário
github_username: pjanuario
initial_commit: true
open_source_license: MIT
package_name: pyecoforest
project_name: pyecoforest
project_short_description: Python library for ecoforest local device.
project_slug: pyecoforest
run_poetry_install: true
setup_github: true
setup_pre_commit: true
pjanuario-pyecoforest-d2c4b4f/.editorconfig 0000664 0000000 0000000 00000000444 14514041106 0021207 0 ustar 00root root 0000000 0000000 # 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
pjanuario-pyecoforest-d2c4b4f/.github/ 0000775 0000000 0000000 00000000000 14514041106 0020070 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/.github/FUNDING.yml 0000664 0000000 0000000 00000000026 14514041106 0021703 0 ustar 00root root 0000000 0000000 github: ["pjanuario"]
pjanuario-pyecoforest-d2c4b4f/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14514041106 0022253 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/.github/ISSUE_TEMPLATE/1-bug_report.md 0000664 0000000 0000000 00000000422 14514041106 0025101 0 ustar 00root root 0000000 0000000 ---
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.
pjanuario-pyecoforest-d2c4b4f/.github/ISSUE_TEMPLATE/2-feature-request.md 0000664 0000000 0000000 00000000672 14514041106 0026062 0 ustar 00root root 0000000 0000000 ---
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.
pjanuario-pyecoforest-d2c4b4f/.github/labels.toml 0000664 0000000 0000000 00000003515 14514041106 0022233 0 ustar 00root root 0000000 0000000 [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"
pjanuario-pyecoforest-d2c4b4f/.github/workflows/ 0000775 0000000 0000000 00000000000 14514041106 0022125 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/.github/workflows/ci.yml 0000664 0000000 0000000 00000005355 14514041106 0023253 0 ustar 00root root 0000000 0000000 name: 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@v4
with:
python-version: 3.x
- uses: pre-commit/action@v3.0.0
# 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@v5.4.3
test:
strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12-dev"
os:
- ubuntu-latest
- windows-latest
- macOS-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: snok/install-poetry@v1.3.4
- name: Install Dependencies
run: poetry install
shell: bash
- name: Test with Pytest
run: poetry run pytest --cov-report=xml
shell: bash
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
release:
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
contents: write
needs:
- test
- lint
- commitlint
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@v8.0.8
if: github.ref_name != 'main'
with:
root_options: --noop
# On main branch: actual PSR + upload to PyPI & GitHub
- name: Release
id: semantic_release
uses: python-semantic-release/python-semantic-release@v8.0.8
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.semantic_release.outputs.released == 'true'
with:
password: ${{ secrets.PYPI_TOKEN }}
- name: Publish package distributions to GitHub Releases
uses: python-semantic-release/upload-to-gh-release@main
if: steps.semantic_release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
pjanuario-pyecoforest-d2c4b4f/.github/workflows/hacktoberfest.yml 0000664 0000000 0000000 00000000534 14514041106 0025476 0 ustar 00root root 0000000 0000000 name: 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 }}
pjanuario-pyecoforest-d2c4b4f/.github/workflows/issue-manager.yml 0000664 0000000 0000000 00000001340 14514041106 0025406 0 ustar 00root root 0000000 0000000 name: 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.4.0
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."
}
}
pjanuario-pyecoforest-d2c4b4f/.github/workflows/labels.yml 0000664 0000000 0000000 00000001041 14514041106 0024106 0 ustar 00root root 0000000 0000000 name: Sync Github labels
on:
push:
branches:
- main
paths:
- ".github/**"
jobs:
labels:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.x
- 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
pjanuario-pyecoforest-d2c4b4f/.github/workflows/poetry-upgrade.yml 0000664 0000000 0000000 00000000340 14514041106 0025614 0 ustar 00root root 0000000 0000000 name: Upgrader
on:
workflow_dispatch:
schedule:
- cron: "25 1 14 * *"
jobs:
upgrade:
uses: browniebroke/github-actions/.github/workflows/poetry-upgrade.yml@v1
secrets:
gh_pat: ${{ secrets.GH_PAT }}
pjanuario-pyecoforest-d2c4b4f/.gitignore 0000664 0000000 0000000 00000004110 14514041106 0020514 0 ustar 00root root 0000000 0000000 # 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 {{package_name}} settings
.spyderproject
.spyproject
# Rope {{package_name}} 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/
pjanuario-pyecoforest-d2c4b4f/.gitpod.yml 0000664 0000000 0000000 00000000306 14514041106 0020616 0 ustar 00root root 0000000 0000000 tasks:
- command: |
pip install poetry
PIP_USER=false poetry install
- command: |
pip install pre-commit
pre-commit install
PIP_USER=false pre-commit install-hooks
pjanuario-pyecoforest-d2c4b4f/.idea/ 0000775 0000000 0000000 00000000000 14514041106 0017510 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/.idea/pyecoforest.iml 0000664 0000000 0000000 00000000515 14514041106 0022556 0 ustar 00root root 0000000 0000000
pjanuario-pyecoforest-d2c4b4f/.idea/watcherTasks.xml 0000664 0000000 0000000 00000005253 14514041106 0022702 0 ustar 00root root 0000000 0000000
pjanuario-pyecoforest-d2c4b4f/.idea/workspace.xml 0000664 0000000 0000000 00000002736 14514041106 0022240 0 ustar 00root root 0000000 0000000
pjanuario-pyecoforest-d2c4b4f/.pre-commit-config.yaml 0000664 0000000 0000000 00000003074 14514041106 0023015 0 ustar 00root root 0000000 0000000 # See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: "CHANGELOG.md|.copier-answers.yml|.all-contributorsrc|tests/fixtures/"
default_stages: [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: v3.8.2
hooks:
- id: commitizen
stages: [commit-msg]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.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
- repo: https://github.com/python-poetry/poetry
rev: 1.6.1
hooks:
- id: poetry-check
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.3
hooks:
- id: prettier
args: ["--tab-width", "2"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.288
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/codespell-project/codespell
rev: v2.2.5
hooks:
- id: codespell
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
hooks:
- id: mypy
additional_dependencies: []
pjanuario-pyecoforest-d2c4b4f/.readthedocs.yml 0000664 0000000 0000000 00000001143 14514041106 0021615 0 ustar 00root root 0000000 0000000 # 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.9"
jobs:
post_create_environment:
# Install poetry
- pip install poetry
# Tell poetry to not use a virtual environment
- poetry config virtualenvs.create false
post_install:
# Install dependencies
- poetry install --with docs
# Build documentation in the docs directory with Sphinx
sphinx:
configuration: docs/conf.py
pjanuario-pyecoforest-d2c4b4f/CHANGELOG 0000664 0000000 0000000 00000004647 14514041106 0017755 0 ustar 00root root 0000000 0000000 # Changelog
## v0.4.0 (2023-10-18)
### Feature
- Feat: added additional sensors (#14)
feat: added additional sensors (#14)
([`7148db8`](https://github.com/pjanuario/pyecoforest/commit/7148db848147fc0484b7c7411e0e27d99b900d8c))
- Feat: added additional sensors
([`9fa97d6`](https://github.com/pjanuario/pyecoforest/commit/9fa97d6d044c107f32b5095dc81f1d6adeb08b40))
### Unknown
- Merge pull request #13 from pjanuario/update/poetry-upgrade
chore(deps): upgrade dependencies ([`f7a7eca`](https://github.com/pjanuario/pyecoforest/commit/f7a7ecadc6f9c25be6d753a8342bd42bb64e73cf))
## v0.3.0 (2023-09-21)
### Feature
- Feat: improve sdk error handling
([`7671271`](https://github.com/pjanuario/pyecoforest/commit/767127178007f52634f769e33e3e5ad296ab9fbb))
- Feat: improve sdk error handling
([`65ab966`](https://github.com/pjanuario/pyecoforest/commit/65ab9669da01a443b55a5b7d18fe64da88678d48))
## v0.2.0 (2023-09-17)
### Feature
- Feat: refactored the sdk interface and added other device properties and functionalities (#10)
([`adb8d92`](https://github.com/pjanuario/pyecoforest/commit/adb8d9240142c8802f7ce5a58008b231fab2e26b))
- Feat: refactored the sdk interface and added other device properties and functionalities
([`e7df1d2`](https://github.com/pjanuario/pyecoforest/commit/e7df1d2cb27b8af7d9a4bdea34af748de73f294c))
### Fix
- Fix: downgrade httpx minimal requirement
([`b8eeb95`](https://github.com/pjanuario/pyecoforest/commit/b8eeb95ef2339ab48563d9e5ae579274f20c3573))
## v0.1.0 (2023-09-14)
### Feature
- Feat: retrieve ecoforest device status
this merge requests adds a new feature to the module allowing a user to retrieve the status of the device. ([`f47ab4f`](https://github.com/pjanuario/pyecoforest/commit/f47ab4f9228f4de994c2159659a442afa9008785))
- Feat: retrieve ecoforest device status
([`d048053`](https://github.com/pjanuario/pyecoforest/commit/d04805356d6aa0609e039309425601ef271920f8))
### Test
- Test: fix broken test matrix and ensure support from python^3.10
([`ac02ddf`](https://github.com/pjanuario/pyecoforest/commit/ac02ddfc5028960471e2610d6e66dee78fdfa902))
- Test: fix broken tests and precommit configuration to prevent changing fixtures
([`87561da`](https://github.com/pjanuario/pyecoforest/commit/87561da3b7337f34cff82eace850e06e94f3f8ef))
### Documentation
- Docs: add @pjanuario as a contributor
([`ef36592`](https://github.com/pjanuario/pyecoforest/commit/ef36592de1c93d8633a2f87820ad01dbbf3c67e2))
pjanuario-pyecoforest-d2c4b4f/CHANGELOG.md 0000664 0000000 0000000 00000000014 14514041106 0020334 0 ustar 00root root 0000000 0000000 # Changelog
pjanuario-pyecoforest-d2c4b4f/CONTRIBUTING.md 0000664 0000000 0000000 00000007422 14514041106 0020766 0 ustar 00root root 0000000 0000000 # 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
pyecoforest could always use more documentation, whether as part of the official pyecoforest 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/pyecoforest.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/pjanuario/pyecoforest/issues
pjanuario-pyecoforest-d2c4b4f/LICENSE 0000664 0000000 0000000 00000002061 14514041106 0017534 0 ustar 00root root 0000000 0000000
MIT License
Copyright (c) 2023 Pedro Januário
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
pjanuario-pyecoforest-d2c4b4f/README.md 0000664 0000000 0000000 00000010077 14514041106 0020014 0 ustar 00root root 0000000 0000000 # pyecoforest
Python library for ecoforest local device.
## Installation
Install this via pip (or your favourite package manager):
`pip install pyecoforest`
## 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
[Copier](https://copier.readthedocs.io/) and the
[browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
project template.
pjanuario-pyecoforest-d2c4b4f/commitlint.config.js 0000664 0000000 0000000 00000000364 14514041106 0022514 0 ustar 00root root 0000000 0000000 module.exports = {
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],
},
};
pjanuario-pyecoforest-d2c4b4f/docs/ 0000775 0000000 0000000 00000000000 14514041106 0017460 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/docs/Makefile 0000664 0000000 0000000 00000001172 14514041106 0021121 0 ustar 00root root 0000000 0000000 # Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
pjanuario-pyecoforest-d2c4b4f/docs/_static/ 0000775 0000000 0000000 00000000000 14514041106 0021106 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/docs/_static/.gitkeep 0000664 0000000 0000000 00000000000 14514041106 0022525 0 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/docs/changelog.md 0000664 0000000 0000000 00000000042 14514041106 0021725 0 ustar 00root root 0000000 0000000 ```{include} ../CHANGELOG.md
```
pjanuario-pyecoforest-d2c4b4f/docs/conf.py 0000664 0000000 0000000 00000001221 14514041106 0020753 0 ustar 00root root 0000000 0000000 # Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# Project information
project = "pyecoforest"
copyright = "2023, Pedro Januário"
author = "Pedro Januário"
release = "0.0.0"
# 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"]
pjanuario-pyecoforest-d2c4b4f/docs/contributing.md 0000664 0000000 0000000 00000000045 14514041106 0022510 0 ustar 00root root 0000000 0000000 ```{include} ../CONTRIBUTING.md
```
pjanuario-pyecoforest-d2c4b4f/docs/index.md 0000664 0000000 0000000 00000000350 14514041106 0021107 0 ustar 00root root 0000000 0000000 # Welcome to pyecoforest documentation!
```{toctree}
:caption: Installation & Usage
:maxdepth: 2
installation
usage
```
```{toctree}
:caption: Project Info
:maxdepth: 2
changelog
contributing
```
```{include} ../README.md
```
pjanuario-pyecoforest-d2c4b4f/docs/installation.md 0000664 0000000 0000000 00000000264 14514041106 0022505 0 ustar 00root root 0000000 0000000 # Installation
The package is published on [PyPI](https://pypi.org/project/pyecoforest/) and can be installed with `pip` (or any equivalent):
```bash
pip install pyecoforest
```
pjanuario-pyecoforest-d2c4b4f/docs/make.bat 0000664 0000000 0000000 00000001375 14514041106 0021073 0 ustar 00root root 0000000 0000000 @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
pjanuario-pyecoforest-d2c4b4f/docs/usage.md 0000664 0000000 0000000 00000000141 14514041106 0021102 0 ustar 00root root 0000000 0000000 # Usage
To use this package, import it:
```python
import pyecoforest
```
TODO: Document usage
pjanuario-pyecoforest-d2c4b4f/poetry.lock 0000664 0000000 0000000 00000177045 14514041106 0020742 0 ustar 00root root 0000000 0000000 # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
[[package]]
name = "alabaster"
version = "0.7.13"
description = "A configurable sidebar-enabled Sphinx theme"
optional = false
python-versions = ">=3.6"
files = [
{file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"},
{file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"},
]
[[package]]
name = "anyio"
version = "4.0.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
python-versions = ">=3.8"
files = [
{file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"},
{file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"},
]
[package.dependencies]
exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
idna = ">=2.8"
sniffio = ">=1.1"
[package.extras]
doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"]
test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
trio = ["trio (>=0.22)"]
[[package]]
name = "babel"
version = "2.13.0"
description = "Internationalization utilities"
optional = false
python-versions = ">=3.7"
files = [
{file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"},
{file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"},
]
[package.extras]
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
[[package]]
name = "beautifulsoup4"
version = "4.12.2"
description = "Screen-scraping library"
optional = false
python-versions = ">=3.6.0"
files = [
{file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"},
{file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"},
]
[package.dependencies]
soupsieve = ">1.2"
[package.extras]
html5lib = ["html5lib"]
lxml = ["lxml"]
[[package]]
name = "certifi"
version = "2023.7.22"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
]
[[package]]
name = "charset-normalizer"
version = "3.3.0"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"},
{file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"},
{file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"},
{file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"},
{file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"},
{file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"},
{file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"},
{file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"},
{file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"},
{file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"},
{file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"},
{file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"},
{file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"},
{file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"},
{file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"},
{file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"},
{file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"},
{file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"},
{file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"},
{file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"},
{file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"},
{file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"},
{file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"},
{file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"},
{file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"},
{file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"},
{file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"},
{file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"},
{file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"},
{file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"},
{file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"},
{file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"},
{file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"},
{file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"},
{file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"},
{file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"},
{file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"},
{file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"},
{file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"},
{file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"},
{file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"},
{file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"},
{file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"},
{file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"},
{file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"},
{file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"},
{file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"},
{file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"},
{file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"},
{file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"},
{file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"},
{file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"},
{file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"},
{file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"},
{file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"},
{file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"},
{file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"},
{file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"},
{file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"},
{file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"},
{file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"},
{file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"},
{file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"},
{file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"},
{file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"},
{file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"},
{file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"},
{file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"},
{file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"},
{file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"},
{file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"},
{file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"},
{file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"},
{file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"},
{file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"},
{file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"},
{file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"},
{file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"},
]
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "coverage"
version = "7.3.2"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"},
{file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"},
{file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"},
{file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"},
{file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"},
{file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"},
{file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"},
{file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"},
{file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"},
{file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"},
{file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"},
{file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"},
{file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"},
{file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"},
{file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"},
{file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"},
{file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"},
{file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"},
{file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"},
{file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"},
{file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"},
{file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"},
{file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"},
{file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"},
{file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"},
{file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"},
{file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"},
{file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"},
{file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"},
{file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"},
{file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"},
{file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"},
{file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"},
{file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"},
{file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"},
{file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"},
{file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"},
{file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"},
{file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"},
{file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"},
{file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"},
{file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"},
{file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"},
{file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"},
{file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"},
{file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"},
{file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"},
{file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"},
{file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"},
{file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"},
{file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"},
{file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"},
]
[package.dependencies]
tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
[package.extras]
toml = ["tomli"]
[[package]]
name = "docutils"
version = "0.20.1"
description = "Docutils -- Python Documentation Utilities"
optional = false
python-versions = ">=3.7"
files = [
{file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"},
{file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
]
[[package]]
name = "exceptiongroup"
version = "1.1.3"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"},
{file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"},
]
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "furo"
version = "2023.9.10"
description = "A clean customisable Sphinx documentation theme."
optional = false
python-versions = ">=3.8"
files = [
{file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"},
{file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"},
]
[package.dependencies]
beautifulsoup4 = "*"
pygments = ">=2.7"
sphinx = ">=6.0,<8.0"
sphinx-basic-ng = "*"
[[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"
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 = "httpcore"
version = "0.18.0"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
files = [
{file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"},
{file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"},
]
[package.dependencies]
anyio = ">=3.0,<5.0"
certifi = "*"
h11 = ">=0.13,<0.15"
sniffio = "==1.*"
[package.extras]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
[[package]]
name = "httpx"
version = "0.25.0"
description = "The next generation HTTP client."
optional = false
python-versions = ">=3.8"
files = [
{file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"},
{file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"},
]
[package.dependencies]
certifi = "*"
httpcore = ">=0.18.0,<0.19.0"
idna = "*"
sniffio = "*"
[package.extras]
brotli = ["brotli", "brotlicffi"]
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
[[package]]
name = "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.*"
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"
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.2"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
]
[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"
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 = "2.1.3"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.7"
files = [
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
{file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
{file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"},
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"},
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"},
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"},
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"},
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"},
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"},
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"},
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"},
{file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"},
{file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"},
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"},
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"},
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"},
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"},
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"},
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"},
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"},
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"},
{file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"},
{file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"},
{file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
]
[[package]]
name = "mdit-py-plugins"
version = "0.4.0"
description = "Collection of plugins for markdown-it-py"
optional = false
python-versions = ">=3.8"
files = [
{file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"},
{file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"},
]
[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"
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 = "2.0.0"
description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser,"
optional = false
python-versions = ">=3.8"
files = [
{file = "myst_parser-2.0.0-py3-none-any.whl", hash = "sha256:7c36344ae39c8e740dad7fdabf5aa6fc4897a813083c6cc9990044eb93656b14"},
{file = "myst_parser-2.0.0.tar.gz", hash = "sha256:ea929a67a6a0b1683cdbe19b8d2e724cd7643f8aa3e7bb18dd65beac3483bead"},
]
[package.dependencies]
docutils = ">=0.16,<0.21"
jinja2 = "*"
markdown-it-py = ">=3.0,<4.0"
mdit-py-plugins = ">=0.4,<1.0"
pyyaml = "*"
sphinx = ">=6,<8"
[package.extras]
code-style = ["pre-commit (>=3.0,<4.0)"]
linkify = ["linkify-it-py (>=2.0,<3.0)"]
rtd = ["ipython", "pydata-sphinx-theme (==v0.13.0rc4)", "sphinx-autodoc2 (>=0.4.2,<0.5.0)", "sphinx-book-theme (==1.0.0rc2)", "sphinx-copybutton", "sphinx-design2", "sphinx-pyscript", "sphinx-tippy (>=0.3.1)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.8.2,<0.9.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"]
testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=7,<8)", "pytest-cov", "pytest-param-files (>=0.3.4,<0.4.0)", "pytest-regressions", "sphinx-pytest"]
testing-docutils = ["pygments", "pytest (>=7,<8)", "pytest-param-files (>=0.3.4,<0.4.0)"]
[[package]]
name = "packaging"
version = "23.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
]
[[package]]
name = "pluggy"
version = "1.3.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
{file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pygments"
version = "2.16.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.7"
files = [
{file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"},
{file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"},
]
[package.extras]
plugins = ["importlib-metadata"]
[[package]]
name = "pytest"
version = "7.4.2"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"},
{file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-asyncio"
version = "0.21.1"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"},
{file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"},
]
[package.dependencies]
pytest = ">=7.0.0"
[package.extras]
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
[[package]]
name = "pytest-cov"
version = "3.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.6"
files = [
{file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
{file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
]
[package.dependencies]
coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "pyyaml"
version = "6.0.1"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]
[[package]]
name = "requests"
version = "2.31.0"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.7"
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "respx"
version = "0.20.2"
description = "A utility for mocking out the Python HTTPX and HTTP Core libraries."
optional = false
python-versions = ">=3.7"
files = [
{file = "respx-0.20.2-py2.py3-none-any.whl", hash = "sha256:ab8e1cf6da28a5b2dd883ea617f8130f77f676736e6e9e4a25817ad116a172c9"},
{file = "respx-0.20.2.tar.gz", hash = "sha256:07cf4108b1c88b82010f67d3c831dae33a375c7b436e54d87737c7f9f99be643"},
]
[package.dependencies]
httpx = ">=0.21.0"
[[package]]
name = "sniffio"
version = "1.3.0"
description = "Sniff out which async library your code is running under"
optional = false
python-versions = ">=3.7"
files = [
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
]
[[package]]
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
optional = false
python-versions = "*"
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.5"
description = "A modern CSS selector implementation for Beautiful Soup."
optional = false
python-versions = ">=3.8"
files = [
{file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
{file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
]
[[package]]
name = "sphinx"
version = "7.2.6"
description = "Python documentation generator"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"},
{file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"},
]
[package.dependencies]
alabaster = ">=0.7,<0.8"
babel = ">=2.9"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
docutils = ">=0.18.1,<0.21"
imagesize = ">=1.3"
Jinja2 = ">=3.0"
packaging = ">=21.0"
Pygments = ">=2.14"
requests = ">=2.25.0"
snowballstemmer = ">=2.0"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
sphinxcontrib-htmlhelp = ">=2.0.0"
sphinxcontrib-jsmath = "*"
sphinxcontrib-qthelp = "*"
sphinxcontrib-serializinghtml = ">=1.1.9"
[package.extras]
docs = ["sphinxcontrib-websupport"]
lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"]
test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"]
[[package]]
name = "sphinx-basic-ng"
version = "1.0.0b2"
description = "A modern skeleton for Sphinx themes."
optional = false
python-versions = ">=3.7"
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 = "sphinxcontrib-applehelp"
version = "1.0.7"
description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl", hash = "sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d"},
{file = "sphinxcontrib_applehelp-1.0.7.tar.gz", hash = "sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"},
]
[package.dependencies]
Sphinx = ">=5"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-devhelp"
version = "1.0.5"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl", hash = "sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"},
{file = "sphinxcontrib_devhelp-1.0.5.tar.gz", hash = "sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212"},
]
[package.dependencies]
Sphinx = ">=5"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-htmlhelp"
version = "2.0.4"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl", hash = "sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"},
{file = "sphinxcontrib_htmlhelp-2.0.4.tar.gz", hash = "sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a"},
]
[package.dependencies]
Sphinx = ">=5"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["html5lib", "pytest"]
[[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"
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 = "1.0.6"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl", hash = "sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"},
{file = "sphinxcontrib_qthelp-1.0.6.tar.gz", hash = "sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d"},
]
[package.dependencies]
Sphinx = ">=5"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-serializinghtml"
version = "1.1.9"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl", hash = "sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"},
{file = "sphinxcontrib_serializinghtml-1.1.9.tar.gz", hash = "sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54"},
]
[package.dependencies]
Sphinx = ">=5"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.7"
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
[[package]]
name = "urllib3"
version = "2.0.6"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.7"
files = [
{file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"},
{file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "cf4115f9a63b1499de889ada45737cbba905df46b2a14c79f83343a2d153820c"
pjanuario-pyecoforest-d2c4b4f/pyproject.toml 0000664 0000000 0000000 00000006336 14514041106 0021454 0 ustar 00root root 0000000 0000000 [tool.poetry]
name = "pyecoforest"
version = "0.4.0"
description = "Python library for ecoforest local device."
authors = ["Pedro Januário "]
license = "MIT"
readme = "README.md"
repository = "https://github.com/pjanuario/pyecoforest"
documentation = "https://pyecoforest.readthedocs.io"
classifiers = [
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Natural Language :: English",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries",
]
packages = [
{ include = "pyecoforest", from = "src" },
]
[tool.poetry.urls]
"Bug Tracker" = "https://github.com/pjanuario/pyecoforest/issues"
"Changelog" = "https://github.com/pjanuario/pyecoforest/blob/main/CHANGELOG.md"
[tool.poetry.dependencies]
python = "^3.10"
httpx = ">=0.24.0"
[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
pytest-cov = "^3.0"
respx = "^0.20.2"
pytest-asyncio = "^0.21.1"
[tool.poetry.group.docs]
optional = true
[tool.poetry.group.docs.dependencies]
myst-parser = ">=0.16"
sphinx = ">=4.0"
furo = ">=2023.5.20"
[tool.semantic_release]
branch = "main"
version_toml = ["pyproject.toml:tool.poetry.version"]
version_variable = "src/pyecoforest/__init__.py:__version__"
build_command = "pip install poetry && poetry build"
[tool.semantic_release.changelog]
exclude_commit_patterns = [
"chore*",
"ci*",
]
[tool.semantic_release.changelog.environment]
keep_trailing_newline = true
[tool.pytest.ini_options]
addopts = "-v -Wdefault --cov=pyecoforest --cov-report=term-missing:skip-covered"
pythonpath = ["src"]
[tool.coverage.run]
branch = true
[tool.coverage.report]
exclude_also = [
"pragma: no cover",
"@overload",
"if TYPE_CHECKING",
"raise NotImplementedError",
'if __name__ == "__main__":',
"_LOGGER.debug",
]
[tool.ruff]
target-version = "py38"
line-length = 88
ignore = [
"D203", # 1 blank line required before class docstring
"D212", # Multi-line docstring summary should start at the first line
"D100", # Missing docstring in public module
"D104", # Missing docstring in public package
"D107", # Missing docstring in `__init__`
"D401", # First line of docstring should be in imperative mood
]
select = [
"B", # flake8-bugbear
"D", # flake8-docstrings
"C4", # flake8-comprehensions
"S", # flake8-bandit
"F", # pyflake
"E", # pycodestyle
"W", # pycodestyle
"UP", # pyupgrade
"I", # isort
"RUF", # ruff specific
]
[tool.ruff.per-file-ignores]
"tests/**/*" = [
"D100",
"D101",
"D102",
"D103",
"D104",
"S101",
]
"setup.py" = ["D100"]
"conftest.py" = ["D100"]
"docs/conf.py" = ["D100"]
[tool.ruff.isort]
known-first-party = ["pyecoforest", "tests"]
[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 = "docs.*"
ignore_errors = true
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
pjanuario-pyecoforest-d2c4b4f/renovate.json 0000664 0000000 0000000 00000000101 14514041106 0021236 0 ustar 00root root 0000000 0000000 {
"extends": ["github>browniebroke/renovate-configs:python"]
}
pjanuario-pyecoforest-d2c4b4f/setup.py 0000664 0000000 0000000 00000000361 14514041106 0020242 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# This is a shim to allow GitHub to detect the package, build is done with poetry
# Taken from https://github.com/Textualize/rich
import setuptools
if __name__ == "__main__":
setuptools.setup(name="pyecoforest")
pjanuario-pyecoforest-d2c4b4f/src/ 0000775 0000000 0000000 00000000000 14514041106 0017317 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/ 0000775 0000000 0000000 00000000000 14514041106 0021661 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/__init__.py 0000664 0000000 0000000 00000000026 14514041106 0023770 0 ustar 00root root 0000000 0000000 __version__ = "0.0.0"
pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/api.py 0000664 0000000 0000000 00000010641 14514041106 0023006 0 ustar 00root root 0000000 0000000 import logging
from http import HTTPStatus
from typing import Any
import httpx
from pyecoforest.models.device import Device
from .const import (
API_ALARMS_OP,
API_SET_POWER_OP,
API_SET_STATE_OP,
API_SET_TEMP_OP,
API_STATS_OP,
API_STATUS_OP,
LOCAL_TIMEOUT,
URL_CGI,
)
from .exceptions import EcoforestAuthenticationRequired, EcoforestConnectionError
from .ssl import NO_VERIFY_SSL_CONTEXT
_LOGGER = logging.getLogger(__name__)
class EcoforestApi:
"""Class for communicating with an ecoforest device."""
def __init__(
self,
host: str,
auth: httpx.BasicAuth | None = None,
client: httpx.AsyncClient | None = None,
timeout: float | httpx.Timeout | None = None,
) -> None:
self._host = host
self._auth = auth
# We use our own httpx client session so we can disable SSL verification,
# the device use self-signed SSL certs.
self._timeout = timeout or LOCAL_TIMEOUT
self._client = client or httpx.AsyncClient(
base_url=self._host, verify=NO_VERIFY_SSL_CONTEXT
) # nosec
async def get(self) -> Device:
"""Retrieve ecoforest information from api."""
return Device.build(
{
"status": await self._status(),
"stats": await self._stats(),
"alarms": await self._alarms(),
}
)
async def turn(self, on: bool | None = False) -> Device:
"""Turn device on and off."""
await self._request(
data={"idOperacion": API_SET_STATE_OP, "on_off": 1 if on else 0}
)
return await self.get()
async def set_temperature(self, target: float) -> Device:
"""Set device target temperature."""
await self._request(
data={"idOperacion": API_SET_TEMP_OP, "temperatura": target}
)
return await self.get()
async def set_power(self, target: int) -> Device:
"""Set device target power."""
await self._request(data={"idOperacion": API_SET_POWER_OP, "potencia": target})
return await self.get()
async def _request(self, data: dict[str, Any] | None = None) -> httpx.Response:
"""Make a request to the device."""
if _LOGGER.isEnabledFor(logging.DEBUG):
_LOGGER.debug("Sending POST to %s with data %s", URL_CGI, data)
try:
response = await self._client.post(
URL_CGI,
auth=self._auth,
timeout=self._timeout,
data=data,
)
response.raise_for_status()
except httpx.TimeoutException as error:
raise EcoforestConnectionError(
"Timeout occurred while connecting to the device."
) from error
except httpx.HTTPError as error:
if error.response.status_code in (
HTTPStatus.UNAUTHORIZED,
HTTPStatus.FORBIDDEN,
):
raise EcoforestAuthenticationRequired(
error.response.status_code
) from error
raise EcoforestConnectionError(
"Error occurred while communicating with device."
) from error
parsed = self._parse(response.text)
if _LOGGER.isEnabledFor(logging.DEBUG):
_LOGGER.debug("Received from POST with data %s", parsed)
return parsed
async def _status(self) -> dict[str, str]:
"""Retrieve ecoforest status."""
return await self._request(data={"idOperacion": API_STATUS_OP})
async def _stats(self) -> dict[str, str]:
"""Retrieve ecoforest stats."""
return await self._request(data={"idOperacion": API_STATS_OP})
async def _alarms(self) -> dict[str, str]:
"""Retrieve ecoforest information from api."""
return await self._request(
data={
"idOperacion": API_ALARMS_OP,
}
)
def _parse(self, response: str) -> dict[str, str]:
"""Parse request data and return as dictionary."""
# discard last line ?
reply = {}
for e in response.split("\n")[:-1]:
pair = e.split("=")
# discard lines without pairs
if len(pair) == 2:
reply[pair[0]] = pair[1]
# Remove all white spaces from bad response from ecoforest ...
reply = {x.translate({32: None}): y for x, y in reply.items()}
return reply
pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/const.py 0000664 0000000 0000000 00000001025 14514041106 0023357 0 ustar 00root root 0000000 0000000 import httpx
# API CGI base path
URL_CGI = "/recepcion_datos_4.cgi"
MODEL_NAME = "Cordoba glass"
SUPPORTED_MODELS = ["CC2014_v2"] # Cordoba glass
# API status operation code
API_STATUS_OP = 1002
API_STATS_OP = 1020
API_ALARMS_OP = 1079
API_SET_STATE_OP = 1013
API_SET_TEMP_OP = 1019
API_SET_POWER_OP = 1004
LOCAL_TIMEOUT = httpx.Timeout(
# The device can be slow to respond but fast to connect to we
# need to set a long timeout for the read and a short timeout
# for the connect
timeout=10.0,
read=60.0,
)
pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/exceptions.py 0000664 0000000 0000000 00000000710 14514041106 0024412 0 ustar 00root root 0000000 0000000 import json
import httpx
ENDPOINT_PROBE_EXCEPTIONS = (json.JSONDecodeError, httpx.HTTPError)
class EcoforestError(Exception):
"""Base class for Ecoforest exceptions."""
class EcoforestConnectionError(EcoforestError):
"""Ecoforest connection exception."""
class EcoforestAuthenticationRequired(EcoforestError):
"""Exception raised when authentication failed."""
def __init__(self, status: str) -> None:
self.status = status
pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/models/ 0000775 0000000 0000000 00000000000 14514041106 0023144 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/models/device.py 0000664 0000000 0000000 00000011143 14514041106 0024755 0 ustar 00root root 0000000 0000000 """Model for the Ecoforest stove status."""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from pyecoforest.const import MODEL_NAME, SUPPORTED_MODELS
from pyecoforest.exceptions import EcoforestError
class OperationMode(Enum):
"""Model that represents the device operation modes."""
POWER = "power"
TEMPERATURE = "temperature"
EMERGENCY = "emergency"
@classmethod
def build(cls, mode: str) -> OperationMode:
"""Parse the operation mode code to an OperationMode object."""
modes = {
"0": OperationMode.POWER,
"1": OperationMode.TEMPERATURE,
"2": OperationMode.EMERGENCY,
}
if mode in modes:
return modes[mode]
raise EcoforestError(f"The operation mode {mode} is not a valid operation!")
class State(Enum):
"""Model that represents the state of the device."""
OFF = "off"
STARTING = "starting"
PRE_HEATING = "pre_heating"
ON = "on"
SHUTTING_DOWN = "shutting_down"
STAND_BY = "stand_by"
ALARM = "alarm"
@classmethod
def build(cls, state: str) -> State:
"""Parse the state code to a State object."""
states = {
"OFF": [0],
"STARTING": [1, 2, 3, 4, 10],
"PRE_HEATING": [5, 6],
"ON": [7],
"SHUTTING_DOWN": [8, 11, -3],
"STAND_BY": [-20],
"ALARM": [-4],
}
for k, v in states.items():
if int(state) in v:
return State[k]
raise EcoforestError(f"The state {state} is not a valid state!")
class Alarm(Enum):
"""Model that represents the alarms of the device."""
AIR_DEPRESSION = "air_depression"
PELLETS = "pellets"
CPU_OVERHEATING = "cpu_overheating"
UNKNOWN = "unknown"
@classmethod
def build(cls, alarm: str) -> Alarm | None:
"""Parse the alarm code to an Alarm object."""
alarms = {
"A001": Alarm.AIR_DEPRESSION,
"A002": Alarm.AIR_DEPRESSION,
"A012": Alarm.CPU_OVERHEATING,
"A099": Alarm.PELLETS,
"N": None,
}
if alarm in alarms:
return alarms[alarm]
return Alarm.UNKNOWN
@dataclass
class Device:
"""Model for the Ecoforest stove."""
# model information
is_supported: bool
firmware: str
model: str
model_name: str
serial_number: str
# confirguation information
operation_mode: OperationMode
# status information
on: bool
state: State
power: int
temperature: float
alarm: Alarm | None = None
alarm_code: str | None = None
# sensors
environment_temperature: float | None = None
cpu_temperature: float | None = None
gas_temperature: float | None = None
ntc_temperature: float | None = None
depression: int | None = None
working_hours: int | None = None
working_state: int | None = None
working_level: int | None = None
ignitions: int | None = None
live_pulse: float | None = None
pulse_offset: float | None = None
extractor: float | None = None
convecto_air_flow: float | None = None
@classmethod
def build(cls, data: dict[str, dict[str, str]]) -> Device:
"""Parse request data and return as Device."""
status = data["status"]
stats = data["stats"]
alarms = data["alarms"]
model = stats["Me"]
return Device(
is_supported=model in SUPPORTED_MODELS,
model=model,
model_name=MODEL_NAME,
firmware=stats["Vs"],
serial_number=stats["Ns"],
operation_mode=OperationMode.build(status["modo_operacion"]),
on=status["on_off"] == "1",
state=State.build(status["estado"]),
power=int(status["consigna_potencia"]),
temperature=float(status["consigna_temperatura"]),
alarm=Alarm.build(alarms["get_alarmas"]),
alarm_code=alarms["get_alarmas"] if alarms["get_alarmas"] != "N" else None,
environment_temperature=float(status["temperatura"]),
cpu_temperature=float(stats["Tp"]),
gas_temperature=float(stats["Th"]),
ntc_temperature=float(stats["Tn"]),
depression=int(stats["Da"]),
working_hours=int(stats["Nh"]),
ignitions=int(stats["Ne"]),
live_pulse=float(stats["Pn"]),
pulse_offset=float(stats["Pf"]),
working_state=int(stats["Es"]),
extractor=float(stats["Ex"]),
working_level=int(stats["Ni"]),
convecto_air_flow=float(stats["Co"]),
)
pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/py.typed 0000664 0000000 0000000 00000000000 14514041106 0023346 0 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/src/pyecoforest/ssl.py 0000664 0000000 0000000 00000002115 14514041106 0023033 0 ustar 00root root 0000000 0000000 import contextlib
import ssl
def create_no_verify_ssl_context() -> ssl.SSLContext:
"""
Return an SSL context that does not verify the server certificate.
This is a copy of aiohttp's create_default_context() function, with the
ssl verify turned off and old SSL versions enabled.
https://github.com/aio-libs/aiohttp/blob/33953f110e97eecc707e1402daa8d543f38a189b/aiohttp/connector.py#L911
"""
sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sslcontext.check_hostname = False
sslcontext.verify_mode = ssl.CERT_NONE
# Allow all ciphers rather than only Python 3.10 default
sslcontext.set_ciphers("DEFAULT")
with contextlib.suppress(AttributeError):
# This only works for OpenSSL >= 1.0.0
sslcontext.options |= ssl.OP_NO_COMPRESSION
sslcontext.set_default_verify_paths()
return sslcontext
NO_VERIFY_SSL_CONTEXT = create_no_verify_ssl_context()
def create_default_ssl_context() -> ssl.SSLContext:
"""Return an default SSL context."""
return ssl.create_default_context()
SSL_CONTEXT = create_default_ssl_context()
pjanuario-pyecoforest-d2c4b4f/templates/ 0000775 0000000 0000000 00000000000 14514041106 0020526 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/templates/CHANGELOG.j2 0000664 0000000 0000000 00000001121 14514041106 0022245 0 ustar 00root root 0000000 0000000 # Changelog
{%- for version, release in context.history.released.items() %}
## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }})
{%- for category, commits in release["elements"].items() %}
{# Category title: Breaking, Fix, Documentation #}
### {{ category | capitalize }}
{# List actual changes in the category #}
{%- for commit in commits %}
- {{ commit.commit.message | capitalize }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
{%- endfor %}{# for commit #}
{%- endfor %}{# for category, commits #}
{%- endfor %}{# for version, release #}
pjanuario-pyecoforest-d2c4b4f/tests/ 0000775 0000000 0000000 00000000000 14514041106 0017672 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/tests/__init__.py 0000664 0000000 0000000 00000000000 14514041106 0021771 0 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/tests/fixtures/ 0000775 0000000 0000000 00000000000 14514041106 0021543 5 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/tests/fixtures/op-1002-status.txt 0000664 0000000 0000000 00000000232 14514041106 0024620 0 ustar 00root root 0000000 0000000 error_MODO_on_off=0
on_off=0
modo_operacion=0
modo_func=1
estado=0
consigna_potencia=3
consigna_temperatura=20.5
temperatura=23.5
temperatura_ext=---.-
0% pjanuario-pyecoforest-d2c4b4f/tests/fixtures/op-1004-set-power.txt 0000664 0000000 0000000 00000000063 14514041106 0025226 0 ustar 00root root 0000000 0000000 hemos chegado set power 1
1
error_set_potencia=0
0% pjanuario-pyecoforest-d2c4b4f/tests/fixtures/op-1013-set-state.txt 0000664 0000000 0000000 00000000000 14514041106 0025201 0 ustar 00root root 0000000 0000000 pjanuario-pyecoforest-d2c4b4f/tests/fixtures/op-1019-set-temp.txt 0000664 0000000 0000000 00000000043 14514041106 0025043 0 ustar 00root root 0000000 0000000 error_set_temperatura_consigna=0
0% pjanuario-pyecoforest-d2c4b4f/tests/fixtures/op-1020-stats.txt 0000664 0000000 0000000 00000000354 14514041106 0024440 0 ustar 00root root 0000000 0000000 error_get_menu2=0
Rt=-30.0
Fu=02
Pa=-0.7
Ni=0
Es=0
Pn=0.0
Pf=0.0
Th=28.1
Tp=32.3
Tn=25
Da=002
Ex=0.0
Ne=001152
Nh=000006826
Dp=144.2
Co=0.0
Re=0
Ta=24.1
Ns=000025568680000
Me=CC2014_v2
Vs=30Abr19_v2z
Am=0.000
pC=P pjanuario-pyecoforest-d2c4b4f/tests/fixtures/op-1079-alarms.txt 0000664 0000000 0000000 00000000044 14514041106 0024573 0 ustar 00root root 0000000 0000000 error_get_alarmas=0
get_alarmas=N
0% pjanuario-pyecoforest-d2c4b4f/tests/test_api.py 0000664 0000000 0000000 00000020663 14514041106 0022063 0 ustar 00root root 0000000 0000000 from pathlib import Path
import httpx
import pytest
import respx
from pyecoforest.api import EcoforestApi
from pyecoforest.const import (
API_ALARMS_OP,
API_SET_POWER_OP,
API_SET_STATE_OP,
API_SET_TEMP_OP,
API_STATS_OP,
API_STATUS_OP,
URL_CGI,
)
from pyecoforest.exceptions import (
EcoforestAuthenticationRequired,
EcoforestConnectionError,
)
from pyecoforest.models.device import Device, OperationMode, State
def _fixtures_dir() -> Path:
return Path(__file__).parent / "fixtures"
def _load_fixture(name: str) -> str:
with open(_fixtures_dir() / name) as read_in:
return read_in.read()
def _mutate_fixture(name: str, pairs: list[tuple[str, str]]) -> str:
fixture = _load_fixture(name)
for k, v in pairs:
fixture = fixture.replace(k, v)
return fixture
def _get_target() -> EcoforestApi:
"""Return a mock Envoy."""
return EcoforestApi("http://127.0.0.1")
@pytest.mark.asyncio
@respx.mock
async def test_get():
"""Get status information."""
target = _get_target()
respx.post(path=URL_CGI, data={"idOperacion": API_STATUS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1002-status.txt"))
)
respx.post(path=URL_CGI, data={"idOperacion": API_STATS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1020-stats.txt"))
)
respx.post(path=URL_CGI, data={"idOperacion": API_ALARMS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1079-alarms.txt"))
)
actual = await target.get()
assert actual is not None
assert actual == Device(
is_supported=True,
model="CC2014_v2",
model_name="Cordoba glass",
firmware="30Abr19_v2z",
serial_number="000025568680000",
operation_mode=OperationMode.POWER,
on=False,
state=State.OFF,
power=3,
temperature=20.5,
alarm=None,
alarm_code=None,
environment_temperature=23.5,
cpu_temperature=32.3,
gas_temperature=28.1,
ntc_temperature=25,
depression=2,
working_hours=6826,
ignitions=1152,
live_pulse=0.0,
pulse_offset=0.0,
working_state=0,
extractor=0,
working_level=0,
convecto_air_flow=0.0,
)
@pytest.mark.asyncio
@respx.mock
@pytest.mark.parametrize(
("side_effect", "expected", "message"),
[
(
httpx.Response(401),
EcoforestAuthenticationRequired,
"401",
),
(
httpx.TimeoutException("timeout"),
EcoforestConnectionError,
"Timeout occurred while connecting to the device.",
),
(
httpx.Response(500),
EcoforestConnectionError,
"Error occurred while communicating with device.",
),
],
)
async def test_get_errors(side_effect, expected, message):
"""Get status information with error."""
target = _get_target()
respx.post(path=URL_CGI, data={"idOperacion": API_STATUS_OP}).mock(
side_effect=side_effect
)
respx.post(path=URL_CGI, data={"idOperacion": API_STATS_OP}).mock(
side_effect=side_effect
)
respx.post(path=URL_CGI, data={"idOperacion": API_ALARMS_OP}).mock(
side_effect=side_effect
)
with pytest.raises(expected) as err:
await target.get()
assert str(err.value) == message
@pytest.mark.asyncio
@respx.mock
async def test_set_temperature():
"""Set target temperature."""
target = _get_target()
respx.post(path=URL_CGI, data={"idOperacion": API_STATUS_OP}).mock(
return_value=httpx.Response(
200,
text=_mutate_fixture(
"op-1002-status.txt",
[("consigna_temperatura=20.5", "consigna_temperatura=23.5")],
),
)
)
respx.post(path=URL_CGI, data={"idOperacion": API_STATS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1020-stats.txt"))
)
respx.post(path=URL_CGI, data={"idOperacion": API_ALARMS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1079-alarms.txt"))
)
respx.post(
path=URL_CGI, data={"idOperacion": API_SET_TEMP_OP, "temperatura": 23.5}
).mock(return_value=httpx.Response(200, text=_load_fixture("op-1019-set-temp.txt")))
actual = await target.set_temperature(23.5)
assert actual is not None
assert actual == Device(
is_supported=True,
model="CC2014_v2",
model_name="Cordoba glass",
firmware="30Abr19_v2z",
serial_number="000025568680000",
operation_mode=OperationMode.POWER,
on=False,
state=State.OFF,
power=3,
temperature=23.5,
alarm=None,
alarm_code=None,
environment_temperature=23.5,
cpu_temperature=32.3,
gas_temperature=28.1,
ntc_temperature=25,
depression=2,
working_hours=6826,
ignitions=1152,
live_pulse=0.0,
pulse_offset=0.0,
working_state=0,
extractor=0,
working_level=0,
convecto_air_flow=0.0,
)
@pytest.mark.asyncio
@respx.mock
async def test_set_power():
"""Set target power."""
target = _get_target()
respx.post(path=URL_CGI, data={"idOperacion": API_STATUS_OP}).mock(
return_value=httpx.Response(
200,
text=_mutate_fixture(
"op-1002-status.txt", [("consigna_potencia=3", "consigna_potencia=5")]
),
)
)
respx.post(path=URL_CGI, data={"idOperacion": API_STATS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1020-stats.txt"))
)
respx.post(path=URL_CGI, data={"idOperacion": API_ALARMS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1079-alarms.txt"))
)
respx.post(
path=URL_CGI, data={"idOperacion": API_SET_POWER_OP, "potencia": 5}
).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1004-set-power.txt"))
)
actual = await target.set_power(5)
assert actual is not None
assert actual == Device(
is_supported=True,
model="CC2014_v2",
model_name="Cordoba glass",
firmware="30Abr19_v2z",
serial_number="000025568680000",
operation_mode=OperationMode.POWER,
on=False,
state=State.OFF,
power=5,
temperature=20.5,
alarm=None,
alarm_code=None,
environment_temperature=23.5,
cpu_temperature=32.3,
gas_temperature=28.1,
ntc_temperature=25,
depression=2,
working_hours=6826,
ignitions=1152,
live_pulse=0.0,
pulse_offset=0.0,
working_state=0,
extractor=0,
working_level=0,
convecto_air_flow=0.0,
)
@pytest.mark.asyncio
@respx.mock
async def test_turn():
"""Turn on device status."""
target = _get_target()
respx.post(path=URL_CGI, data={"idOperacion": API_STATUS_OP}).mock(
return_value=httpx.Response(
200,
text=_mutate_fixture(
"op-1002-status.txt",
[("on_off=0", "on_off=1"), ("estado=0", "estado=1")],
),
)
)
respx.post(path=URL_CGI, data={"idOperacion": API_STATS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1020-stats.txt"))
)
respx.post(path=URL_CGI, data={"idOperacion": API_ALARMS_OP}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1079-alarms.txt"))
)
respx.post(path=URL_CGI, data={"idOperacion": API_SET_STATE_OP, "on_off": 1}).mock(
return_value=httpx.Response(200, text=_load_fixture("op-1004-set-power.txt"))
)
actual = await target.turn(True)
assert actual is not None
assert actual == Device(
is_supported=True,
model="CC2014_v2",
model_name="Cordoba glass",
firmware="30Abr19_v2z",
serial_number="000025568680000",
operation_mode=OperationMode.POWER,
on=True,
state=State.STARTING,
power=3,
temperature=20.5,
alarm=None,
alarm_code=None,
environment_temperature=23.5,
cpu_temperature=32.3,
gas_temperature=28.1,
ntc_temperature=25,
depression=2,
working_hours=6826,
ignitions=1152,
live_pulse=0.0,
pulse_offset=0.0,
working_state=0,
extractor=0,
working_level=0,
convecto_air_flow=0.0,
)
pjanuario-pyecoforest-d2c4b4f/tests/test_models.py 0000664 0000000 0000000 00000006062 14514041106 0022572 0 ustar 00root root 0000000 0000000 import pytest
from pyecoforest.exceptions import EcoforestError
from pyecoforest.models.device import Alarm, Device, OperationMode, State
def get_api_data():
return {
"status": {
"on_off": "0",
"estado": "0",
"consigna_potencia": "6",
"consigna_temperatura": "22",
"temperatura": "24.5",
"modo_operacion": "0",
},
"stats": {
"Me": "model-version",
"Vs": "firmware-version",
"Ns": "serial-number",
"Tp": "33.5",
"Th": "36.5",
"Tn": "23.5",
"Da": "002",
"Nh": "000006826",
"Ne": "001152",
"Pn": "0.0",
"Pf": "0.0",
"Es": "0",
"Ex": "0.0",
"Ni": "0",
"Co": "0.0",
},
"alarms": {"get_alarmas": "A099"},
}
def test_operation_mode_build():
assert OperationMode.build("0") == OperationMode.POWER
assert OperationMode.build("1") == OperationMode.TEMPERATURE
assert OperationMode.build("2") == OperationMode.EMERGENCY
with pytest.raises(EcoforestError) as error:
OperationMode.build("3")
assert str(error.value) == "The operation mode 3 is not a valid operation!"
def test_state_build():
assert State.build("0") == State.OFF
for i in [*range(1, 5), 10]:
assert State.build(i) == State.STARTING
for i in range(5, 7):
assert State.build(i) == State.PRE_HEATING
assert State.build("7") == State.ON
for i in [8, 11, -3]:
assert State.build(i) == State.SHUTTING_DOWN
assert State.build("-20") == State.STAND_BY
assert State.build("-4") == State.ALARM
with pytest.raises(EcoforestError) as error:
State.build("9")
assert str(error.value) == "The state 9 is not a valid state!"
def test_alarm_build():
for i in ["A001", "A002"]:
assert Alarm.build(i) == Alarm.AIR_DEPRESSION
assert Alarm.build("A012") == Alarm.CPU_OVERHEATING
assert Alarm.build("A099") == Alarm.PELLETS
assert Alarm.build("N") is None
assert Alarm.build("A100") == Alarm.UNKNOWN
def test_device_build():
assert Device.build(get_api_data()) == Device(
is_supported=False,
model="model-version",
model_name="Cordoba glass",
firmware="firmware-version",
serial_number="serial-number",
operation_mode=OperationMode.POWER,
on=False,
state=State.OFF,
power=6,
temperature=22,
alarm=Alarm.PELLETS,
alarm_code="A099",
environment_temperature=24.5,
cpu_temperature=33.5,
gas_temperature=36.5,
ntc_temperature=23.5,
depression=2,
working_hours=6826,
ignitions=1152,
live_pulse=0.0,
pulse_offset=0.0,
working_state=0,
extractor=0,
working_level=0,
convecto_air_flow=0.0,
)
def test_device_build_with_supportted_device():
data = get_api_data()
data["stats"]["Me"] = "CC2014_v2"
assert Device.build(data).is_supported is True