From 66209a1d086e95cd15bd64f669af2f6ea06e4ecb Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Thu, 8 Feb 2024 21:34:37 +0100 Subject: [PATCH 1/8] Clean up repo and code Clean up repository and code Add missing dependencies Fix commit --- .codespell-ignore | 0 .flake8 | 5 - .github/renovate.json | 41 + .github/workflows/codeql.yaml | 8 +- .github/workflows/draft-release.yaml | 3 +- .github/workflows/issues.yaml | 6 +- .github/workflows/linting.yaml | 239 +++-- .github/workflows/release.yaml | 38 +- .github/workflows/requirements.txt | 2 - .github/workflows/sync-labels.yaml | 2 +- .github/workflows/tests.yaml | 87 +- .github/workflows/typing.yaml | 45 +- .github/workflows/verify-pr-labels.yaml | 3 +- .pre-commit-config.yaml | 63 +- ojmicroline_thermostat/__init__.py | 20 +- ojmicroline_thermostat/exceptions.py | 10 +- ojmicroline_thermostat/models/schedule.py | 36 +- ojmicroline_thermostat/models/thermostat.py | 70 +- ojmicroline_thermostat/ojmicroline.py | 110 +- ojmicroline_thermostat/wd5.py | 30 +- ojmicroline_thermostat/wg4.py | 11 +- poetry.lock | 1041 +++---------------- pyproject.toml | 146 ++- test_output.py | 126 +-- tests/__init__.py | 9 +- tests/ruff.toml | 13 + tests/test_model_schedule.py | 9 +- tests/test_model_thermostat.py | 11 +- tests/test_ojmicroline.py | 13 +- tests/test_wd5.py | 16 +- tests/test_wd5_update.py | 93 +- tests/test_wg4.py | 12 +- tests/test_wg4_update.py | 27 +- 33 files changed, 819 insertions(+), 1526 deletions(-) delete mode 100644 .codespell-ignore delete mode 100644 .flake8 create mode 100644 .github/renovate.json delete mode 100644 .github/workflows/requirements.txt create mode 100644 tests/ruff.toml diff --git a/.codespell-ignore b/.codespell-ignore deleted file mode 100644 index e69de29..0000000 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 46e80d7..0000000 --- a/.flake8 +++ /dev/null @@ -1,5 +0,0 @@ -[flake8] -max-line-length = 88 -ignore = D202,W503 -per-file-ignores = tests/*:DAR,S101,S106,E800 -exclude = .git,.github,__pycache__,.venv,test_output.py diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..5696782 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "timezone": "Europe/Amsterdam", + "schedule": ["before 6am every weekday"], + "rebaseWhen": "behind-base-branch", + "dependencyDashboard": true, + "labels": ["dependencies"], + "lockFileMaintenance": { + "enabled": true, + "automerge": true + }, + "commitMessagePrefix": "⬆️", + "packageRules": [ + { + "matchManagers": ["poetry"], + "addLabels": ["python"] + }, + { + "matchManagers": ["poetry"], + "matchDepTypes": ["dev"], + "rangeStrategy": "pin" + }, + { + "matchManagers": ["poetry"], + "matchUpdateTypes": ["minor", "patch"], + "automerge": true + }, + { + "matchManagers": ["github-actions"], + "addLabels": ["github_actions"], + "rangeStrategy": "pin", + "extractVersion": "^(?v\\d+\\.\\d+\\.\\d+)$", + "versioning": "regex:^v(?\\d+)(\\.(?\\d+)\\.(?\\d+))?$" + }, + { + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["minor", "patch"], + "automerge": true + } + ] +} diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 6935c53..a4e9445 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -16,12 +16,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v4.1.1 - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: python + uses: github/codeql-action/init@v3.24.0 - name: Perform Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v3.24.0 diff --git a/.github/workflows/draft-release.yaml b/.github/workflows/draft-release.yaml index a789a4a..852c664 100644 --- a/.github/workflows/draft-release.yaml +++ b/.github/workflows/draft-release.yaml @@ -4,6 +4,7 @@ name: Draft Release on: push: branches: [main] + workflow_dispatch: jobs: update-release-draft: @@ -12,6 +13,6 @@ jobs: steps: - name: Run release drafter - uses: release-drafter/release-drafter@v6 + uses: release-drafter/release-drafter@v6.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/issues.yaml b/.github/workflows/issues.yaml index b5ecab2..4bab968 100644 --- a/.github/workflows/issues.yaml +++ b/.github/workflows/issues.yaml @@ -3,7 +3,7 @@ name: Issues on: schedule: - - cron: "0 8 * * *" + - cron: "0 17 * * *" workflow_dispatch: jobs: @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v5 + - uses: dessant/lock-threads@v5.0.1 with: github-token: ${{ github.token }} issue-inactive-days: "30" @@ -26,7 +26,7 @@ jobs: steps: - name: Run stale - uses: actions/stale@v9 + uses: actions/stale@v9.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 diff --git a/.github/workflows/linting.yaml b/.github/workflows/linting.yaml index 177bb06..330016f 100644 --- a/.github/workflows/linting.yaml +++ b/.github/workflows/linting.yaml @@ -6,105 +6,186 @@ on: pull_request: workflow_dispatch: + +env: + DEFAULT_PYTHON: "3.11" + jobs: - precommit: - name: ${{ matrix.name }} + codespell: + name: codespell runs-on: ubuntu-latest - if: ${{ github.actor != 'dependabot[bot]' }} - strategy: - matrix: - include: - - id: bandit - name: Check with bandit - - id: black - name: Check code style - - id: blacken-docs - name: Check code style in documentation - - id: check-ast - name: Check Python AST - - id: check-case-conflict - name: Check for case conflicts - - id: check-docstring-first - name: Check docstring is first - - id: check-executables-have-shebangs - name: Check that executables have shebangs - - id: check-json - name: Check JSON files - - id: check-merge-conflict - name: Check for merge conflicts - - id: check-symlinks - name: Check for broken symlinks - - id: check-toml - name: Check TOML files - - id: check-yaml - name: Check YAML files - - id: codespell - name: Check code for common misspellings - - id: debug-statements - name: Debug Statements and imports (Python) - - id: detect-private-key - name: Detect Private Keys - - id: end-of-file-fixer - name: Check End of Files - - id: fix-byte-order-marker - name: Check UTF-8 byte order marker - - id: flake8 - name: Enforcing style guide with flake8 - - id: isort - name: Check imports are sorted - - id: poetry - name: Check pyproject file - - id: pylint - name: Check with pylint - - id: pyupgrade - name: Check for upgradable syntax - - id: trailing-whitespace - name: Trim Trailing Whitespace - - id: vulture - name: Check for unused Python code - - id: yamllint - name: Check YAML style + steps: + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Set up Poetry + run: pipx install poetry + + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v5.0.0 + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache: "poetry" + + - name: Install workflow dependencies + run: | + poetry config virtualenvs.create true + poetry config virtualenvs.in-project true + + - name: Install dependencies + run: poetry install --no-interaction + + - name: Check code for common misspellings + run: poetry run pre-commit run codespell --all-files + ruff: + name: Ruff + runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v4.1.1 - - name: Set up Python 3.9 + - name: Set up Poetry + run: pipx install poetry + + - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v5 + uses: actions/setup-python@v5.0.0 with: - python-version: '3.9' + python-version: ${{ env.DEFAULT_PYTHON }} + cache: "poetry" - - name: Set pip cache dir - id: pip-cache + - name: Install workflow dependencies run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + poetry config virtualenvs.create true + poetry config virtualenvs.in-project true + + - name: Install dependencies + run: poetry install --no-interaction - - name: Restore cached Python pip packages - uses: actions/cache@v4 + - name: Run Ruff linter + run: poetry run ruff check --output-format=github . + + - name: Run Ruff formatter + run: poetry run ruff format --check . + + pre-commit-hooks: + name: pre-commit-hooks + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Set up Poetry + run: pipx install poetry + + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v5.0.0 with: - path: ${{ steps.pip-cache.outputs.dir }} - key: pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('.github/workflows/requirements.txt') }} - restore-keys: | - pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- + python-version: ${{ env.DEFAULT_PYTHON }} + cache: "poetry" - name: Install workflow dependencies run: | - pip install -r .github/workflows/requirements.txt poetry config virtualenvs.create true poetry config virtualenvs.in-project true - - name: Restore cached Python venv - id: cached-poetry-dependencies - uses: actions/cache@v4 + - name: Install dependencies + run: poetry install --no-interaction + + - name: Check Python AST + run: poetry run pre-commit run check-ast --all-files + + - name: Check for case conflicts + run: poetry run pre-commit run check-case-conflict --all-files + + - name: Check docstring is first + run: poetry run pre-commit run check-docstring-first --all-files + + - name: Check that executables have shebangs + run: poetry run pre-commit run check-executables-have-shebangs --all-files + + - name: Check JSON files + run: poetry run pre-commit run check-json --all-files + + - name: Check for merge conflicts + run: poetry run pre-commit run check-merge-conflict --all-files + + - name: Check for broken symlinks + run: poetry run pre-commit run check-symlinks --all-files + + - name: Check TOML files + run: poetry run pre-commit run check-toml --all-files + + - name: Check XML files + run: poetry run pre-commit run check-xml --all-files + + - name: Check YAML files + run: poetry run pre-commit run check-yaml --all-files + + - name: Detect Private Keys + run: poetry run pre-commit run detect-private-key --all-files + + - name: Check End of Files + run: poetry run pre-commit run end-of-file-fixer --all-files + + - name: Trim Trailing Whitespace + run: poetry run pre-commit run trailing-whitespace --all-files + + pylint: + name: pylint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Set up Poetry + run: pipx install poetry + + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v5.0.0 + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache: "poetry" + + - name: Install workflow dependencies + run: | + poetry config virtualenvs.create true + poetry config virtualenvs.in-project true + + - name: Install dependencies + run: poetry install --no-interaction + + - name: Run pylint + run: poetry run pre-commit run pylint --all-files + + yamllint: + name: yamllint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Set up Poetry + run: pipx install poetry + + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v5.0.0 with: - path: .venv - key: >- - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- + python-version: ${{ env.DEFAULT_PYTHON }} + cache: "poetry" + + - name: Install workflow dependencies + run: | + poetry config virtualenvs.create true + poetry config virtualenvs.in-project true - name: Install dependencies run: poetry install --no-interaction - - name: Run pre-commit for ${{ matrix.id }} - run: poetry run pre-commit run ${{ matrix.id }} --all-files + - name: Run yamllint + run: poetry run yamllint . diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 81895b3..b5c27c7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -5,6 +5,9 @@ on: release: types: [published] +env: + DEFAULT_PYTHON: "3.11" + jobs: release: name: Releasing to PyPi @@ -12,42 +15,23 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - id: python - uses: actions/setup-python@v5 - with: - python-version: '3.9' + uses: actions/checkout@v4.1.1 - - name: Set pip cache dir - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + - name: Set up Poetry + run: pipx install poetry - - name: Restore cached Python pip packages - uses: actions/cache@v4 + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v5.0.0 with: - path: ${{ steps.pip-cache.outputs.dir }} - key: pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('.github/workflows/requirements.txt') }} - restore-keys: | - pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- + python-version: ${{ env.DEFAULT_PYTHON }} + cache: "poetry" - name: Install workflow dependencies run: | - pip install -r .github/workflows/requirements.txt poetry config virtualenvs.create true poetry config virtualenvs.in-project true - - name: Restore cached Python venv - id: cached-poetry-dependencies - uses: actions/cache@v4 - with: - path: .venv - key: >- - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- - - name: Install dependencies run: poetry install --no-interaction diff --git a/.github/workflows/requirements.txt b/.github/workflows/requirements.txt deleted file mode 100644 index 8d7240b..0000000 --- a/.github/workflows/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pip==23.3 -poetry==1.3.1 diff --git a/.github/workflows/sync-labels.yaml b/.github/workflows/sync-labels.yaml index 6ffcf09..86d18c6 100644 --- a/.github/workflows/sync-labels.yaml +++ b/.github/workflows/sync-labels.yaml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v4.1.1 - name: Run Label Syncer uses: micnncim/action-label-syncer@v1.3.0 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d286d87..d2f728a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -6,54 +6,36 @@ on: pull_request: workflow_dispatch: +env: + DEFAULT_PYTHON: "3.11" + jobs: pytest: - name: Python ${{ matrix.python }} on ${{ matrix.os }} - runs-on: ${{ matrix.os }}-latest + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest strategy: - fail-fast: false matrix: - os: [ubuntu] - python: ["3.9", "3.10", "3.11"] + python: ["3.11", "3.12"] steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v4.1.1 + + - name: Set up Poetry + run: pipx install poetry - name: Set up Python ${{ matrix.python }} id: python - uses: actions/setup-python@v5 + uses: actions/setup-python@v5.0.0 with: python-version: ${{ matrix.python }} - - - name: Set pip cache dir - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - - - name: Restore cached Python pip packages - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('.github/workflows/requirements.txt') }} - restore-keys: | - pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- + cache: "poetry" - name: Install workflow dependencies run: | - pip install -r .github/workflows/requirements.txt poetry config virtualenvs.create true poetry config virtualenvs.in-project true - - name: Restore cached Python venv - id: cached-poetry-dependencies - uses: actions/cache@v4 - with: - path: .venv - key: >- - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- - - name: Install dependencies run: poetry install --no-interaction @@ -61,9 +43,9 @@ jobs: run: poetry run pytest --cov ojmicroline_thermostat tests - name: Upload coverage artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v4.3.1 with: - name: coverage-${{ matrix.python }}-${{ matrix.os }} + name: coverage-${{ matrix.python }} path: .coverage coverage: @@ -71,45 +53,28 @@ jobs: needs: pytest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v4.1.1 + with: + fetch-depth: 0 - name: Download coverage data - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v4.1.2 - - name: Set up Python 3.9 - id: python - uses: actions/setup-python@v5 - with: - python-version: '3.9' + - name: Set up Poetry + run: pipx install poetry - - name: Set pip cache dir - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - - - name: Restore cached Python pip packages - uses: actions/cache@v4 + - name: Set up Python ${{ matrix.python }} + id: python + uses: actions/setup-python@v5.0.0 with: - path: ${{ steps.pip-cache.outputs.dir }} - key: pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('.github/workflows/requirements.txt') }} - restore-keys: | - pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- + python-version: ${{ matrix.python }} + cache: "poetry" - name: Install workflow dependencies run: | - pip install -r .github/workflows/requirements.txt poetry config virtualenvs.create true poetry config virtualenvs.in-project true - - name: Restore cached Python venv - id: cached-poetry-dependencies - uses: actions/cache@v4 - with: - path: .venv - key: >- - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- - - name: Install dependencies run: poetry install --no-interaction @@ -119,4 +84,4 @@ jobs: poetry run coverage xml -i - name: Upload coverage report - uses: codecov/codecov-action@v4.0.0-beta.3 + uses: codecov/codecov-action@v3.1.6 diff --git a/.github/workflows/typing.yaml b/.github/workflows/typing.yaml index 786d939..93e3f65 100644 --- a/.github/workflows/typing.yaml +++ b/.github/workflows/typing.yaml @@ -7,54 +7,33 @@ on: pull_request: workflow_dispatch: +env: + DEFAULT_PYTHON: "3.11" + jobs: mypy: - name: mypy on Python ${{ matrix.python }} + name: Mypy runs-on: ubuntu-latest - if: ${{ github.actor != 'dependabot[bot]' }} - strategy: - fail-fast: false - matrix: - python: ["3.9", "3.10", "3.11"] steps: - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python }} - id: python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} + uses: actions/checkout@v4.1.1 - - name: Set pip cache dir - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + - name: Set up Poetry + run: pipx install poetry - - name: Restore cached Python pip packages - uses: actions/cache@v4 + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v5.0.0 with: - path: ${{ steps.pip-cache.outputs.dir }} - key: pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('.github/workflows/requirements.txt') }} - restore-keys: | - pip-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- + python-version: ${{ env.DEFAULT_PYTHON }} + cache: "poetry" - name: Install workflow dependencies run: | - pip install -r .github/workflows/requirements.txt poetry config virtualenvs.create true poetry config virtualenvs.in-project true - - name: Restore cached Python venv - id: cached-poetry-dependencies - uses: actions/cache@v4 - with: - path: .venv - key: >- - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} - venv-${{ runner.os }}-v2-${{ steps.python.outputs.python-version }}- - - name: Install dependencies run: poetry install --no-interaction diff --git a/.github/workflows/verify-pr-labels.yaml b/.github/workflows/verify-pr-labels.yaml index 1a804fa..8c0644c 100644 --- a/.github/workflows/verify-pr-labels.yaml +++ b/.github/workflows/verify-pr-labels.yaml @@ -4,6 +4,7 @@ name: PR Labels on: pull_request_target: types: [opened, labeled, unlabeled, synchronize] + workflow_call: jobs: pr-labels: @@ -14,8 +15,8 @@ jobs: - name: Verify PR has a valid label uses: jesusvasquez333/verify-pr-label-action@v1.4.0 with: - github-token: "${{ secrets.GITHUB_TOKEN }}" pull-request-number: "${{ github.event.pull_request.number }}" + github-token: "${{ secrets.GITHUB_TOKEN }}" valid-labels: >- breaking-change, bugfix, hotfix, documentation, enhancement, refactor, performance, new-feature, maintenance, ci, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 49ac4cf..5eccb24 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,25 +2,20 @@ repos: - repo: local hooks: - - id: bandit - name: Checking using bandit + - id: ruff-check + name: Ruff Linter language: system types: [python] - entry: poetry run bandit - files: ^ojmicroline_thermostat/ + entry: poetry run ruff check --fix require_serial: true - - id: black - name: Format using black + stages: [commit, push, manual] + - id: ruff-format + name: Ruff Formatter language: system types: [python] - entry: poetry run black - require_serial: true - - id: blacken-docs - name: Format documentation examples using black - language: system - files: '\.(rst|md|markdown|py|tex)$' - entry: poetry run blacken-docs + entry: poetry run ruff format require_serial: true + stages: [commit, push, manual] - id: check-ast name: Check Python AST language: system @@ -61,6 +56,11 @@ repos: language: system types: [toml] entry: poetry run check-toml + - id: check-xml + name: Check XML files + entry: poetry run check-xml + language: system + types: [xml] - id: check-yaml name: Check YAML files language: system @@ -72,12 +72,6 @@ repos: types: [text] exclude: ^poetry\.lock$ entry: poetry run codespell - args: [--ignore-words=.codespell-ignore] - - id: debug-statements - name: Debug Statements and imports (Python) - language: system - types: [python] - entry: poetry run debug-statement-hook - id: detect-private-key name: Detect Private Keys language: system @@ -89,22 +83,6 @@ repos: types: [text] entry: poetry run end-of-file-fixer stages: [commit, push, manual] - - id: fix-byte-order-marker - name: Fix UTF-8 byte order marker - language: system - types: [text] - entry: poetry run fix-byte-order-marker - - id: flake8 - name: Enforcing style guide with flake8 - language: system - types: [python] - entry: poetry run flake8 - require_serial: true - - id: isort - name: Sort all imports with isort - language: system - types: [python] - entry: poetry run isort - id: mypy name: Static type checking using mypy language: system @@ -135,25 +113,12 @@ repos: types: [python] entry: poetry run pytest pass_filenames: false - - id: pyupgrade - name: Checking for upgradable syntax with pyupgrade - language: system - types: [python] - entry: poetry run pyupgrade - args: [--py39-plus, --keep-runtime-typing] - id: trailing-whitespace - name: ✄ Trim Trailing Whitespace + name: Trim Trailing Whitespace language: system types: [text] entry: poetry run trailing-whitespace-fixer stages: [commit, push, manual] - - id: vulture - name: Find unused Python code with Vulture - language: system - types: [python] - entry: poetry run vulture - pass_filenames: false - require_serial: true - id: yamllint name: Check YAML files with yamllint language: system diff --git a/ojmicroline_thermostat/__init__.py b/ojmicroline_thermostat/__init__.py index 33b0a95..8b86418 100644 --- a/ojmicroline_thermostat/__init__.py +++ b/ojmicroline_thermostat/__init__.py @@ -1,11 +1,11 @@ """Asynchronous Python client controlling an OJ Microline Thermostat.""" from .exceptions import ( - OJMicrolineAuthException, - OJMicrolineConnectionException, - OJMicrolineException, - OJMicrolineResultsException, - OJMicrolineTimeoutException, + OJMicrolineAuthError, + OJMicrolineConnectionError, + OJMicrolineError, + OJMicrolineResultsError, + OJMicrolineTimeoutError, ) from .models import Thermostat from .ojmicroline import OJMicroline @@ -15,11 +15,11 @@ __all__ = [ "Thermostat", "OJMicroline", - "OJMicrolineException", - "OJMicrolineAuthException", - "OJMicrolineConnectionException", - "OJMicrolineResultsException", - "OJMicrolineTimeoutException", + "OJMicrolineError", + "OJMicrolineAuthError", + "OJMicrolineConnectionError", + "OJMicrolineResultsError", + "OJMicrolineTimeoutError", "WD5API", "WG4API", ] diff --git a/ojmicroline_thermostat/exceptions.py b/ojmicroline_thermostat/exceptions.py index 2c07114..99e6115 100644 --- a/ojmicroline_thermostat/exceptions.py +++ b/ojmicroline_thermostat/exceptions.py @@ -1,21 +1,21 @@ """Asynchronous Python client controlling an OJ Microline Thermostat.""" -class OJMicrolineException(Exception): +class OJMicrolineError(Exception): """Generic API exception.""" -class OJMicrolineAuthException(OJMicrolineException): +class OJMicrolineAuthError(OJMicrolineError): """API authentication/authorization exception.""" -class OJMicrolineConnectionException(OJMicrolineException): +class OJMicrolineConnectionError(OJMicrolineError): """API connection exception.""" -class OJMicrolineResultsException(OJMicrolineException): +class OJMicrolineResultsError(OJMicrolineError): """API results exception.""" -class OJMicrolineTimeoutException(OJMicrolineException): +class OJMicrolineTimeoutError(OJMicrolineError): """API request timed out.""" diff --git a/ojmicroline_thermostat/models/schedule.py b/ojmicroline_thermostat/models/schedule.py index 1ece746..d4de5ad 100644 --- a/ojmicroline_thermostat/models/schedule.py +++ b/ojmicroline_thermostat/models/schedule.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from typing import Any @@ -15,10 +15,10 @@ class ScheduleEvent: temperature: int def __init__(self, date: datetime, temperature: int) -> None: - """ - Create a new ScheduleEvent instance. + """Create a new ScheduleEvent instance. Args: + ---- date: The date of the event. temperature: The temperature of the event. """ @@ -34,18 +34,18 @@ class Schedule: @classmethod def from_json(cls, data: dict[str, Any]) -> Schedule: - """ - Return a new Schedule instance based on the given JSON. + """Return a new Schedule instance based on the given JSON. Args: + ---- data: The JSON data from the API. Returns: + ------- A Schedule Object. """ - result = {} - now = datetime.now() + now = datetime.now(tz=UTC) current_day = now.weekday() for item in data["Days"]: # Define the week days. @@ -85,17 +85,17 @@ def from_json(cls, data: dict[str, Any]) -> Schedule: return cls(days=result) def get_active_temperature(self) -> int: - """ - Get the current active temperature. + """Get the current active temperature. Returns the currently active temperature based on the schedule. It parses the schedule and compares the schedule times with the current date time. - Returns: + Returns + ------- The currently active temperature based on the schedule. """ - now = datetime.now() + now = datetime.now(tz=UTC) current_day = now.weekday() temperature = 0 @@ -107,25 +107,23 @@ def get_active_temperature(self) -> int: if temperature != 0: return temperature - if current_day == 0: - prev_day = 6 - else: - prev_day = current_day - 1 + # Define the prev day. + prev_day = 6 if current_day == 0 else current_day - 1 return self.days[prev_day][-1].temperature def get_lowest_temperature(self) -> int: - """ - Get the lowest temperature. + """Get the lowest temperature. Returns the lowest temperature based on the schedule. It is used for eco mode. - Returns: + Returns + ------- The lowest temperature based on the schedule. """ temperature = None - for _, day in self.days.items(): + for day in self.days.values(): for event in day: if temperature is None or temperature > event.temperature: temperature = event.temperature diff --git a/ojmicroline_thermostat/models/thermostat.py b/ojmicroline_thermostat/models/thermostat.py index 15b3d37..fc4dd96 100644 --- a/ojmicroline_thermostat/models/thermostat.py +++ b/ojmicroline_thermostat/models/thermostat.py @@ -1,14 +1,15 @@ +# ruff: noqa: PLR0911 # pylint: disable=too-many-instance-attributes,too-many-return-statements """Thermostat model for OJ Microline Thermostat.""" from __future__ import annotations from dataclasses import dataclass -from datetime import datetime +from datetime import datetime, timedelta from math import ceil from time import gmtime, strftime from typing import Any -from ..const import ( +from ojmicroline_thermostat.const import ( REGULATION_BOOST, REGULATION_COMFORT, REGULATION_ECO, @@ -21,6 +22,7 @@ SENSOR_ROOM_FLOOR, WG4_DATETIME_FORMAT, ) + from .schedule import Schedule @@ -68,13 +70,14 @@ class Thermostat: @classmethod def from_wd5_json(cls, data: dict[str, Any]) -> Thermostat: - """ - Return a new Thermostat instance based on JSON from the WD5-series API. + """Return a new Thermostat instance based on JSON from the WD5-series API. Args: + ---- data: The JSON data from the API. Returns: + ------- A Thermostat Object. """ time_zone = data["TimeZone"] @@ -112,19 +115,18 @@ def from_wd5_json(cls, data: dict[str, Any]) -> Thermostat: manual_temperature=data["ManualModeSetpoint"], frost_protection_temperature=data["FrostProtectionTemperature"], boost_temperature=data["MaxSetpoint"], - boost_end_time=parse_date(data["BoostEndTime"], time_zone), - comfort_end_time=parse_date(data["ComfortEndTime"], time_zone), + boost_end_time=parse_wd5_date(data["BoostEndTime"], time_zone), + comfort_end_time=parse_wd5_date(data["ComfortEndTime"], time_zone), vacation_mode=data["VacationEnabled"], - vacation_begin_time=parse_date(data["VacationBeginDay"], time_zone), - vacation_end_time=parse_date(data["VacationEndDay"], time_zone), + vacation_begin_time=parse_wd5_date(data["VacationBeginDay"], time_zone), + vacation_end_time=parse_wd5_date(data["VacationEndDay"], time_zone), vacation_temperature=data["VacationTemperature"], schedule=data["Schedule"], ) @classmethod def from_wg4_json(cls, data: dict[str, Any]) -> Thermostat: - """ - Return a new Thermostat instance based on JSON from the WG4-series API. + """Return a new Thermostat instance based on JSON from the WG4-series API. The WG4 API does return vacation-related fields but does not actually implement any sort of vacation mode, so those fields are ignored. @@ -132,9 +134,11 @@ def from_wg4_json(cls, data: dict[str, Any]) -> Thermostat: The WG4 API does return schedule data but it is currently ignored. Args: + ---- data: The JSON data from the API. Returns: + ------- A Thermostat Object. """ return cls( @@ -160,17 +164,14 @@ def from_wg4_json(cls, data: dict[str, Any]) -> Thermostat: max_temperature=data["MaxTemp"], comfort_temperature=data["ComfortTemperature"], manual_temperature=data["ManualTemperature"], - comfort_end_time=datetime.strptime( - _fix_wg4_date(data["ComfortEndTime"]), - WG4_DATETIME_FORMAT, - ), + comfort_end_time=parse_wg4_date(data["ComfortEndTime"]), ) def get_target_temperature(self) -> int: - """ - Return the target temperature for the thermostat. + """Return the target temperature for the thermostat. - Returns: + Returns + ------- The current temperature. """ # The WG4 API makes this easy: @@ -196,16 +197,15 @@ def get_target_temperature(self) -> int: return 0 def get_current_temperature(self) -> int: - """ - Return the current temperature for the thermostat. + """Return the current temperature for the thermostat. For WD5-series thermostats, the current temperature based on the sensor type. If it is set to room/floor, then the average is used. - Returns: + Returns + ------- The current temperature. """ - # Once again, this is easy with the WG4 API: if self.temperature is not None: return self.temperature @@ -222,34 +222,44 @@ def get_current_temperature(self) -> int: return 0 -def parse_date(value: str, offset: int) -> datetime: - """ - Parse a given value and offset into a datetime object. +def parse_wd5_date(value: str, offset: int) -> datetime: + """Parse a given value and offset into a datetime object. Args: + ---- value: The date time string. offset: The time zone offset in seconds. Returns: + ------- The parsed datetime. """ timezone = strftime("%H:%M", gmtime(abs(offset))) separator = "+" if offset >= 0 else "-" - return datetime.strptime(f"{value}{separator}{timezone}", "%Y-%m-%dT%H:%M:%S%z") + value = f"{value}{separator}{timezone}" + seconds = timedelta(seconds=offset) + if offset >= 0: + return datetime.strptime(value, "%Y-%m-%dT%H:%M:%S%z") - seconds + + return datetime.strptime(value, "%Y-%m-%dT%H:%M:%S%z") + seconds -def _fix_wg4_date(value: str) -> str: - """ - Fix corrupt date formats used by the WG4 API. + +def parse_wg4_date(value: str) -> datetime: + """Fix corrupt date formats used by the WG4 API. Bizarrely the WG4 API sometimes--but not always--sends dates of the form: 28/01/2024 23:29:00 +00:00 +00:00 Args: + ---- value: The raw date string from API JSON. Returns: - A fixed version of the string. + ------- + The parsed datetime. """ - return "+".join(value.split("+")[:2]).strip() + value = "+".join(value.split("+")[:2]).strip() + + return datetime.strptime(value, WG4_DATETIME_FORMAT).astimezone() diff --git a/ojmicroline_thermostat/ojmicroline.py b/ojmicroline_thermostat/ojmicroline.py index 1de3c16..f5fe5d5 100644 --- a/ojmicroline_thermostat/ojmicroline.py +++ b/ojmicroline_thermostat/ojmicroline.py @@ -1,4 +1,3 @@ -# pylint: disable=too-many-arguments """Asynchronous Python client communicating with the OJ Microline API.""" from __future__ import annotations @@ -6,7 +5,7 @@ import json import socket from dataclasses import dataclass -from typing import Any +from typing import TYPE_CHECKING, Any, Self import async_timeout from aiohttp import ClientError, ClientSession, hdrs @@ -15,17 +14,18 @@ from .const import COMFORT_DURATION from .exceptions import ( - OJMicrolineAuthException, - OJMicrolineConnectionException, - OJMicrolineException, - OJMicrolineTimeoutException, + OJMicrolineAuthError, + OJMicrolineConnectionError, + OJMicrolineError, + OJMicrolineTimeoutError, ) -from .models import Thermostat + +if TYPE_CHECKING: + from .models import Thermostat class OJMicrolineAPI(Protocol): - """ - Implements support for a specific OJ Microline API. + """Implements support for a specific OJ Microline API. OJ Microline offers various models of thermostats; somewhat unusually, they are managed via distinct APIs that are only vaguely similar. @@ -49,10 +49,10 @@ def get_thermostats_params(self) -> dict[str, Any]: """Compute additional query string params for fetching thermostats.""" def parse_thermostats_response(self, data: Any) -> list[Thermostat]: - """ - Parse an HTTP response containing thermostat data. + """Parse an HTTP response containing thermostat data. Args: + ---- data: The JSON data contained in the response. """ @@ -60,10 +60,10 @@ def parse_thermostats_response(self, data: Any) -> list[Thermostat]: """HTTP path used to update a thermostat's mode""" def update_regulation_mode_params(self, thermostat: Thermostat) -> dict[str, Any]: - """ - Compute additional query string params for posting to the update path. + """Compute additional query string params for posting to the update path. Args: + ---- thermostat: The Thermostat model. """ @@ -74,10 +74,10 @@ def update_regulation_mode_body( temperature: int | None, duration: int, ) -> dict[str, Any]: - """ - Compute HTTP body parameters used when posting to the update path. + """Compute HTTP body parameters used when posting to the update path. Args: + ---- thermostat: The Thermostat model. regulation_mode: The mode to set the thermostat to. temperature: The temperature to set or None. @@ -88,10 +88,10 @@ def update_regulation_mode_body( """ def parse_update_regulation_mode_response(self, data: Any) -> bool: - """ - Parse the HTTP response received after updating the regulation mode. + """Parse the HTTP response received after updating the regulation mode. Args: + ---- data: The JSON data contained in the response. Returns: True if the update succeeded. @@ -120,10 +120,10 @@ class OJMicroline: def __init__( self, api: OJMicrolineAPI, session: ClientSession | None = None ) -> None: - """ - Create a new OJMicroline instance. + """Create a new OJMicroline instance. Args: + ---- api: An object that specifies how to interact with the API. session: The session to use, or a new session will be created. """ @@ -138,23 +138,25 @@ async def _request( params: dict[str, Any] | None = None, body: dict[str, Any] | None = None, ) -> Any: - """ - Handle a request to the OJ Microline API. + """Handle a request to the OJ Microline API. Args: + ---- uri: Request URI, without '/', for example, 'status' method: HTTP method to use, for example, 'GET' params: Extra options to improve or limit the response. body: Data can be used in a POST and PATCH request. Returns: + ------- A Python dictionary (text) with the response from the API. Raises: - OJMicrolineTimeoutException: A timeout occurred. - OJMicrolineConnectionException: An error occurred. - OJMicrolineException: Received an unexpected response from the API. + ------ + OJMicrolineTimeoutError: A timeout occurred. + OJMicrolineConnectionError: An error occurred. + OJMicrolineError: Received an unexpected response from the API. """ try: if self.__http_session is None: @@ -181,30 +183,28 @@ async def _request( ) response.raise_for_status() except asyncio.TimeoutError as exception: - raise OJMicrolineTimeoutException( - "Timeout occurred while connecting to the OJ Microline API.", - ) from exception + msg = "Timeout occurred while connecting to the OJ Microline API." + raise OJMicrolineTimeoutError(msg) from exception except (ClientError, socket.gaierror) as exception: - raise OJMicrolineConnectionException( - "Error occurred while communicating with the OJ Microline API." - ) from exception + msg = "Error occurred while communicating with the OJ Microline API." + raise OJMicrolineConnectionError(msg) from exception content_type = response.headers.get("Content-Type", "") if not any(item in content_type for item in ["application/json"]): text = await response.text() - raise OJMicrolineException( - "Unexpected content type response from the OJ Microline API", - {"Content-Type": content_type, "response": text}, + msg = "Unexpected content type response from the OJ Microline API" + raise OJMicrolineError( + msg, {"Content-Type": content_type, "response": text} ) return json.loads(await response.text()) async def login(self) -> None: - """ - Get a valid session to do requests with the OJ Microline API. + """Get a valid session to do requests with the OJ Microline API. - Raises: - OJMicrolineAuthException: An error occurred while authenticating. + Raises + ------ + OJMicrolineAuthError: An error occurred while authenticating. """ if self.__session_calls_left == 0 or self.__session_id is None: # Get a new session. @@ -215,19 +215,18 @@ async def login(self) -> None: ) if data["ErrorCode"] == 1: - raise OJMicrolineAuthException( - "Unable to create session, wrong username, password, API key or customer ID provided." # noqa: E501 - ) + msg = "Unable to create session, wrong username, password, API key or customer ID provided." # noqa: E501 + raise OJMicrolineAuthError(msg) # Reset the number of session calls. self.__session_calls_left = self.__session_calls self.__session_id = data["SessionId"] async def get_thermostats(self) -> list[Thermostat]: - """ - Get all the thermostats. + """Get all the thermostats. - Returns: + Returns + ------- A list of Thermostats objects. """ await self.login() @@ -249,14 +248,14 @@ async def set_regulation_mode( temperature: int | None = None, duration: int = COMFORT_DURATION, ) -> bool: - """ - Set the regulation mode. + """Set the regulation mode. Set the provided thermostat to a given preset mode and temperature. The input can be a Thermostat model or a string containing the serial number. Args: + ---- resource: The Thermostat model. regulation_mode: The mode to set the thermostat to. temperature: The temperature to set or None. @@ -264,10 +263,12 @@ async def set_regulation_mode( for (comfort mode only), defaults to 4 hours. Returns: + ------- True if it succeeded. Raises: - OJMicrolineException: An error occurred while setting the regulation mode. + ------ + OJMicrolineError: An error occurred while setting the regulation mode. """ if self.__session_id is None: await self.login() @@ -288,7 +289,8 @@ async def set_regulation_mode( ) if not self.__api.parse_update_regulation_mode_response(data): - raise OJMicrolineException("Unable to set preset mode.") + msg = "Unable to set preset mode." + raise OJMicrolineError(msg) return True @@ -299,20 +301,20 @@ async def close(self) -> None: self.__session_id = None await self.__http_session.close() - async def __aenter__(self) -> OJMicroline: - """ - Async enter. + async def __aenter__(self) -> Self: + """Async enter. - Returns: + Returns + ------- The API object. """ return self - async def __aexit__(self, *_exc_info: str) -> None: - """ - Async exit. + async def __aexit__(self, *_exc_info: object) -> None: + """Async exit. Args: + ---- _exc_info: Exec type. """ await self.close() diff --git a/ojmicroline_thermostat/wd5.py b/ojmicroline_thermostat/wd5.py index 828d1da..14325ee 100644 --- a/ojmicroline_thermostat/wd5.py +++ b/ojmicroline_thermostat/wd5.py @@ -1,10 +1,11 @@ +# ruff: noqa: PLR0913, PERF401 # pylint: disable=too-many-arguments """Implementation of OJMicrolineAPI for WD5-series thermostats.""" from __future__ import annotations from dataclasses import dataclass -from datetime import datetime, timedelta -from typing import Any, Optional +from datetime import UTC, datetime, timedelta +from typing import Any from .const import ( REGULATION_BOOST, @@ -13,7 +14,7 @@ REGULATION_SCHEDULE, WD5_DATETIME_FORMAT, ) -from .exceptions import OJMicrolineResultsException +from .exceptions import OJMicrolineResultsError from .models import Thermostat from .ojmicroline import OJMicrolineAPI @@ -31,10 +32,10 @@ def __init__( client_sw_version: int = 1060, host: str = "ocd5.azurewebsites.net", ) -> None: - """ - Create a new instance of the API object. + """Create a new instance of the API object. Args: + ---- api_key: The API key to use in requests. customer_id: The customer ID to use in requests. username: The username to log in with. @@ -67,10 +68,8 @@ def get_thermostats_params(self) -> dict[str, Any]: # noqa: D102 def parse_thermostats_response(self, data: Any) -> list[Thermostat]: # noqa: D102 if data["ErrorCode"] == 1: - raise OJMicrolineResultsException( - "Unable to get thermostats via API.", - {"data": data}, - ) + msg = "Unable to get thermostats via API." + raise OJMicrolineResultsError(msg, {"data": data}) results: list[Thermostat] = [] for group in data["GroupContents"]: @@ -82,7 +81,8 @@ def parse_thermostats_response(self, data: Any) -> list[Thermostat]: # noqa: D1 update_regulation_mode_path: str = "api/Group/UpdateGroup" def update_regulation_mode_params( # noqa: D102 - self, thermostat: Thermostat + self, + thermostat: Thermostat, # noqa: ARG002 ) -> dict[str, Any]: return {} @@ -94,14 +94,18 @@ def update_regulation_mode_body( # noqa: D102 duration: int, ) -> dict[str, Any]: if regulation_mode == REGULATION_COMFORT: - comfort_end_time = datetime.today() + timedelta(minutes=duration) + comfort_end_time = ( + datetime.now(tz=UTC) + timedelta(minutes=duration) + ).astimezone() comfort_temperature = temperature or thermostat.comfort_temperature else: comfort_end_time = thermostat.comfort_end_time comfort_temperature = thermostat.comfort_temperature if regulation_mode == REGULATION_BOOST: - boost_end_time: Optional[datetime] = datetime.today() + timedelta(hours=1) + boost_end_time: datetime | None = ( + datetime.now(tz=UTC) + timedelta(hours=1) + ).astimezone() else: boost_end_time = thermostat.boost_end_time @@ -139,5 +143,5 @@ def parse_update_regulation_mode_response(self, data: Any) -> bool: # noqa: D10 return data["ErrorCode"] != 1 -def _fmt(d: Optional[datetime]) -> Any: +def _fmt(d: datetime | None) -> Any: return None if d is None else d.strftime(WD5_DATETIME_FORMAT) diff --git a/ojmicroline_thermostat/wg4.py b/ojmicroline_thermostat/wg4.py index 791fe4a..7b3c571 100644 --- a/ojmicroline_thermostat/wg4.py +++ b/ojmicroline_thermostat/wg4.py @@ -1,9 +1,10 @@ +# ruff: noqa: PERF401 """Implementation of OJMicrolineAPI for WG4-series thermostats.""" from __future__ import annotations from dataclasses import dataclass -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Any from .const import REGULATION_COMFORT, REGULATION_MANUAL @@ -18,10 +19,10 @@ class WG4API(OJMicrolineAPI): def __init__( self, username: str, password: str, host: str = "mythermostat.info" ) -> None: - """ - Create a new instance of the API object. + """Create a new instance of the API object. Args: + ---- username: The username to log in with. password: The password for the username. host: The host name used for API requests. @@ -62,7 +63,7 @@ def update_regulation_mode_params( # noqa: D102 def update_regulation_mode_body( # noqa: D102 self, - thermostat: Thermostat, + thermostat: Thermostat, # noqa: ARG002 regulation_mode: int, temperature: int | None, duration: int, @@ -73,7 +74,7 @@ def update_regulation_mode_body( # noqa: D102 "ManualTemperature": temperature, } elif regulation_mode == REGULATION_COMFORT: - end = datetime.now(tz=timezone.utc) + timedelta(minutes=duration) + end = datetime.now(tz=UTC) + timedelta(minutes=duration) extras = { "ComfortTemperature": temperature, "ComfortEndTime": end.strftime("%d/%m/%Y %H:%M:00 +00:00"), diff --git a/poetry.lock b/poetry.lock index f851945..8d28beb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -87,7 +87,6 @@ files = [ [package.dependencies] aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -123,37 +122,22 @@ files = [ [package.dependencies] aiohttp = [ - {version = ">=3.7.0", markers = "python_version >= \"3.10\" and python_version < \"3.12\""}, {version = ">=3.7.0,<3.8.dev0 || >=3.9.dev0", markers = "python_version >= \"3.12\""}, - {version = ">=3.6.0", markers = "python_version >= \"3.8\" and python_version < \"3.10\""}, + {version = ">=3.7.0", markers = "python_version >= \"3.10\" and python_version < \"3.12\""}, ] pytest-asyncio = {version = ">=0.17.0", markers = "python_version >= \"3.7\""} -[[package]] -name = "astor" -version = "0.8.1" -description = "Read/rewrite/write Python ASTs" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -files = [ - {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, - {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, -] - [[package]] name = "astroid" -version = "3.0.2" +version = "3.0.3" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.0.2-py3-none-any.whl", hash = "sha256:d6e62862355f60e716164082d6b4b041d38e2a8cf1c7cd953ded5108bac8ff5c"}, - {file = "astroid-3.0.2.tar.gz", hash = "sha256:4a61cf0a59097c7bb52689b0fd63717cd2a8a14dc9f1eee97b82d814881c8c91"}, + {file = "astroid-3.0.3-py3-none-any.whl", hash = "sha256:92fcf218b89f449cdf9f7b39a269f8d5d617b27be68434912e11e79203963a17"}, + {file = "astroid-3.0.3.tar.gz", hash = "sha256:4148645659b08b70d72460ed1921158027a9e53ae8b7234149b1400eddacbb93"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - [[package]] name = "async-timeout" version = "4.0.3" @@ -184,100 +168,6 @@ tests = ["attrs[tests-no-zope]", "zope-interface"] tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] -[[package]] -name = "bandit" -version = "1.7.7" -description = "Security oriented static analyser for python code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "bandit-1.7.7-py3-none-any.whl", hash = "sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed"}, - {file = "bandit-1.7.7.tar.gz", hash = "sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - -[package.extras] -baseline = ["GitPython (>=3.1.30)"] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] -toml = ["tomli (>=1.1.0)"] -yaml = ["PyYAML"] - -[[package]] -name = "black" -version = "24.1.1" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"}, - {file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"}, - {file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"}, - {file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"}, - {file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"}, - {file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"}, - {file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"}, - {file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"}, - {file = "black-24.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8"}, - {file = "black-24.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e"}, - {file = "black-24.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6"}, - {file = "black-24.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b"}, - {file = "black-24.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62"}, - {file = "black-24.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5"}, - {file = "black-24.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6"}, - {file = "black-24.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717"}, - {file = "black-24.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9"}, - {file = "black-24.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024"}, - {file = "black-24.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2"}, - {file = "black-24.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac"}, - {file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"}, - {file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "blacken-docs" -version = "1.16.0" -description = "Run Black on Python code blocks in documentation files." -optional = false -python-versions = ">=3.8" -files = [ - {file = "blacken_docs-1.16.0-py3-none-any.whl", hash = "sha256:b0dcb84b28ebfb352a2539202d396f50e15a54211e204a8005798f1d1edb7df8"}, - {file = "blacken_docs-1.16.0.tar.gz", hash = "sha256:b4bdc3f3d73898dfbf0166f292c6ccfe343e65fc22ddef5319c95d1a8dcc6c1c"}, -] - -[package.dependencies] -black = ">=22.1.0" - -[[package]] -name = "certifi" -version = "2023.11.17" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, -] - [[package]] name = "cfgv" version = "3.4.0" @@ -289,119 +179,6 @@ files = [ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] -[[package]] -name = "charset-normalizer" -version = "3.3.2" -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.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "codespell" version = "2.2.6" @@ -430,6 +207,20 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "covdefaults" +version = "2.3.0" +description = "A coverage plugin to provide sensible default settings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "covdefaults-2.3.0-py2.py3-none-any.whl", hash = "sha256:2832961f6ffcfe4b57c338bc3418a3526f495c26fb9c54565409c5532f7c41be"}, + {file = "covdefaults-2.3.0.tar.gz", hash = "sha256:4e99f679f12d792bc62e5510fa3eb59546ed47bd569e36e4fddc4081c9c3ebf7"}, +] + +[package.dependencies] +coverage = ">=6.0.2" + [[package]] name = "coverage" version = "7.4.1" @@ -491,23 +282,9 @@ files = [ {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] -[[package]] -name = "darglint" -version = "1.8.1" -description = "A utility for ensuring Google-style docstrings stay up to date with the source code." -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, - {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, -] - [[package]] name = "dill" version = "0.3.8" @@ -534,50 +311,6 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] -[[package]] -name = "dparse" -version = "0.6.3" -description = "A parser for Python dependency files" -optional = false -python-versions = ">=3.6" -files = [ - {file = "dparse-0.6.3-py3-none-any.whl", hash = "sha256:0d8fe18714056ca632d98b24fbfc4e9791d4e47065285ab486182288813a5318"}, - {file = "dparse-0.6.3.tar.gz", hash = "sha256:27bb8b4bcaefec3997697ba3f6e06b2447200ba273c0b085c3d012a04571b528"}, -] - -[package.dependencies] -packaging = "*" -tomli = {version = "*", markers = "python_version < \"3.11\""} - -[package.extras] -conda = ["pyyaml"] -pipenv = ["pipenv (<=2022.12.19)"] - -[[package]] -name = "eradicate" -version = "2.3.0" -description = "Removes commented-out code." -optional = false -python-versions = "*" -files = [ - {file = "eradicate-2.3.0-py3-none-any.whl", hash = "sha256:2b29b3dd27171f209e4ddd8204b70c02f0682ae95eecb353f10e8d72b149c63e"}, - {file = "eradicate-2.3.0.tar.gz", hash = "sha256:06df115be3b87d0fc1c483db22a2ebb12bcf40585722810d809cc770f5031c37"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "filelock" version = "3.13.1" @@ -594,146 +327,6 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] -[[package]] -name = "flake8" -version = "7.0.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, - {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.2.0,<3.3.0" - -[[package]] -name = "flake8-bandit" -version = "4.1.1" -description = "Automated security testing with bandit and flake8." -optional = false -python-versions = ">=3.6" -files = [ - {file = "flake8_bandit-4.1.1-py3-none-any.whl", hash = "sha256:4c8a53eb48f23d4ef1e59293657181a3c989d0077c9952717e98a0eace43e06d"}, - {file = "flake8_bandit-4.1.1.tar.gz", hash = "sha256:068e09287189cbfd7f986e92605adea2067630b75380c6b5733dab7d87f9a84e"}, -] - -[package.dependencies] -bandit = ">=1.7.3" -flake8 = ">=5.0.0" - -[[package]] -name = "flake8-bugbear" -version = "24.1.17" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-bugbear-24.1.17.tar.gz", hash = "sha256:bcb388a4f3b516258749b1e690ee394c082eff742f44595e3754cf5c7781c2c7"}, - {file = "flake8_bugbear-24.1.17-py3-none-any.whl", hash = "sha256:46cc840ddaed26507cd0ada530d1526418b717ee76c9b5dfdbd238b5eab34139"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "flake8-builtins" -version = "2.2.0" -description = "Check for python builtins being used as variables or parameters" -optional = false -python-versions = ">=3.8" -files = [ - {file = "flake8_builtins-2.2.0-py3-none-any.whl", hash = "sha256:7ee5766d9c60e5d579dfda84e65c6d0e6c26005f6f59cb9bf722462d7987a807"}, - {file = "flake8_builtins-2.2.0.tar.gz", hash = "sha256:392d5af3a0720c5a863aa93dc47f48c879081345a143fe9f20d995fe9ff5686a"}, -] - -[package.dependencies] -flake8 = "*" - -[package.extras] -test = ["pytest"] - -[[package]] -name = "flake8-comprehensions" -version = "3.14.0" -description = "A flake8 plugin to help you write better list/set/dict comprehensions." -optional = false -python-versions = ">=3.8" -files = [ - {file = "flake8_comprehensions-3.14.0-py3-none-any.whl", hash = "sha256:7b9d07d94aa88e62099a6d1931ddf16c344d4157deedf90fe0d8ee2846f30e97"}, - {file = "flake8_comprehensions-3.14.0.tar.gz", hash = "sha256:81768c61bfc064e1a06222df08a2580d97de10cb388694becaf987c331c6c0cf"}, -] - -[package.dependencies] -flake8 = ">=3.0,<3.2.0 || >3.2.0" - -[[package]] -name = "flake8-docstrings" -version = "1.7.0" -description = "Extension for flake8 which uses pydocstyle to check docstrings" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, - {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, -] - -[package.dependencies] -flake8 = ">=3" -pydocstyle = ">=2.1" - -[[package]] -name = "flake8-eradicate" -version = "1.5.0" -description = "Flake8 plugin to find commented out code" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "flake8_eradicate-1.5.0-py3-none-any.whl", hash = "sha256:18acc922ad7de623f5247c7d5595da068525ec5437dd53b22ec2259b96ce9d22"}, - {file = "flake8_eradicate-1.5.0.tar.gz", hash = "sha256:aee636cb9ecb5594a7cd92d67ad73eb69909e5cc7bd81710cf9d00970f3983a6"}, -] - -[package.dependencies] -attrs = "*" -eradicate = ">=2.0,<3.0" -flake8 = ">5" - -[[package]] -name = "flake8-markdown" -version = "0.5.0" -description = "Lints Python code blocks in Markdown files using flake8" -optional = false -python-versions = ">=3.8" -files = [ - {file = "flake8_markdown-0.5.0-py3-none-any.whl", hash = "sha256:1930a77e883e130166da20d11ce34bddeeb2779c49783b2673498a2b21c0749c"}, - {file = "flake8_markdown-0.5.0.tar.gz", hash = "sha256:ec2aa584edb16a7ad5a5e9ea59c287aebddd9cb92ebcb5b10efa1402e52caf1e"}, -] - -[package.dependencies] -flake8 = ">=5.0.0" - -[[package]] -name = "flake8-simplify" -version = "0.21.0" -description = "flake8 plugin which checks for code that can be simplified" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "flake8_simplify-0.21.0-py3-none-any.whl", hash = "sha256:439391e762a9370b371208add0b5c5c40c3d25a98e1f5421d263215d08194183"}, - {file = "flake8_simplify-0.21.0.tar.gz", hash = "sha256:c95ff1dcc1de5949af47e0087cbf1164445881131b15bcd7a71252670f492f4d"}, -] - -[package.dependencies] -astor = ">=0.1" -flake8 = ">=3.7" - [[package]] name = "freezegun" version = "1.4.0" @@ -884,136 +477,6 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] -[[package]] -name = "jinja2" -version = "3.1.3" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, -] - -[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.4" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, - {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, -] - -[[package]] -name = "marshmallow" -version = "3.20.2" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -optional = false -python-versions = ">=3.8" -files = [ - {file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"}, - {file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"}, -] - -[package.dependencies] -packaging = ">=17.0" - -[package.extras] -dev = ["pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.15)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["pre-commit (>=2.4,<4.0)"] -tests = ["pytest", "pytz", "simplejson"] - [[package]] name = "mccabe" version = "0.7.0" @@ -1025,98 +488,103 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - [[package]] name = "multidict" -version = "6.0.4" +version = "6.0.5" description = "multidict implementation" optional = false python-versions = ">=3.7" files = [ - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, - {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, - {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, - {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, - {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, - {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, - {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, - {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, - {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, - {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, - {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, - {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, - {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] [[package]] @@ -1157,7 +625,6 @@ files = [ [package.dependencies] mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.1.0" [package.extras] @@ -1213,17 +680,6 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] -[[package]] -name = "pbr" -version = "6.0.0" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -files = [ - {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, - {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, -] - [[package]] name = "platformdirs" version = "4.2.0" @@ -1285,61 +741,6 @@ files = [ [package.dependencies] "ruamel.yaml" = ">=0.15" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "pycodestyle" -version = "2.11.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, -] - -[[package]] -name = "pydocstyle" -version = "6.3.0" -description = "Python docstring style checker" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, - {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, -] - -[package.dependencies] -snowballstemmer = ">=2.2.0" - -[package.extras] -toml = ["tomli (>=1.2.3)"] - -[[package]] -name = "pyflakes" -version = "3.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, -] - -[[package]] -name = "pygments" -version = "2.17.2" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, -] - -[package.extras] -plugins = ["importlib-metadata"] -windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" @@ -1356,16 +757,13 @@ files = [ astroid = ">=3.0.1,<=3.1.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [package.extras] spelling = ["pyenchant (>=3.2,<4.0)"] @@ -1384,11 +782,9 @@ files = [ [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"] @@ -1444,19 +840,16 @@ files = [ six = ">=1.5" [[package]] -name = "pyupgrade" -version = "3.15.0" -description = "A tool to automatically upgrade syntax for newer versions." +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" optional = false -python-versions = ">=3.8.1" +python-versions = "*" files = [ - {file = "pyupgrade-3.15.0-py2.py3-none-any.whl", hash = "sha256:8dc8ebfaed43566e2c65994162795017c7db11f531558a74bc8aa077907bc305"}, - {file = "pyupgrade-3.15.0.tar.gz", hash = "sha256:a7fde381060d7c224f55aef7a30fae5ac93bbc428367d27e70a603bc2acd4f00"}, + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] -[package.dependencies] -tokenize-rt = ">=5.2.0" - [[package]] name = "pyyaml" version = "6.0.1" @@ -1482,7 +875,6 @@ files = [ {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_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {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"}, @@ -1517,54 +909,15 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rich" -version = "13.7.0" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - [[package]] name = "ruamel-yaml" -version = "0.18.5" +version = "0.18.6" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3.7" files = [ - {file = "ruamel.yaml-0.18.5-py3-none-any.whl", hash = "sha256:a013ac02f99a69cdd6277d9664689eb1acba07069f912823177c5eced21a6ada"}, - {file = "ruamel.yaml-0.18.5.tar.gz", hash = "sha256:61917e3a35a569c1133a8f772e1226961bf5a1198bea7e23f06a0841dea1ab0e"}, + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, ] [package.dependencies] @@ -1583,24 +936,24 @@ python-versions = ">=3.6" files = [ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, @@ -1608,7 +961,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, @@ -1616,7 +969,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, @@ -1624,7 +977,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, @@ -1634,31 +987,31 @@ files = [ ] [[package]] -name = "safety" -version = "2.4.0b2" -description = "Checks installed dependencies for known vulnerabilities and licenses." +name = "ruff" +version = "0.1.14" +description = "An extremely fast Python linter and code formatter, written in Rust." optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "safety-2.4.0b2-py3-none-any.whl", hash = "sha256:63773ce92e17f5f80e7dff4c8a25d8abb7d62d375897b5f3bb4afe9313b100ff"}, - {file = "safety-2.4.0b2.tar.gz", hash = "sha256:9907010c6ca7720861ca7fa1496bdb80449b0619ca136eb7ac7e02bd3516cd4f"}, + {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:96f76536df9b26622755c12ed8680f159817be2f725c17ed9305b472a757cdbb"}, + {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab3f71f64498c7241123bb5a768544cf42821d2a537f894b22457a543d3ca7a9"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060156ecc572b8f984fd20fd8b0fcb692dd5d837b7606e968334ab7ff0090ab"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a53d8e35313d7b67eb3db15a66c08434809107659226a90dcd7acb2afa55faea"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bea9be712b8f5b4ebed40e1949379cfb2a7d907f42921cf9ab3aae07e6fba9eb"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2270504d629a0b064247983cbc495bed277f372fb9eaba41e5cf51f7ba705a6a"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80258bb3b8909b1700610dfabef7876423eed1bc930fe177c71c414921898efa"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:653230dd00aaf449eb5ff25d10a6e03bc3006813e2cb99799e568f55482e5cae"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b3acc6c4e6928459ba9eb7459dd4f0c4bf266a053c863d72a44c33246bfdbf"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b3dadc9522d0eccc060699a9816e8127b27addbb4697fc0c08611e4e6aeb8b5"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c8eca1a47b4150dc0fbec7fe68fc91c695aed798532a18dbb1424e61e9b721f"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:62ce2ae46303ee896fc6811f63d6dabf8d9c389da0f3e3f2bce8bc7f15ef5488"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b2027dde79d217b211d725fc833e8965dc90a16d0d3213f1298f97465956661b"}, + {file = "ruff-0.1.14-py3-none-win32.whl", hash = "sha256:722bafc299145575a63bbd6b5069cb643eaa62546a5b6398f82b3e4403329cab"}, + {file = "ruff-0.1.14-py3-none-win_amd64.whl", hash = "sha256:e3d241aa61f92b0805a7082bd89a9990826448e4d0398f0e2bc8f05c75c63d99"}, + {file = "ruff-0.1.14-py3-none-win_arm64.whl", hash = "sha256:269302b31ade4cde6cf6f9dd58ea593773a37ed3f7b97e793c8594b262466b67"}, + {file = "ruff-0.1.14.tar.gz", hash = "sha256:ad3f8088b2dfd884820289a06ab718cde7d38b94972212cc4ba90d5fbc9955f3"}, ] -[package.dependencies] -Click = ">=8.0.2" -dparse = ">=0.6.2" -jinja2 = {version = ">=3.1.0", markers = "python_version >= \"3.7\""} -marshmallow = {version = ">=3.15.0", markers = "python_version >= \"3.7\""} -packaging = ">=21.0" -requests = "*" -"ruamel.yaml" = ">=0.17.21" -setuptools = {version = ">=65.5.1", markers = "python_version >= \"3.7\""} -urllib3 = ">=1.26.5" - -[package.extras] -github = ["pygithub (>=1.43.3)"] -gitlab = ["python-gitlab (>=1.3.0)"] - [[package]] name = "setuptools" version = "69.0.3" @@ -1686,53 +1039,6 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[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 = "stevedore" -version = "5.1.0" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.8" -files = [ - {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, - {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, -] - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - -[[package]] -name = "tokenize-rt" -version = "5.2.0" -description = "A wrapper around the stdlib `tokenize` which roundtrips." -optional = false -python-versions = ">=3.8" -files = [ - {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"}, - {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "tomlkit" version = "0.12.3" @@ -1745,14 +1051,14 @@ files = [ ] [[package]] -name = "types-cachetools" -version = "5.3.0.7" -description = "Typing stubs for cachetools" +name = "types-pytz" +version = "2024.1.0.20240203" +description = "Typing stubs for pytz" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "types-cachetools-5.3.0.7.tar.gz", hash = "sha256:27c982cdb9cf3fead8b0089ee6b895715ecc99dac90ec29e2cab56eb1aaf4199"}, - {file = "types_cachetools-5.3.0.7-py3-none-any.whl", hash = "sha256:98c069dc7fc087b1b061703369c80751b0a0fc561f6fb072b554e5eee23773a0"}, + {file = "types-pytz-2024.1.0.20240203.tar.gz", hash = "sha256:c93751ee20dfc6e054a0148f8f5227b9a00b79c90a4d3c9f464711a73179c89e"}, + {file = "types_pytz-2024.1.0.20240203-py3-none-any.whl", hash = "sha256:9679eef0365db3af91ef7722c199dbb75ee5c1b67e3c4dd7bfbeb1b8a71c21a3"}, ] [[package]] @@ -1766,23 +1072,6 @@ files = [ {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] -[[package]] -name = "urllib3" -version = "2.2.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.8" -files = [ - {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, - {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - [[package]] name = "virtualenv" version = "20.25.0" @@ -1803,29 +1092,15 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] -[[package]] -name = "vulture" -version = "2.11" -description = "Find dead code" -optional = false -python-versions = ">=3.8" -files = [ - {file = "vulture-2.11-py2.py3-none-any.whl", hash = "sha256:12d745f7710ffbf6aeb8279ba9068a24d4e52e8ed333b8b044035c9d6b823aba"}, - {file = "vulture-2.11.tar.gz", hash = "sha256:f0fbb60bce6511aad87ee0736c502456737490a82d919a44e6d92262cb35f1c2"}, -] - -[package.dependencies] -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - [[package]] name = "yamllint" -version = "1.33.0" +version = "1.34.0" description = "A linter for YAML files." optional = false python-versions = ">=3.8" files = [ - {file = "yamllint-1.33.0-py3-none-any.whl", hash = "sha256:28a19f5d68d28d8fec538a1db21bb2d84c7dc2e2ea36266da8d4d1c5a683814d"}, - {file = "yamllint-1.33.0.tar.gz", hash = "sha256:2dceab9ef2d99518a2fcf4ffc964d44250ac4459be1ba3ca315118e4a1a81f7d"}, + {file = "yamllint-1.34.0-py3-none-any.whl", hash = "sha256:33b813f6ff2ffad2e57a288281098392b85f7463ce1f3d5cd45aa848b916a806"}, + {file = "yamllint-1.34.0.tar.gz", hash = "sha256:7f0a6a41e8aab3904878da4ae34b6248b6bc74634e0d3a90f0fb2d7e723a3d4f"}, ] [package.dependencies] @@ -1940,5 +1215,5 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" -python-versions = "^3.9" -content-hash = "b694b96da42ca7719638d2899d9078c94d8050226c918b3f0edaeec71b4e69f4" +python-versions = "^3.11" +content-hash = "a768093ea77b63c394a78cd757a626d4f6df0582990c371f8c386c3637423c28" diff --git a/pyproject.toml b/pyproject.toml index 52a9dab..018fcaa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,9 +15,8 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules", ] @@ -27,76 +26,46 @@ packages = [ [tool.poetry.dependencies] aiohttp = ">=3.0.0" -python = "^3.9" +python = "^3.11" yarl = ">=1.6.0" -[tool.poetry.group.dev.dependencies] -aresponses = "^3.0.0" -async_timeout = "^4.0.3" -bandit = "^1.7.4" -black = ">=22.12,<25.0" -blacken-docs = "^1.12.1" -codespell = "^2.2.2" -coverage = {version = "^7.0.4", extras = ["toml"]} -darglint = "^1.8.1" -flake8 = ">=5.0.4,<8.0.0" -flake8-bandit = "^4.1.1" -flake8-bugbear = ">=22.12.6,<25.0.0" -flake8-builtins = "^2.1.0" -flake8-comprehensions = "^3.10.1" -flake8-docstrings = "^1.6.0" -flake8-eradicate = "^1.4.0" -flake8-markdown = ">=0.4,<0.6" -flake8-simplify = ">=0.19.3,<0.22.0" -isort = "^5.11.4" -mypy = ">=0.991,<1.9" -pre-commit = ">=2.21,<4.0" -pre-commit-hooks = "^4.4.0" -pylint = ">=2.15.10,<4.0.0" -pytest = "^7.2.0" -pytest-asyncio = ">=0.20.3,<0.24.0" -pytest-cov = "^4.0.0" -pyupgrade = "^3.3.1" -safety = ">=2.3.5,<4.0.0" -vulture = "^2.7" -yamllint = "^1.28.0" -types-cachetools = "^5.2.1" -freezegun = "^1.2.2" - [tool.poetry.urls] "Bug Tracker" = "https://github.com/robbinjanssen/python-ojmicroline-thermostat/issues" Changelog = "https://github.com/robbinjanssen/python-ojmicroline-thermostat/releases" -[tool.black] -target-version = ['py39'] +[tool.poetry.group.dev.dependencies] +aresponses = "3.0.0" +codespell = "2.2.6" +covdefaults = "2.3.0" +coverage = {version = "7.4.1", extras = ["toml"]} +mypy = "1.8.0" +pre-commit = "3.6.0" +pre-commit-hooks = "4.5.0" +pylint = "3.0.3" +pytest = "7.4.4" +pytest-asyncio = "0.23.4" +pytest-cov = "4.1.0" +ruff = "0.1.14" +yamllint = "1.34.0" +pytz = "^2024.1" +types-pytz = "^2024.1.0.20240203" +async-timeout = "^4.0.3" +freezegun = "^1.4.0" -[tool.coverage.paths] +[tool.coverage.run] +plugins = ["covdefaults"] source = ["ojmicroline_thermostat"] [tool.coverage.report] +fail_under = 90 show_missing = true -exclude_lines = ["pragma: no cover", "if TYPE_CHECKING:"] - -[tool.coverage.run] -branch = true -source = ["ojmicroline_thermostat"] - -[tool.isort] -profile = "black" -multi_line_output = 3 [tool.mypy] # Specify the target platform details in config, so your developers are # free to run mypy on Windows, Linux, or macOS and get consistent # results. platform = "linux" -python_version = 3.9 - -[[tool.mypy.overrides]] -module = [ - "tests/*" -] -ignore_errors = true +python_version = "3.11" # flake8-mypy expects the two following for sensible formatting show_column_numbers = true @@ -113,8 +82,8 @@ disallow_any_generics = true disallow_incomplete_defs = true disallow_subclassing_any = true disallow_untyped_calls = true +disallow_untyped_decorators = true disallow_untyped_defs = true -disallow_untyped_decorators = false # thanks backoff :( no_implicit_optional = true no_implicit_reexport = true strict_optional = true @@ -125,49 +94,64 @@ warn_return_any = true warn_unused_configs = true warn_unused_ignores = true -# No incremental mode -cache_dir = "/dev/null" +[tool.pylint.MASTER] +ignore = ["tests"] [tool.pylint.BASIC] -good-names = [ - "_", - "ex", - "fp", - "i", - "id", - "j", - "k", - "on", - "Run", - "T", -] +good-names = ["_", "ex", "fp", "i", "id", "j", "k", "on", "Run", "T"] [tool.pylint."MESSAGES CONTROL"] disable= [ - "too-few-public-methods", - "duplicate-code", - "format", - "unsubscriptable-object", + "too-few-public-methods", + "duplicate-code", + "format", + "unsubscriptable-object", ] [tool.pylint.SIMILARITIES] ignore-imports = true [tool.pylint.FORMAT] -max-line-length=88 +max-line-length = 88 [tool.pylint.DESIGN] -max-attributes=20 +max-attributes = 20 [tool.pytest.ini_options] addopts = "--cov" asyncio_mode = "auto" -[tool.vulture] -min_confidence = 80 -paths = ["ojmicroline_thermostat"] -verbose = true +[tool.ruff] +target-version = "py311" +select = ["ALL"] +ignore = [ + "ANN101", # Self... explanatory + "ANN102", # cls... just as useless + "ANN401", # Opinioated warning on disallowing dynamically typed expressions + "D203", # Conflicts with other rules + "D213", # Conflicts with other rules + "D417", # False positives in some occasions + "PLR2004", # Just annoying, not really useful + "SLOT000", # Has a bug with enums: https://github.com/astral-sh/ruff/issues/5748 + + # Conflicts with the Ruff formatter + "COM812", + "ISC001", +] + +[tool.ruff.per-file-ignores] +"test_output.py" = ["ERA001", "T201"] + +[tool.ruff.flake8-pytest-style] +mark-parentheses = false +fixture-parentheses = false + +[tool.ruff.isort] +known-first-party = ["omnikinverter"] + +[tool.ruff.mccabe] +max-complexity = 25 [build-system] -requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" +requires = ["poetry-core>=1.0.0"] diff --git a/test_output.py b/test_output.py index 608e93f..9fd3154 100644 --- a/test_output.py +++ b/test_output.py @@ -1,7 +1,6 @@ -# pylint: disable=redefined-outer-name,too-many-statements +# ruff: noqa: S106, E501 """Asynchronous Python client for OJ Microline Thermostat.""" import asyncio -from time import sleep from ojmicroline_thermostat import WD5API, OJMicroline from ojmicroline_thermostat.const import ( @@ -33,13 +32,14 @@ SENSOR_ROOM_FLOOR: "Room/Floor", } -DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S" +DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S%z" async def main() -> None: """Show example on using the OJ Microline client.""" - async with OJMicroline( # noqa: S106 + async with OJMicroline( api=WD5API( + host="ocd5.azurewebsites.net", customer_id=99, api_key="", username="", @@ -49,70 +49,70 @@ async def main() -> None: # fmt: off thermostats = await client.get_thermostats() for resource in thermostats: - print("####################") - print(f"# {resource.name}") - print("####################") - print("- Details:") - print(f" ID: {resource.thermostat_id}") - print(f" Model: {resource.model}") - print(f" Serial Number: {resource.serial_number}") - print(f" Software Version: {resource.software_version}") - print(f" Zone: {resource.zone_name} ({resource.zone_id})") - if resource.sensor_mode is not None: - print(f" Sensor mode: {SENSOR_MODES[resource.sensor_mode]}") - print("- Regulation:") - print(f" Mode: {REGULATION_MODES[resource.regulation_mode]}") - print(f" Temperature: {resource.get_current_temperature()}") - print(f" Target temperature: {resource.get_target_temperature()}") - print("- Temperatures:") - print(f" Current: {resource.get_current_temperature()}") - print(f" Target: {resource.get_target_temperature()}") - print(f" Range: {resource.min_temperature} - {resource.max_temperature}") - print("- Dates:") - print(f" Comfort End: {resource.comfort_end_time.strftime(DATETIME_FORMAT)}") # noqa: E501 - if resource.boost_end_time is not None: - print(f" Boost End: {resource.boost_end_time.strftime(DATETIME_FORMAT)}") - if resource.vacation_begin_time is not None: - print(f" Vacation Begin: {resource.vacation_begin_time.strftime(DATETIME_FORMAT)}") # noqa: E501 - if resource.vacation_end_time is not None: - print(f" Vacation End: {resource.vacation_end_time.strftime(DATETIME_FORMAT)}") # noqa: E501 - print("- Status:") - print(f" Online: {resource.online}") - print(f" Heating: {resource.heating}") - print(f" Adaptive Mode: {resource.adaptive_mode}") - if resource.vacation_mode is not None: - print(f" Vacation Mode: {resource.vacation_mode}") - if resource.open_window_detection is not None: - print(f" Open Window Detection: {resource.open_window_detection}") - if resource.daylight_saving_active is not None: - print(f" Daylight Saving: {resource.daylight_saving_active}") - print(f" Last Primary Mode is auto: {resource.last_primary_mode_is_auto}") - print("") + if resource.name == "Woonkamer": + print("####################") + print(f"# {resource.name}") + print("####################") + print("- Details:") + print(f" ID: {resource.thermostat_id}") + print(f" Model: {resource.model}") + print(f" Serial Number: {resource.serial_number}") + print(f" Software Version: {resource.software_version}") + print(f" Zone: {resource.zone_name} ({resource.zone_id})") + if resource.sensor_mode is not None: + print(f" Sensor mode: {SENSOR_MODES[resource.sensor_mode]}") + print("- Regulation:") + print(f" Mode: {REGULATION_MODES[resource.regulation_mode]}") + print(f" Temperature: {resource.get_current_temperature()}") + print(f" Target temperature: {resource.get_target_temperature()}") + print("- Temperatures:") + print(f" Current: {resource.get_current_temperature()}") + print(f" Target: {resource.get_target_temperature()}") + print(f" Range: {resource.min_temperature} - {resource.max_temperature}") + print("- Dates:") + print(f" Comfort End: {resource.comfort_end_time.strftime(DATETIME_FORMAT)}") + if resource.boost_end_time is not None: + print(f" Boost End: {resource.boost_end_time.strftime(DATETIME_FORMAT)}") + if resource.vacation_begin_time is not None: + print(f" Vacation Begin: {resource.vacation_begin_time.strftime(DATETIME_FORMAT)}") + if resource.vacation_end_time is not None: + print(f" Vacation End: {resource.vacation_end_time.strftime(DATETIME_FORMAT)}") + print("- Status:") + print(f" Online: {resource.online}") + print(f" Heating: {resource.heating}") + print(f" Adaptive Mode: {resource.adaptive_mode}") + if resource.vacation_mode is not None: + print(f" Vacation Mode: {resource.vacation_mode}") + if resource.open_window_detection is not None: + print(f" Open Window Detection: {resource.open_window_detection}") + if resource.daylight_saving_active is not None: + print(f" Daylight Saving: {resource.daylight_saving_active}") + print(f" Last Primary Mode is auto: {resource.last_primary_mode_is_auto}") + print("") - sleep(5) - print(f"Updating the preset mode for {resource.name}") - print(f"Current: {REGULATION_MODES[resource.regulation_mode]}") + # sleep(5) + # print(f"Updating the preset mode for {resource.name}") + # print(f"Current: {REGULATION_MODES[resource.regulation_mode]}") - print(f"- Setting to {REGULATION_MODES[REGULATION_MANUAL]} and temperature 2500") # noqa: E501 - await client.set_regulation_mode(resource, REGULATION_MANUAL, 2500) - print("Sleeping for 5 seconds..") - sleep(5) + print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") + await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) + # sleep(5) - if REGULATION_BOOST in resource.supported_regulation_modes: - print(f"- Setting to {REGULATION_MODES[REGULATION_BOOST]}") # noqa: E501 - await client.set_regulation_mode(resource, REGULATION_BOOST) - print("Sleeping for 5 seconds..") - sleep(5) + # if REGULATION_BOOST in resource.supported_regulation_modes: + # print(f"- Setting to {REGULATION_MODES[REGULATION_BOOST]}") + # await client.set_regulation_mode(resource, REGULATION_BOOST) + # print("Sleeping for 5 seconds..") + # sleep(5) - print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") # noqa: E501 - await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) - print("Sleeping for 5 seconds..") + # print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") + # await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) + # print("Sleeping for 5 seconds..") - print(f"- Setting to {REGULATION_MODES[REGULATION_SCHEDULE]}") # noqa: E501 - await client.set_regulation_mode(resource, REGULATION_SCHEDULE) - print("Sleeping for 5 seconds..") - sleep(5) - # fmt: on + # print(f"- Setting to {REGULATION_MODES[REGULATION_SCHEDULE]}") + # await client.set_regulation_mode(resource, REGULATION_SCHEDULE) + # print("Sleeping for 5 seconds..") + # sleep(5) + # fmt: on if __name__ == "__main__": diff --git a/tests/__init__.py b/tests/__init__.py index cc00e95..776a9a4 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,10 +1,11 @@ """Asynchronous Python client communicating with the OJ Microline API.""" +from pathlib import Path -import os + +def _fixture_path(filename: str) -> Path: + return Path(__file__).parent / "fixtures" / filename def load_fixtures(filename: str) -> str: """Load a fixture.""" - path = os.path.join(os.path.dirname(__file__), "fixtures", filename) - with open(path, encoding="utf-8") as fptr: - return fptr.read() + return _fixture_path(filename).read_text(encoding="utf-8") diff --git a/tests/ruff.toml b/tests/ruff.toml new file mode 100644 index 0000000..fdce3ba --- /dev/null +++ b/tests/ruff.toml @@ -0,0 +1,13 @@ +# This extend our general Ruff rules specifically for tests +extend = "../pyproject.toml" + +extend-select = [ + "PT", # Use @pytest.fixture without parentheses +] + +extend-ignore = [ + "S101", # Use of assert detected. As these are tests + "SLF001", # Tests will access private/protected members + "S106", # Ignore possible passwords + "ANN001", # Ignore missing annotations +] diff --git a/tests/test_model_schedule.py b/tests/test_model_schedule.py index 9e11385..e992dbf 100644 --- a/tests/test_model_schedule.py +++ b/tests/test_model_schedule.py @@ -6,7 +6,6 @@ import pytest from freezegun import freeze_time - from ojmicroline_thermostat.models import Schedule from . import load_fixtures @@ -20,7 +19,7 @@ async def test_schedule_from_json() -> None: schedule = Schedule.from_json(data) assert schedule.days is not None - for _, day in schedule.days.items(): + for day in schedule.days.values(): assert isinstance(day, Sequence) for event in day: assert event.temperature is not None @@ -63,8 +62,7 @@ async def test_schedule_get_active_temperaturea() -> None: @pytest.mark.asyncio @freeze_time("2023-01-04 06:00") async def test_schedule_get_active_temperature_use_event_from_prev_day() -> None: - """ - Use event from prev day when time is before first event. + """Use event from prev day when time is before first event. WeekDayGrpNo 2 = Tuesday and WeekDayGrpNo 3 = Wednesday @@ -90,8 +88,7 @@ async def test_schedule_get_active_temperature_use_event_from_prev_day() -> None @pytest.mark.asyncio @freeze_time("2023-01-02 06:00") async def test_schedule_get_active_temperature_use_event_from_sunday() -> None: - """ - Use sunday when monday and before first event. + """Use sunday when monday and before first event. WeekDayGrpNo 0 = Sunday and WeekDayGrpNo 1 = Monday diff --git a/tests/test_model_thermostat.py b/tests/test_model_thermostat.py index 43384ee..9d65ac6 100644 --- a/tests/test_model_thermostat.py +++ b/tests/test_model_thermostat.py @@ -5,7 +5,6 @@ import pytest from freezegun import freeze_time - from ojmicroline_thermostat.const import ( REGULATION_BOOST, REGULATION_COMFORT, @@ -192,6 +191,16 @@ async def test_thermostat_from_json_wg4() -> None: assert thermostat.get_target_temperature() == 2600 +@pytest.mark.asyncio +async def test_thermostat_from_json_wd5_timezone_negative() -> None: + """Make sure a negative timezone is set when TimeZone is negative.""" + data = json.loads(load_fixtures("wd5_thermostat.json")) + data["TimeZone"] = -7200 + + thermostat = Thermostat.from_wd5_json(data) + assert thermostat.comfort_end_time.strftime("%z") == "-0200" + + REQUIRED_FIELDS = [ "model", "serial_number", diff --git a/tests/test_ojmicroline.py b/tests/test_ojmicroline.py index dc4c347..83b5ad8 100644 --- a/tests/test_ojmicroline.py +++ b/tests/test_ojmicroline.py @@ -8,13 +8,12 @@ import aiohttp import pytest from aresponses import Response, ResponsesMockServer # type: ignore[import] - from ojmicroline_thermostat import ( WG4API, OJMicroline, - OJMicrolineConnectionException, - OJMicrolineException, - OJMicrolineTimeoutException, + OJMicrolineConnectionError, + OJMicrolineError, + OJMicrolineTimeoutError, ) from . import load_fixtures @@ -75,7 +74,7 @@ async def response_handler(_: aiohttp.ClientResponse) -> Response: ) monkeypatch.setattr(client, "_OJMicroline__request_timeout", 0.1) - with pytest.raises(OJMicrolineTimeoutException): + with pytest.raises(OJMicrolineTimeoutError): assert await client._request("test") @@ -102,7 +101,7 @@ async def test_content_type(aresponses: ResponsesMockServer) -> None: session=session, ) - with pytest.raises(OJMicrolineException): + with pytest.raises(OJMicrolineError): assert await client._request("test") @@ -121,7 +120,7 @@ async def test_client_error() -> None: with ( patch.object(session, "request", side_effect=aiohttp.ClientError), - pytest.raises(OJMicrolineConnectionException), + pytest.raises(OJMicrolineConnectionError), ): assert await client._request("test") diff --git a/tests/test_wd5.py b/tests/test_wd5.py index e79de3c..f7b69a2 100644 --- a/tests/test_wd5.py +++ b/tests/test_wd5.py @@ -7,13 +7,12 @@ import aiohttp import pytest from aresponses import Response, ResponsesMockServer # type: ignore[import] - from ojmicroline_thermostat import ( WD5API, OJMicroline, - OJMicrolineAuthException, - OJMicrolineException, - OJMicrolineResultsException, + OJMicrolineAuthError, + OJMicrolineError, + OJMicrolineResultsError, Thermostat, ) from ojmicroline_thermostat.const import REGULATION_COMFORT, REGULATION_MANUAL @@ -50,7 +49,7 @@ async def test_login(aresponses: ResponsesMockServer) -> None: assert ( client._OJMicroline__session_calls_left == client._OJMicroline__session_calls - ) # noqa: E501 + ) assert client._OJMicroline__session_id == "f00br4" @@ -79,7 +78,7 @@ async def test_login_failed(aresponses: ResponsesMockServer) -> None: session=session, ) - with pytest.raises(OJMicrolineAuthException): + with pytest.raises(OJMicrolineAuthError): await client.login() assert client._OJMicroline__session_calls_left == -1 @@ -150,7 +149,7 @@ async def test_get_thermostats_failed( monkeypatch.setattr(client, "_OJMicroline__session_calls_left", 300) monkeypatch.setattr(client, "_OJMicroline__session_id", "f00b4r") - with pytest.raises(OJMicrolineResultsException): + with pytest.raises(OJMicrolineResultsError): await client.get_thermostats() assert client._OJMicroline__session_calls_left == 299 @@ -239,7 +238,6 @@ async def test_set_regulation_mode_failed( monkeypatch, aresponses: ResponsesMockServer ) -> None: """Test update the regulation mode when an error occurs.""" - aresponses.add( "ojmicroline.test.host", "/api/Group/UpdateGroup", @@ -268,7 +266,7 @@ async def test_set_regulation_mode_failed( monkeypatch.setattr(client, "_OJMicroline__session_calls_left", 300) monkeypatch.setattr(client, "_OJMicroline__session_id", "f00b4r") - with pytest.raises(OJMicrolineException): + with pytest.raises(OJMicrolineError): await client.set_regulation_mode(thermostat, REGULATION_COMFORT, 2500, 360) assert client._OJMicroline__session_calls_left == 299 diff --git a/tests/test_wd5_update.py b/tests/test_wd5_update.py index 27d890f..7369f43 100644 --- a/tests/test_wd5_update.py +++ b/tests/test_wd5_update.py @@ -2,17 +2,15 @@ import json from datetime import datetime, timedelta -from typing import Optional import pytest +import pytz from freezegun import freeze_time - from ojmicroline_thermostat.const import ( COMFORT_DURATION, REGULATION_BOOST, REGULATION_COMFORT, REGULATION_MANUAL, - WD5_DATETIME_FORMAT, ) from ojmicroline_thermostat.models import Thermostat from ojmicroline_thermostat.wd5 import WD5API @@ -20,11 +18,18 @@ from . import load_fixtures +def datetime_now() -> datetime: + """Return the date in local timezone because freezegun doesn't respect UTC. + + @link https://github.com/spulec/freezegun/issues/89 + """ + return datetime.utcnow().replace(tzinfo=pytz.utc).astimezone() # noqa: DTZ003 + + @pytest.mark.asyncio @freeze_time("2023-01-01 11:30:35") async def test_update_regulation_mode_comfort() -> None: - """ - Test that the regulation mode can be set to comfort. + """Test that the regulation mode can be set to comfort. Make sure the end times are set correctly. """ @@ -32,10 +37,10 @@ async def test_update_regulation_mode_comfort() -> None: thermostat = Thermostat.from_wd5_json(json.loads(data)) # Check the current times. - assert _fmt(thermostat.comfort_end_time) == "2022-12-28T15:58:34" - assert _fmt(thermostat.boost_end_time) == "2023-01-02T14:55:01" - assert _fmt(thermostat.vacation_begin_time) == "2022-12-21T00:00:00" - assert _fmt(thermostat.vacation_end_time) == "2022-12-22T00:00:00" + assert _fmt(thermostat.comfort_end_time) == "2022-12-28T14:58:34" + assert _fmt(thermostat.boost_end_time) == "2023-01-02T13:55:01" + assert _fmt(thermostat.vacation_begin_time) == "2022-12-20T23:00:00" + assert _fmt(thermostat.vacation_end_time) == "2022-12-21T23:00:00" # Check the current temperature for comfort. assert thermostat.comfort_temperature == 2000 @@ -55,13 +60,13 @@ async def test_update_regulation_mode_comfort() -> None: # Assert comfort end time is the current date + COMFORT_DURATION minutes. assert result["SetGroup"]["ComfortEndTime"] == _fmt( - datetime.now() + timedelta(minutes=COMFORT_DURATION) + datetime_now() + timedelta(minutes=COMFORT_DURATION) ) # Assert rest is the same. - assert result["SetGroup"]["BoostEndTime"] == "2023-01-02T14:55:01" - assert result["SetGroup"]["VacationBeginDay"] == "2022-12-21T00:00:00" - assert result["SetGroup"]["VacationEndDay"] == "2022-12-22T00:00:00" + assert result["SetGroup"]["BoostEndTime"] == "2023-01-02T13:55:01" + assert result["SetGroup"]["VacationBeginDay"] == "2022-12-20T23:00:00" + assert result["SetGroup"]["VacationEndDay"] == "2022-12-21T23:00:00" # Assert the temperature is the same. assert result["SetGroup"]["ComfortSetpoint"] == 2000 @@ -70,8 +75,7 @@ async def test_update_regulation_mode_comfort() -> None: @pytest.mark.asyncio @freeze_time("2023-01-01 11:30:35") async def test_update_regulation_mode_comfort_with_temp_and_duration() -> None: - """ - Test that the regulation mode can be set to comfort. + """Test that the regulation mode can be set to comfort. Make sure the end times are set correctly. """ @@ -79,10 +83,10 @@ async def test_update_regulation_mode_comfort_with_temp_and_duration() -> None: thermostat = Thermostat.from_wd5_json(json.loads(data)) # Check the current times. - assert _fmt(thermostat.comfort_end_time) == "2022-12-28T15:58:34" - assert _fmt(thermostat.boost_end_time) == "2023-01-02T14:55:01" - assert _fmt(thermostat.vacation_begin_time) == "2022-12-21T00:00:00" - assert _fmt(thermostat.vacation_end_time) == "2022-12-22T00:00:00" + assert _fmt(thermostat.comfort_end_time) == "2022-12-28T14:58:34" + assert _fmt(thermostat.boost_end_time) == "2023-01-02T13:55:01" + assert _fmt(thermostat.vacation_begin_time) == "2022-12-20T23:00:00" + assert _fmt(thermostat.vacation_end_time) == "2022-12-21T23:00:00" # Check the current temperature for comfort. assert thermostat.comfort_temperature == 2000 @@ -102,13 +106,13 @@ async def test_update_regulation_mode_comfort_with_temp_and_duration() -> None: # Assert comfort end time is the current date + 360 minutes. assert result["SetGroup"]["ComfortEndTime"] == _fmt( - datetime.now() + timedelta(minutes=360) + datetime_now() + timedelta(minutes=360) ) # Assert rest is the same. - assert result["SetGroup"]["BoostEndTime"] == "2023-01-02T14:55:01" - assert result["SetGroup"]["VacationBeginDay"] == "2022-12-21T00:00:00" - assert result["SetGroup"]["VacationEndDay"] == "2022-12-22T00:00:00" + assert result["SetGroup"]["BoostEndTime"] == "2023-01-02T13:55:01" + assert result["SetGroup"]["VacationBeginDay"] == "2022-12-20T23:00:00" + assert result["SetGroup"]["VacationEndDay"] == "2022-12-21T23:00:00" # Assert the temperature is the same. assert result["SetGroup"]["ComfortSetpoint"] == 2350 @@ -117,8 +121,7 @@ async def test_update_regulation_mode_comfort_with_temp_and_duration() -> None: @pytest.mark.asyncio @freeze_time("2023-01-01 11:30:35") async def test_update_regulation_mode_boost() -> None: - """ - Test that the regulation mode can be set to boost. + """Test that the regulation mode can be set to boost. Make sure the end times are set correctly. """ @@ -126,10 +129,10 @@ async def test_update_regulation_mode_boost() -> None: thermostat = Thermostat.from_wd5_json(json.loads(data)) # Check the current times. - assert _fmt(thermostat.comfort_end_time) == "2022-12-28T15:58:34" - assert _fmt(thermostat.boost_end_time) == "2023-01-02T14:55:01" - assert _fmt(thermostat.vacation_begin_time) == "2022-12-21T00:00:00" - assert _fmt(thermostat.vacation_end_time) == "2022-12-22T00:00:00" + assert _fmt(thermostat.comfort_end_time) == "2022-12-28T14:58:34" + assert _fmt(thermostat.boost_end_time) == "2023-01-02T13:55:01" + assert _fmt(thermostat.vacation_begin_time) == "2022-12-20T23:00:00" + assert _fmt(thermostat.vacation_end_time) == "2022-12-21T23:00:00" request = WD5API( api_key="v3ry-s3cr3t", @@ -146,32 +149,30 @@ async def test_update_regulation_mode_boost() -> None: # Assert comfort end time is the current date + 1 hours. assert result["SetGroup"]["BoostEndTime"] == _fmt( - datetime.now() + timedelta(hours=1) + datetime_now() + timedelta(hours=1) ) # Assert rest stayed the same. - assert result["SetGroup"]["ComfortEndTime"] == "2022-12-28T15:58:34" - assert result["SetGroup"]["VacationBeginDay"] == "2022-12-21T00:00:00" - assert result["SetGroup"]["VacationEndDay"] == "2022-12-22T00:00:00" + assert result["SetGroup"]["ComfortEndTime"] == "2022-12-28T14:58:34" + assert result["SetGroup"]["VacationBeginDay"] == "2022-12-20T23:00:00" + assert result["SetGroup"]["VacationEndDay"] == "2022-12-21T23:00:00" @pytest.mark.asyncio @freeze_time("2023-01-01 11:30:35") async def test_update_regulation_mode_with_temp() -> None: - """ - Test that the regulation mode can be set to manual mode. + """Test that the regulation mode can be set to manual mode. Make sure the end times are not adjusted. """ - data = load_fixtures("wd5_thermostat.json") thermostat = Thermostat.from_wd5_json(json.loads(data)) # Check the current times. - assert _fmt(thermostat.comfort_end_time) == "2022-12-28T15:58:34" - assert _fmt(thermostat.boost_end_time) == "2023-01-02T14:55:01" - assert _fmt(thermostat.vacation_begin_time) == "2022-12-21T00:00:00" - assert _fmt(thermostat.vacation_end_time) == "2022-12-22T00:00:00" + assert _fmt(thermostat.comfort_end_time) == "2022-12-28T14:58:34" + assert _fmt(thermostat.boost_end_time) == "2023-01-02T13:55:01" + assert _fmt(thermostat.vacation_begin_time) == "2022-12-20T23:00:00" + assert _fmt(thermostat.vacation_end_time) == "2022-12-21T23:00:00" # Check the current temperature for manual. assert thermostat.manual_temperature == 2350 @@ -190,14 +191,14 @@ async def test_update_regulation_mode_with_temp() -> None: ) # Assert the dates are unchanged. - assert result["SetGroup"]["BoostEndTime"] == "2023-01-02T14:55:01" - assert result["SetGroup"]["ComfortEndTime"] == "2022-12-28T15:58:34" - assert result["SetGroup"]["VacationBeginDay"] == "2022-12-21T00:00:00" - assert result["SetGroup"]["VacationEndDay"] == "2022-12-22T00:00:00" + assert result["SetGroup"]["BoostEndTime"] == "2023-01-02T13:55:01" + assert result["SetGroup"]["ComfortEndTime"] == "2022-12-28T14:58:34" + assert result["SetGroup"]["VacationBeginDay"] == "2022-12-20T23:00:00" + assert result["SetGroup"]["VacationEndDay"] == "2022-12-21T23:00:00" # Assert the temperature is updated. assert result["SetGroup"]["ManualModeSetpoint"] == 3000 -def _fmt(date: Optional[datetime]) -> str: - return "None" if date is None else date.strftime(WD5_DATETIME_FORMAT) +def _fmt(date: datetime | None) -> str: + return "None" if date is None else date.strftime("%Y-%m-%dT%H:%M:%S") diff --git a/tests/test_wg4.py b/tests/test_wg4.py index f17168a..45b7728 100644 --- a/tests/test_wg4.py +++ b/tests/test_wg4.py @@ -7,12 +7,11 @@ import aiohttp import pytest from aresponses import Response, ResponsesMockServer # type: ignore[import] - from ojmicroline_thermostat import ( WG4API, OJMicroline, - OJMicrolineAuthException, - OJMicrolineException, + OJMicrolineAuthError, + OJMicrolineError, Thermostat, ) from ojmicroline_thermostat.const import ( @@ -51,7 +50,7 @@ async def test_login(aresponses: ResponsesMockServer) -> None: assert ( client._OJMicroline__session_calls_left == client._OJMicroline__session_calls - ) # noqa: E501 + ) assert client._OJMicroline__session_id == "f00br4" @@ -78,7 +77,7 @@ async def test_login_failed(aresponses: ResponsesMockServer) -> None: session=session, ) - with pytest.raises(OJMicrolineAuthException): + with pytest.raises(OJMicrolineAuthError): await client.login() assert client._OJMicroline__session_calls_left == -1 @@ -197,7 +196,6 @@ async def test_set_regulation_mode_failed( monkeypatch, aresponses: ResponsesMockServer ) -> None: """Test update the regulation mode when an error occurs.""" - aresponses.add( "ojmicroline.test.host", "/api/thermostat", @@ -224,7 +222,7 @@ async def test_set_regulation_mode_failed( monkeypatch.setattr(client, "_OJMicroline__session_calls_left", 300) monkeypatch.setattr(client, "_OJMicroline__session_id", "f00b4r") - with pytest.raises(OJMicrolineException): + with pytest.raises(OJMicrolineError): await client.set_regulation_mode(thermostat, REGULATION_COMFORT, 2500, 360) assert client._OJMicroline__session_calls_left == 299 diff --git a/tests/test_wg4_update.py b/tests/test_wg4_update.py index a8ec450..4d8a908 100644 --- a/tests/test_wg4_update.py +++ b/tests/test_wg4_update.py @@ -1,11 +1,10 @@ """Test the update method for a WD5-series thermostat.""" import json -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest from freezegun import freeze_time - from ojmicroline_thermostat.const import ( COMFORT_DURATION, REGULATION_COMFORT, @@ -21,8 +20,7 @@ @pytest.mark.asyncio @freeze_time("2024-01-25 12:00:00") async def test_update_regulation_mode_comfort() -> None: - """ - Test that the regulation mode can be set to comfort. + """Test that the regulation mode can be set to comfort. Make sure the end times are set correctly. """ @@ -30,7 +28,7 @@ async def test_update_regulation_mode_comfort() -> None: thermostat = Thermostat.from_wg4_json(json.loads(data)) # Check the current times. - assert thermostat.comfort_end_time == datetime(2024, 1, 24, 5, tzinfo=timezone.utc) + assert thermostat.comfort_end_time == datetime(2024, 1, 24, 5, tzinfo=UTC) # Check the current temperature for comfort. assert thermostat.comfort_temperature == 2000 @@ -47,16 +45,15 @@ async def test_update_regulation_mode_comfort() -> None: ) # Assert comfort end time is the current date + COMFORT_DURATION minutes. - assert datetime.strptime( + assert datetime.strptime( # noqa: DTZ007 result["ComfortEndTime"], WG4_DATETIME_FORMAT - ) == datetime.now(tz=timezone.utc) + timedelta(minutes=COMFORT_DURATION) + ) == datetime.now(tz=UTC) + timedelta(minutes=COMFORT_DURATION) @pytest.mark.asyncio @freeze_time("2024-01-25 12:00:00") async def test_update_regulation_mode_comfort_with_temp_and_duration() -> None: - """ - Test that the regulation mode can be set to comfort. + """Test that the regulation mode can be set to comfort. Make sure the end times are set correctly. """ @@ -64,7 +61,7 @@ async def test_update_regulation_mode_comfort_with_temp_and_duration() -> None: thermostat = Thermostat.from_wg4_json(json.loads(data)) # Check the current times. - assert thermostat.comfort_end_time == datetime(2024, 1, 24, 5, tzinfo=timezone.utc) + assert thermostat.comfort_end_time == datetime(2024, 1, 24, 5, tzinfo=UTC) # Check the current temperature for comfort. assert thermostat.comfort_temperature == 2000 @@ -81,9 +78,9 @@ async def test_update_regulation_mode_comfort_with_temp_and_duration() -> None: ) # Assert comfort end time is the current date + 360 minutes. - assert datetime.strptime( + assert datetime.strptime( # noqa: DTZ007 result["ComfortEndTime"], WG4_DATETIME_FORMAT - ) == datetime.now(tz=timezone.utc) + timedelta(minutes=360) + ) == datetime.now(tz=UTC) + timedelta(minutes=360) # Assert the temperature is the same. assert result["ComfortTemperature"] == 2350 @@ -92,17 +89,15 @@ async def test_update_regulation_mode_comfort_with_temp_and_duration() -> None: @pytest.mark.asyncio @freeze_time("2024-01-25 12:00:00") async def test_update_regulation_mode_with_temp() -> None: - """ - Test that the regulation mode can be set to manual mode. + """Test that the regulation mode can be set to manual mode. Make sure the end times are not adjusted. """ - data = load_fixtures("wg4_thermostat.json") thermostat = Thermostat.from_wg4_json(json.loads(data)) # Check the current times. - assert thermostat.comfort_end_time == datetime(2024, 1, 24, 5, tzinfo=timezone.utc) + assert thermostat.comfort_end_time == datetime(2024, 1, 24, 5, tzinfo=UTC) # Check the current temperature for manual. assert thermostat.manual_temperature == 2600 From fec7c77cc428e233fef49808d6d289bbdb103a3a Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Thu, 8 Feb 2024 22:00:40 +0100 Subject: [PATCH 2/8] Fix versoin --- .github/workflows/tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d2f728a..b4fe013 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -63,11 +63,11 @@ jobs: - name: Set up Poetry run: pipx install poetry - - name: Set up Python ${{ matrix.python }} + - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v5.0.0 with: - python-version: ${{ matrix.python }} + python-version: ${{ env.DEFAULT_PYTHON }} cache: "poetry" - name: Install workflow dependencies From df91a5a78a9d17811725ff9ab2c251d3044b7de6 Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Thu, 8 Feb 2024 22:02:20 +0100 Subject: [PATCH 3/8] Revert example --- test_output.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/test_output.py b/test_output.py index 9fd3154..539b960 100644 --- a/test_output.py +++ b/test_output.py @@ -1,6 +1,7 @@ # ruff: noqa: S106, E501 """Asynchronous Python client for OJ Microline Thermostat.""" import asyncio +from time import sleep from ojmicroline_thermostat import WD5API, OJMicroline from ojmicroline_thermostat.const import ( @@ -90,28 +91,28 @@ async def main() -> None: print(f" Last Primary Mode is auto: {resource.last_primary_mode_is_auto}") print("") - # sleep(5) - # print(f"Updating the preset mode for {resource.name}") - # print(f"Current: {REGULATION_MODES[resource.regulation_mode]}") + sleep(5) + print(f"Updating the preset mode for {resource.name}") + print(f"Current: {REGULATION_MODES[resource.regulation_mode]}") print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) - # sleep(5) + sleep(5) - # if REGULATION_BOOST in resource.supported_regulation_modes: - # print(f"- Setting to {REGULATION_MODES[REGULATION_BOOST]}") - # await client.set_regulation_mode(resource, REGULATION_BOOST) - # print("Sleeping for 5 seconds..") - # sleep(5) + if REGULATION_BOOST in resource.supported_regulation_modes: + print(f"- Setting to {REGULATION_MODES[REGULATION_BOOST]}") + await client.set_regulation_mode(resource, REGULATION_BOOST) + print("Sleeping for 5 seconds..") + sleep(5) - # print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") - # await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) - # print("Sleeping for 5 seconds..") + print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") + await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) + print("Sleeping for 5 seconds..") - # print(f"- Setting to {REGULATION_MODES[REGULATION_SCHEDULE]}") - # await client.set_regulation_mode(resource, REGULATION_SCHEDULE) - # print("Sleeping for 5 seconds..") - # sleep(5) + print(f"- Setting to {REGULATION_MODES[REGULATION_SCHEDULE]}") + await client.set_regulation_mode(resource, REGULATION_SCHEDULE) + print("Sleeping for 5 seconds..") + sleep(5) # fmt: on From d8ed0d2bae57ed6cdde5af685dc08dfb392452d7 Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Thu, 8 Feb 2024 22:04:01 +0100 Subject: [PATCH 4/8] Remove debug --- test_output.py | 118 ++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/test_output.py b/test_output.py index 539b960..2e7b397 100644 --- a/test_output.py +++ b/test_output.py @@ -50,70 +50,70 @@ async def main() -> None: # fmt: off thermostats = await client.get_thermostats() for resource in thermostats: - if resource.name == "Woonkamer": - print("####################") - print(f"# {resource.name}") - print("####################") - print("- Details:") - print(f" ID: {resource.thermostat_id}") - print(f" Model: {resource.model}") - print(f" Serial Number: {resource.serial_number}") - print(f" Software Version: {resource.software_version}") - print(f" Zone: {resource.zone_name} ({resource.zone_id})") - if resource.sensor_mode is not None: - print(f" Sensor mode: {SENSOR_MODES[resource.sensor_mode]}") - print("- Regulation:") - print(f" Mode: {REGULATION_MODES[resource.regulation_mode]}") - print(f" Temperature: {resource.get_current_temperature()}") - print(f" Target temperature: {resource.get_target_temperature()}") - print("- Temperatures:") - print(f" Current: {resource.get_current_temperature()}") - print(f" Target: {resource.get_target_temperature()}") - print(f" Range: {resource.min_temperature} - {resource.max_temperature}") - print("- Dates:") - print(f" Comfort End: {resource.comfort_end_time.strftime(DATETIME_FORMAT)}") - if resource.boost_end_time is not None: - print(f" Boost End: {resource.boost_end_time.strftime(DATETIME_FORMAT)}") - if resource.vacation_begin_time is not None: - print(f" Vacation Begin: {resource.vacation_begin_time.strftime(DATETIME_FORMAT)}") - if resource.vacation_end_time is not None: - print(f" Vacation End: {resource.vacation_end_time.strftime(DATETIME_FORMAT)}") - print("- Status:") - print(f" Online: {resource.online}") - print(f" Heating: {resource.heating}") - print(f" Adaptive Mode: {resource.adaptive_mode}") - if resource.vacation_mode is not None: - print(f" Vacation Mode: {resource.vacation_mode}") - if resource.open_window_detection is not None: - print(f" Open Window Detection: {resource.open_window_detection}") - if resource.daylight_saving_active is not None: - print(f" Daylight Saving: {resource.daylight_saving_active}") - print(f" Last Primary Mode is auto: {resource.last_primary_mode_is_auto}") - print("") + print("####################") + print(f"# {resource.name}") + print("####################") + print("- Details:") + print(f" ID: {resource.thermostat_id}") + print(f" Model: {resource.model}") + print(f" Serial Number: {resource.serial_number}") + print(f" Software Version: {resource.software_version}") + print(f" Zone: {resource.zone_name} ({resource.zone_id})") + if resource.sensor_mode is not None: + print(f" Sensor mode: {SENSOR_MODES[resource.sensor_mode]}") + print("- Regulation:") + print(f" Mode: {REGULATION_MODES[resource.regulation_mode]}") + print(f" Temperature: {resource.get_current_temperature()}") + print(f" Target temperature: {resource.get_target_temperature()}") + print("- Temperatures:") + print(f" Current: {resource.get_current_temperature()}") + print(f" Target: {resource.get_target_temperature()}") + print(f" Range: {resource.min_temperature} - {resource.max_temperature}") + print("- Dates:") + print(f" Comfort End: {resource.comfort_end_time.strftime(DATETIME_FORMAT)}") + if resource.boost_end_time is not None: + print(f" Boost End: {resource.boost_end_time.strftime(DATETIME_FORMAT)}") + if resource.vacation_begin_time is not None: + print(f" Vacation Begin: {resource.vacation_begin_time.strftime(DATETIME_FORMAT)}") + if resource.vacation_end_time is not None: + print(f" Vacation End: {resource.vacation_end_time.strftime(DATETIME_FORMAT)}") + print("- Status:") + print(f" Online: {resource.online}") + print(f" Heating: {resource.heating}") + print(f" Adaptive Mode: {resource.adaptive_mode}") + if resource.vacation_mode is not None: + print(f" Vacation Mode: {resource.vacation_mode}") + if resource.open_window_detection is not None: + print(f" Open Window Detection: {resource.open_window_detection}") + if resource.daylight_saving_active is not None: + print(f" Daylight Saving: {resource.daylight_saving_active}") + print(f" Last Primary Mode is auto: {resource.last_primary_mode_is_auto}") + print("") - sleep(5) - print(f"Updating the preset mode for {resource.name}") - print(f"Current: {REGULATION_MODES[resource.regulation_mode]}") - - print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") - await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) - sleep(5) + sleep(5) + print(f"Updating the preset mode for {resource.name}") + print(f"Current: {REGULATION_MODES[resource.regulation_mode]}") - if REGULATION_BOOST in resource.supported_regulation_modes: - print(f"- Setting to {REGULATION_MODES[REGULATION_BOOST]}") - await client.set_regulation_mode(resource, REGULATION_BOOST) - print("Sleeping for 5 seconds..") - sleep(5) + print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") + await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) + print("Sleeping for 5 seconds..") + sleep(5) - print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") - await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) - print("Sleeping for 5 seconds..") - - print(f"- Setting to {REGULATION_MODES[REGULATION_SCHEDULE]}") - await client.set_regulation_mode(resource, REGULATION_SCHEDULE) + if REGULATION_BOOST in resource.supported_regulation_modes: + print(f"- Setting to {REGULATION_MODES[REGULATION_BOOST]}") + await client.set_regulation_mode(resource, REGULATION_BOOST) print("Sleeping for 5 seconds..") sleep(5) - # fmt: on + + print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") + await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) + print("Sleeping for 5 seconds..") + + print(f"- Setting to {REGULATION_MODES[REGULATION_SCHEDULE]}") + await client.set_regulation_mode(resource, REGULATION_SCHEDULE) + print("Sleeping for 5 seconds..") + sleep(5) + # fmt: on if __name__ == "__main__": From e2434ca95dd9edc59b965b135ab32105d91d603c Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Thu, 8 Feb 2024 22:10:21 +0100 Subject: [PATCH 5/8] Keep pylint happy --- test_output.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test_output.py b/test_output.py index 2e7b397..89db819 100644 --- a/test_output.py +++ b/test_output.py @@ -1,7 +1,8 @@ -# ruff: noqa: S106, E501 +# ruff: noqa: S106, E501, PLR0915 +# pylint: disable=too-many-arguments """Asynchronous Python client for OJ Microline Thermostat.""" import asyncio -from time import sleep +from asyncio import sleep from ojmicroline_thermostat import WD5API, OJMicroline from ojmicroline_thermostat.const import ( @@ -42,9 +43,9 @@ async def main() -> None: api=WD5API( host="ocd5.azurewebsites.net", customer_id=99, - api_key="", - username="", - password="", + api_key="f219aab4-9ac0-4343-8422-b72203e2fac9", + username="robbinjanssen", + password="2K4f2YMMZdggd4EZ", ), ) as client: # fmt: off @@ -90,29 +91,30 @@ async def main() -> None: print(f" Last Primary Mode is auto: {resource.last_primary_mode_is_auto}") print("") - sleep(5) + await sleep(5) print(f"Updating the preset mode for {resource.name}") print(f"Current: {REGULATION_MODES[resource.regulation_mode]}") print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) print("Sleeping for 5 seconds..") - sleep(5) + await sleep(5) if REGULATION_BOOST in resource.supported_regulation_modes: print(f"- Setting to {REGULATION_MODES[REGULATION_BOOST]}") await client.set_regulation_mode(resource, REGULATION_BOOST) print("Sleeping for 5 seconds..") - sleep(5) + await sleep(5) print(f"- Setting to {REGULATION_MODES[REGULATION_COMFORT]} and temperature 2500") await client.set_regulation_mode(resource, REGULATION_COMFORT, 2500) print("Sleeping for 5 seconds..") + await sleep(5) print(f"- Setting to {REGULATION_MODES[REGULATION_SCHEDULE]}") await client.set_regulation_mode(resource, REGULATION_SCHEDULE) print("Sleeping for 5 seconds..") - sleep(5) + await sleep(5) # fmt: on From 910e78e8bac0c4a2b7ca53deeec8beff0f0695c5 Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Thu, 8 Feb 2024 22:12:54 +0100 Subject: [PATCH 6/8] Fix pylint --- test_output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_output.py b/test_output.py index 89db819..9ffc547 100644 --- a/test_output.py +++ b/test_output.py @@ -1,5 +1,5 @@ # ruff: noqa: S106, E501, PLR0915 -# pylint: disable=too-many-arguments +# pylint: disable=too-many-statements """Asynchronous Python client for OJ Microline Thermostat.""" import asyncio from asyncio import sleep From 2d23fe09d7e0045338ff5d589712f3d32a13c751 Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Fri, 9 Feb 2024 09:34:47 +0100 Subject: [PATCH 7/8] Remove personal details (passwd changed) --- test_output.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_output.py b/test_output.py index 9ffc547..f9ad33f 100644 --- a/test_output.py +++ b/test_output.py @@ -43,9 +43,9 @@ async def main() -> None: api=WD5API( host="ocd5.azurewebsites.net", customer_id=99, - api_key="f219aab4-9ac0-4343-8422-b72203e2fac9", - username="robbinjanssen", - password="2K4f2YMMZdggd4EZ", + api_key="", + username="", + password="", ), ) as client: # fmt: off From 712e1e210eafeec3e3c1b69893bbd385024be742 Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Fri, 9 Feb 2024 09:35:33 +0100 Subject: [PATCH 8/8] Remove host --- test_output.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test_output.py b/test_output.py index f9ad33f..e30f6ef 100644 --- a/test_output.py +++ b/test_output.py @@ -41,7 +41,6 @@ async def main() -> None: """Show example on using the OJ Microline client.""" async with OJMicroline( api=WD5API( - host="ocd5.azurewebsites.net", customer_id=99, api_key="", username="",