From 120920912e8442bdbb8177d6fef50e814697bdf8 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 11 May 2024 09:48:06 -0700 Subject: [PATCH] fix: `handle_state_change` updated for new state change event (#364) * fix: `handle_state_change` updated for new state change event * use tox for testing * formatting * adjust black checks --- .github/workflows/pytest.yaml | 49 ++++++++++++++++-------- .gitignore | 2 + custom_components/keymaster/__init__.py | 2 +- custom_components/keymaster/helpers.py | 6 +-- pylintrc | 50 +++++++++++++++++++++++++ requirements_test.txt | 3 ++ setup.cfg | 32 ++++++++++++++++ tests/test_config_flow.py | 2 + tox.ini | 36 ++++++++++++++++++ 9 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 pylintrc create mode 100644 setup.cfg create mode 100644 tox.ini diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 14d689c0..4dbb28ad 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -7,32 +7,49 @@ on: - cron: "0 7 1-28/7 * *" jobs: - build: + tests: runs-on: ubuntu-latest + name: Run tests strategy: matrix: python-version: - "3.12" steps: - - uses: actions/checkout@v4 + - name: 📥 Checkout the repository + uses: actions/checkout@v4 + - name: 🛠️ Set up Python + uses: actions/setup-python@v5 + with: + fetch-depth: 2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - sudo apt-get install libudev-dev - python -m pip install --upgrade pip - pip install -r requirements_test.txt - - name: Run formatting + - name: 📦 Install requirements run: | - python -m isort -v --profile black . - python -m black -v . - - name: Generate coverage report - run: | - python -m pytest - pip install pytest-cov - pytest ./tests/ --cov=custom_components/keymaster/ --cov-report=xml - - name: Upload coverage to Codecov + pip install tox tox-gh-actions + - name: 🏃 Test with tox + run: tox + - name: 📤 Upload coverage to Codecov + uses: "actions/upload-artifact@v4" + with: + name: coverage-data + path: "coverage.xml" + + coverage: + runs-on: ubuntu-latest + needs: tests + steps: + - name: 📥 Checkout the repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + - name: 📥 Download coverage data + uses: actions/download-artifact@v4 + with: + name: coverage-data + - name: 📤 Upload coverage report uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} # required diff --git a/.gitignore b/.gitignore index 9033bdde..8779d4b0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ custom_components/*/__pycache__/ venv *.pyc .coverage +coverage.xml +.tox/py312/.tox-info.json diff --git a/custom_components/keymaster/__init__.py b/custom_components/keymaster/__init__.py index b343ccae..4f1780b0 100644 --- a/custom_components/keymaster/__init__.py +++ b/custom_components/keymaster/__init__.py @@ -116,7 +116,7 @@ async def homeassistant_started_listener( async_track_state_change_event( hass, [lock.lock_entity_id for lock in locks_to_watch], - functools.partial(handle_state_change, hass, config_entry) + functools.partial(handle_state_change, hass, config_entry), ) ) diff --git a/custom_components/keymaster/helpers.py b/custom_components/keymaster/helpers.py index 403c1642..a9ed8bb2 100644 --- a/custom_components/keymaster/helpers.py +++ b/custom_components/keymaster/helpers.py @@ -23,7 +23,7 @@ STATE_UNAVAILABLE, STATE_UNKNOWN, ) -from homeassistant.core import Event, HomeAssistant, State, callback +from homeassistant.core import Event, EventStateChangedData, HomeAssistant, callback from homeassistant.exceptions import ServiceNotFound from homeassistant.helpers.device_registry import async_get as async_get_device_registry from homeassistant.helpers.entity_registry import ( @@ -261,14 +261,14 @@ def handle_state_change( hass: HomeAssistant, config_entry: ConfigEntry, changed_entity: str, - old_state: State, - new_state: State, + event: Event[EventStateChangedData] | None = None, ) -> None: """Listener to track state changes to lock entities.""" primary_lock: KeymasterLock = hass.data[DOMAIN][config_entry.entry_id][PRIMARY_LOCK] child_locks: List[KeymasterLock] = hass.data[DOMAIN][config_entry.entry_id][ CHILD_LOCKS ] + new_state = event.data["new_state"] for lock in [primary_lock, *child_locks]: # Don't do anything if the changed entity is not this lock diff --git a/pylintrc b/pylintrc new file mode 100644 index 00000000..b2b29679 --- /dev/null +++ b/pylintrc @@ -0,0 +1,50 @@ +[MASTER] +ignore=tests +# Use a conservative default here; 2 should speed up most setups and not hurt +# any too bad. Override on command line as appropriate. +jobs=2 +persistent=no + +[BASIC] +good-names=id,i,j,k,ex,Run,_,fp +max-attributes=15 +argument-naming-style=snake_case +attr-naming-style=snake_case + +[MESSAGES CONTROL] +# Reasons disabled: +# locally-disabled - it spams too much +# too-many-* - are not enforced for the sake of readability +# too-few-* - same as too-many-* +# import-outside-toplevel - TODO +disable= + duplicate-code, + fixme, + import-outside-toplevel, + locally-disabled, + too-few-public-methods, + too-many-arguments, + too-many-public-methods, + too-many-instance-attributes, + too-many-branches, + too-many-statements, + broad-except, + too-many-lines, + too-many-locals, + unexpected-keyword-arg, + abstract-method, + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=8 + +[REPORTS] +score=no + +[TYPECHECK] +# For attrs +ignored-classes=_CountingAttr + +[FORMAT] +expected-line-ending-format=LF \ No newline at end of file diff --git a/requirements_test.txt b/requirements_test.txt index 5e896423..ccc85e05 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -7,3 +7,6 @@ black isort pydispatcher zeroconf +tox +mypy +flake8 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..9742134a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,32 @@ +[mypy] +python_version = 3.10 +show_error_codes = true +ignore_errors = true +follow_imports = silent +ignore_missing_imports = true +warn_incomplete_stub = true +warn_redundant_casts = true +warn_unused_configs = true + +[flake8] +exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build +# To work with Black +max-line-length = 88 +# E501: line too long +# W503: Line break occurred before a binary operator +# E203: Whitespace before ':' +# D202 No blank lines allowed after function docstring +# W504 line break after binary operator +ignore = + E501, + W503, + E203, + D202, + W504 + +[isort] +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +use_parentheses = True +line_length = 88 \ No newline at end of file diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 01e9dd13..0fb182b8 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -20,6 +20,8 @@ KWIKSET_910_LOCK_ENTITY = "lock.smart_code_with_home_connect_technology" _LOGGER = logging.getLogger(__name__) +pytestmark = pytest.mark.asyncio + @pytest.mark.parametrize( "input_1,title,data", diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..d3491069 --- /dev/null +++ b/tox.ini @@ -0,0 +1,36 @@ +[tox] +skipsdist = true +envlist = py310, py311, py312, lint, mypy +skip_missing_interpreters = True + +[gh-actions] +python = + 3.10: py310 + 3.11: py311 + 3.12: py312, lint, mypy + +[testenv] +commands = + pytest --asyncio-mode=auto --timeout=30 --cov=custom_components/keymaster --cov-report=xml {posargs} +deps = + -rrequirements_test.txt + +[testenv:lint] +basepython = python3 +ignore_errors = True +commands = + black --check custom_components/ + black --check tests/ + flake8 custom_components/keymaster + pylint custom_components/keymaster + pydocstyle custom_components/keymaster tests +deps = + -rrequirements_test.txt + +[testenv:mypy] +basepython = python3 +ignore_errors = True +commands = + mypy custom_components/keymaster +deps = + -rrequirements_test.txt