From 67de19db895408e2da135200d28dc3f23b698a40 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 12:43:12 +0200 Subject: [PATCH 01/12] uv/pip re-try and version bump for python core requirement --- .github/workflows/test.yml | 18 ++++++++++++---- .pre-commit-config.yaml | 7 +++++++ CHANGELOG.md | 4 +++- pyproject.toml | 4 ++-- scripts/core-testing.sh | 43 +++++++++++++++++++++++++++++--------- scripts/python-venv.sh | 28 ------------------------- scripts/setup.sh | 21 ------------------- 7 files changed, 59 insertions(+), 66 deletions(-) delete mode 100755 scripts/python-venv.sh delete mode 100755 scripts/setup.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e14f7dd5..ef053f41e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,16 +77,26 @@ jobs: ${{ env.CACHE_VERSION}}-${{ runner.os }}-base-hacore ${{ env.CACHE_VERSION}}-${{ runner.os }} ${{ env.CACHE_VERSION}} - - name: Set-up environment + - name: Install pre-commit dependencies run: | - scripts/setup.sh + if [ -z "$VIRTUAL_ENV" ]; then + if [ -x "$(command -v uv)" ]; then + uv venv venv + else + python3 -m venv venv + fi + # shellcheck disable=SC1091 # ingesting virtualenv + source venv/bin/activate + fi + + if ! [ -x "$(command -v uv)" ]; then + python3 -m pip install uv + fi - name: Install pre-commit dependencies run: | - . venv/bin/activate pre-commit install-hooks - name: Run all-files pre-commit excluding testing run: | - . venv/bin/activate pre-commit run --all-files --show-diff-on-failure env: # While not problematic, save time on performing the local hooks as they are run from the complete script in the next job SKIP: local-test-core-prep,local-test-pip-prep,local-testing,local-quality diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 28adfd747..cab570648 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,6 +51,13 @@ repos: hooks: - id: yamllint name: "Linting yaml" + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.10.0.1 + hooks: + - id: shellcheck + name: "Shell checking" + args: + - --external-sources - repo: https://github.com/biomejs/pre-commit rev: v1.9.4 hooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index f81d9060d..a5d8bf2c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ Versions from 0.40 and up -## Ongoing +## v0.58.0 - Downstream HA [Core PR](https://github.com/home-assistant/core/pull/138201) update signature of platforms' async_setup_entry +- Maintenance chores +- Minimum python to 3.13 following HA ## v0.57.1 diff --git a/pyproject.toml b/pyproject.toml index 58bbb6189..c6b094a43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ [project] name = "plugwise-beta" -version = "0.57.1" +version = "0.58.0" description = "Plugwise beta custom-component" readme = "README.md" -requires-python = ">=3.12" +requires-python = ">=3.13" license = "MIT" keywords = ["Plugwise", "Adam", "Anna", "P1", "Stretch", "Smile"] authors = [ diff --git a/scripts/core-testing.sh b/scripts/core-testing.sh index 7fd3bd332..2675ce1c9 100755 --- a/scripts/core-testing.sh +++ b/scripts/core-testing.sh @@ -56,14 +56,34 @@ which jq || ( echo -e "${CFAIL}You should have jq installed, exiting${CNORM}"; e # - testing # - quality - my_path=$(git rev-parse --show-toplevel) # Ensure environment is set-up -# shellcheck disable=SC1091 -source "${my_path}/scripts/setup.sh" -# shellcheck disable=SC1091 -source "${my_path}/scripts/python-venv.sh" + +# 20250613 Copied from HA-core and shell-check adjusted and modified for local use +set -e + +if [ -z "$VIRTUAL_ENV" ]; then + if [ -x "$(command -v uv)" ]; then + uv venv venv + else + python3 -m venv venv + fi + # shellcheck disable=SC1091 # ingesting virtualenv + source venv/bin/activate +fi + +if ! [ -x "$(command -v uv)" ]; then + python3 -m pip install uv +fi +# /20250613 + +# Install commit requirements +#uv pip install --upgrade -e . -r requirements_commit.txt +uv pip install --upgrade -r requirements_commit.txt + +# Install pre-commit hook +pre-commit install # i.e. args used for functions, not directions set +u @@ -145,8 +165,11 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then echo "" echo -e "${CINFO}Ensure HA-core venv${CWARN}" - # shellcheck disable=SC1091 - source "${my_path}/scripts/python-venv.sh" + if [ -x "$(command -v uv)" ]; then + uv venv venv + else + python3 -m venv venv + fi # shellcheck disable=SC1091 source ./venv/bin/activate @@ -185,12 +208,12 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "pip_prep" ] ; then echo -e "${CINFO}Installing pip modules (using uv)${CNORM}" echo "" echo -e "${CINFO} - HA requirements (core and test)${CNORM}" - pip install --upgrade -r requirements.txt -r requirements_test.txt + uv pip install --upgrade -r requirements.txt -r requirements_test.txt grep -hEi "${pip_packages}" requirements_test_all.txt > ./tmp/requirements_test_extra.txt echo -e "${CINFO} - extra's required for plugwise${CNORM}" - pip install --upgrade -r ./tmp/requirements_test_extra.txt + uv pip install --upgrade -r ./tmp/requirements_test_extra.txt echo -e "${CINFO} - home assistant basics${CNORM}" - pip install -e . --config-settings editable_mode=compat --constraint homeassistant/package_constraints.txt + uv pip install -e . --config-settings editable_mode=compat --constraint homeassistant/package_constraints.txt echo "" # When using test.py prettier makes multi-line, so use jq module=$(jq '.requirements[]' ../custom_components/plugwise/manifest.json | tr -d '"') diff --git a/scripts/python-venv.sh b/scripts/python-venv.sh deleted file mode 100755 index fd0a70174..000000000 --- a/scripts/python-venv.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -eu - -pyversions=(3.13) -my_path=$(git rev-parse --show-toplevel) -my_venv=${my_path}/venv - -# Ensures a python virtualenv is available at the highest available python3 version -for pv in "${pyversions[@]}"; do - if [ "$(which "python$pv")" ]; then - # If not (yet) available instantiate python virtualenv - if [ ! -d "${my_venv}" ]; then - "python${pv}" -m pip install pip uv || pip install uv - uv venv -p "${pv}" "${my_venv}" - # shellcheck disable=SC1091 - . "${my_venv}/bin/activate" - # Ensure wheel is installed (preventing local issues) - uv pip install wheel - break - fi - fi -done - -# Failsafe -if [ ! -d "${my_venv}" ]; then - echo "Unable to instantiate venv, check your base python3 version and if you have python3-venv installed" - exit 1 -fi diff --git a/scripts/setup.sh b/scripts/setup.sh deleted file mode 100755 index ab8e4e701..000000000 --- a/scripts/setup.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -set -eu - -my_path=$(git rev-parse --show-toplevel) - -# shellcheck disable=SC1091 -. "${my_path}/scripts/python-venv.sh" - -# shellcheck disable=SC2154 -if [ -f "${my_venv}/bin/activate" ]; then - # shellcheck disable=SC1091 - . "${my_venv}/bin/activate" - # Install commit requirements - uv pip install --upgrade uv wheel - uv pip install --upgrade -r requirements_commit.txt - # Install pre-commit hook - "${my_venv}/bin/pre-commit" install -else - echo "Virtualenv available, bailing out" - exit 2 -fi From c4b0d224a5d6657bbf004d4245866aa9026854a4 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 13:02:07 +0200 Subject: [PATCH 02/12] uv/pip re-try and version bump for python core requirement --- requirements_commit.txt | 1 - scripts/core-testing.sh | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 requirements_commit.txt diff --git a/requirements_commit.txt b/requirements_commit.txt deleted file mode 100644 index 416634f52..000000000 --- a/requirements_commit.txt +++ /dev/null @@ -1 +0,0 @@ -pre-commit diff --git a/scripts/core-testing.sh b/scripts/core-testing.sh index 2675ce1c9..5f782cd43 100755 --- a/scripts/core-testing.sh +++ b/scripts/core-testing.sh @@ -79,8 +79,7 @@ fi # /20250613 # Install commit requirements -#uv pip install --upgrade -e . -r requirements_commit.txt -uv pip install --upgrade -r requirements_commit.txt +uv pip install --upgrade pre-commit # Install pre-commit hook pre-commit install From 5a1c8534bd9667b238459b29ea38c3c74c785a5a Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 13:11:35 +0200 Subject: [PATCH 03/12] uv/pip re-try and version bump for python core requirement --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef053f41e..3c8b430bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -90,7 +90,7 @@ jobs: fi if ! [ -x "$(command -v uv)" ]; then - python3 -m pip install uv + python3 -m pip install uv pre-commit fi - name: Install pre-commit dependencies run: | From c0efb2959211764ba5a6a1eff67466f550b51965 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 13:14:21 +0200 Subject: [PATCH 04/12] uv/pip re-try and version bump for python core requirement --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c8b430bd..eadfe9a8a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,9 +94,13 @@ jobs: fi - name: Install pre-commit dependencies run: | + # shellcheck disable=SC1091 # ingesting virtualenv + source venv/bin/activate pre-commit install-hooks - name: Run all-files pre-commit excluding testing run: | + # shellcheck disable=SC1091 # ingesting virtualenv + source venv/bin/activate pre-commit run --all-files --show-diff-on-failure env: # While not problematic, save time on performing the local hooks as they are run from the complete script in the next job SKIP: local-test-core-prep,local-test-pip-prep,local-testing,local-quality From be8b8ac71a04c33eaa258af8ec1d9474a9f5deef Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 13:19:10 +0200 Subject: [PATCH 05/12] uv/pip re-try and version bump for python core requirement --- scripts/core-testing.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/core-testing.sh b/scripts/core-testing.sh index 5f782cd43..6dfa8fcb4 100755 --- a/scripts/core-testing.sh +++ b/scripts/core-testing.sh @@ -172,6 +172,10 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then # shellcheck disable=SC1091 source ./venv/bin/activate + if ! [ -x "$(command -v uv)" ]; then + python3 -m pip install uv + fi + echo -e "${CINFO}Bootstrap pip parts of HA-core${CWARN}" grep -v "^#" "${coredir}/script/bootstrap" | grep "pip install" | sed 's/python3 -m pip install/uv pip install/g' | sh uv pip install -e . --config-settings editable_mode=compat --constraint homeassistant/package_constraints.txt From 4ae2ead40dacff33cb670e06d2a523d223f6d323 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 13:33:37 +0200 Subject: [PATCH 06/12] uv/pip re-try and version bump for python core requirement --- .github/workflows/test.yml | 2 +- scripts/core-testing.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eadfe9a8a..b00841a0c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,7 +81,7 @@ jobs: run: | if [ -z "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then - uv venv venv + uv venv --with-pipvenv else python3 -m venv venv fi diff --git a/scripts/core-testing.sh b/scripts/core-testing.sh index 6dfa8fcb4..9767f7204 100755 --- a/scripts/core-testing.sh +++ b/scripts/core-testing.sh @@ -65,7 +65,7 @@ set -e if [ -z "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then - uv venv venv + uv venv --with-pip venv else python3 -m venv venv fi @@ -165,7 +165,7 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then echo "" echo -e "${CINFO}Ensure HA-core venv${CWARN}" if [ -x "$(command -v uv)" ]; then - uv venv venv + uv venv --with-pip venv else python3 -m venv venv fi From f3982e13df4ff484f84be8b52ab86b1c5a529081 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 13:50:49 +0200 Subject: [PATCH 07/12] uv/pip re-try and version bump for python core requirement --- .github/workflows/test.yml | 2 +- scripts/core-testing.sh | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b00841a0c..eadfe9a8a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,7 +81,7 @@ jobs: run: | if [ -z "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then - uv venv --with-pipvenv + uv venv venv else python3 -m venv venv fi diff --git a/scripts/core-testing.sh b/scripts/core-testing.sh index 9767f7204..b1ea6cc28 100755 --- a/scripts/core-testing.sh +++ b/scripts/core-testing.sh @@ -65,7 +65,7 @@ set -e if [ -z "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then - uv venv --with-pip venv + uv venv venv else python3 -m venv venv fi @@ -165,14 +165,15 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then echo "" echo -e "${CINFO}Ensure HA-core venv${CWARN}" if [ -x "$(command -v uv)" ]; then - uv venv --with-pip venv + uv venv venv else python3 -m venv venv fi # shellcheck disable=SC1091 - source ./venv/bin/activate + source venv/bin/activate if ! [ -x "$(command -v uv)" ]; then + echo -e "${CINFO}Ensure uv presence${CWARN}" python3 -m pip install uv fi @@ -198,16 +199,17 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "pip_prep" ] ; then echo "" echo -e "${CINFO}Ensure HA-core venv${CNORM}" # shellcheck disable=SC1091 - source "./venv/bin/activate" + source venv/bin/activate mkdir -p ./tmp echo "" echo -e "${CINFO}Ensure translations are there${CNORM}" echo "" python3 -m script.translations develop --all > /dev/null 2>&1 echo "" - echo -e "${CINFO}Ensure uv is there${CNORM}" - echo "" - python3 -m pip install pip uv + if ! [ -x "$(command -v uv)" ]; then + echo -e "${CINFO}Ensure uv is there${CNORM}" + python3 -m pip install uv + fi echo -e "${CINFO}Installing pip modules (using uv)${CNORM}" echo "" echo -e "${CINFO} - HA requirements (core and test)${CNORM}" @@ -232,7 +234,7 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "testing" ] ; then echo "" echo -e "${CINFO}Ensure HA-core venv${CNORM}" # shellcheck disable=SC1091 - source "./venv/bin/activate" + source venv/bin/activate echo "" echo -e "${CINFO}Test commencing ...${CNORM}" echo "" @@ -249,7 +251,7 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "quality" ] ; then echo "" echo -e "${CINFO}Ensure HA-core venv${CNORM}" # shellcheck disable=SC1091 - source "./venv/bin/activate" + source venv/bin/activate echo "" set +e echo -e "${CINFO}... ruff-ing component...${CNORM}" @@ -273,7 +275,7 @@ if [ -z "${GITHUB_ACTIONS}" ]; then echo "" echo "Ensure HA-core venv${CNORM}" # shellcheck disable=SC1091 - source "./venv/bin/activate" + source venv/bin/activate echo "" echo -e "${CINFO}Copy back modified files ...${CNORM}" echo "" From 8a45cb984722e5e88afe55a68b668c5cc81d3d52 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 14:05:33 +0200 Subject: [PATCH 08/12] uv/pip re-try and version bump for python core requirement --- .github/workflows/test.yml | 2 +- scripts/core-testing.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eadfe9a8a..50f4232fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,7 +81,7 @@ jobs: run: | if [ -z "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then - uv venv venv + uv venv --seed venv else python3 -m venv venv fi diff --git a/scripts/core-testing.sh b/scripts/core-testing.sh index b1ea6cc28..4569ff9db 100755 --- a/scripts/core-testing.sh +++ b/scripts/core-testing.sh @@ -65,7 +65,7 @@ set -e if [ -z "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then - uv venv venv + uv venv --seed venv else python3 -m venv venv fi @@ -165,7 +165,7 @@ if [ -z "${GITHUB_ACTIONS}" ] || [ "$1" == "core_prep" ] ; then echo "" echo -e "${CINFO}Ensure HA-core venv${CWARN}" if [ -x "$(command -v uv)" ]; then - uv venv venv + uv venv --seed venv else python3 -m venv venv fi From 0d343cc5e258ac24b9b64fe961c9e12c8b469d81 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 13 Jun 2025 15:44:20 +0200 Subject: [PATCH 09/12] Correct fixture typing --- tests/components/plugwise/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/plugwise/conftest.py b/tests/components/plugwise/conftest.py index a88959a26..1148b7942 100644 --- a/tests/components/plugwise/conftest.py +++ b/tests/components/plugwise/conftest.py @@ -37,7 +37,7 @@ def chosen_env(request: pytest.FixtureRequest) -> str: return request.param @pytest.fixture -def cooling_present(request: pytest.FixtureRequest) -> str: +def cooling_present(request: pytest.FixtureRequest) -> bool: """Pass the cooling_present boolean. Used with fixtures that require parametrization of the cooling capability. @@ -61,7 +61,7 @@ def heater_id(request: pytest.FixtureRequest) -> str: return request.param @pytest.fixture -def reboot(request: pytest.FixtureRequest) -> str: +def reboot(request: pytest.FixtureRequest) -> bool: """Pass the reboot boolean. Used with fixtures that require parametrization of the reboot capability. From 6793dc4e59eaf34ec78daa084ccda2e0147c96b8 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 13 Jun 2025 23:41:27 +0200 Subject: [PATCH 10/12] Re-align pyproject --- pyproject.toml | 863 ++++++++++++++++++++++++++----------------------- 1 file changed, 466 insertions(+), 397 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c6b094a43..670cc8376 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,29 +48,28 @@ init-hook = """\ ) \ """ load-plugins = [ - "pylint.extensions.code_style", - "pylint.extensions.typing", - "hass_enforce_class_module", - "hass_enforce_sorted_platforms", - "hass_enforce_super_call", - "hass_enforce_type_hints", - "hass_inheritance", - "hass_imports", - "hass_logger", - "pylint_per_file_ignores", + "pylint.extensions.code_style", + "pylint.extensions.typing", + "hass_decorator", + "hass_enforce_class_module", + "hass_enforce_sorted_platforms", + "hass_enforce_super_call", + "hass_enforce_type_hints", + "hass_inheritance", + "hass_imports", + "hass_logger", + "pylint_per_file_ignores", ] persistent = false extension-pkg-allow-list = [ - "av.audio.stream", - "av.logging", - "av.stream", - "ciso8601", - "orjson", - "cv2", -] -fail-on = [ - "I", + "av.audio.stream", + "av.logging", + "av.stream", + "ciso8601", + "orjson", + "cv2", ] +fail-on = ["I"] [tool.pylint.BASIC] class-const-naming-style = "any" @@ -95,257 +94,257 @@ class-const-naming-style = "any" # consider-using-namedtuple-or-dataclass - too opinionated # consider-using-assignment-expr - decision to use := better left to devs disable = [ - "format", - "abstract-method", - "cyclic-import", - "duplicate-code", - "inconsistent-return-statements", - "locally-disabled", - "not-context-manager", - "too-few-public-methods", - "too-many-ancestors", - "too-many-arguments", - "too-many-instance-attributes", - "too-many-lines", - "too-many-locals", - "too-many-public-methods", - "too-many-boolean-expressions", - "too-many-positional-arguments", - "wrong-import-order", - "consider-using-namedtuple-or-dataclass", - "consider-using-assignment-expr", - "possibly-used-before-assignment", + "format", + "abstract-method", + "cyclic-import", + "duplicate-code", + "inconsistent-return-statements", + "locally-disabled", + "not-context-manager", + "too-few-public-methods", + "too-many-ancestors", + "too-many-arguments", + "too-many-instance-attributes", + "too-many-lines", + "too-many-locals", + "too-many-public-methods", + "too-many-boolean-expressions", + "too-many-positional-arguments", + "wrong-import-order", + "consider-using-namedtuple-or-dataclass", + "consider-using-assignment-expr", + "possibly-used-before-assignment", - # Handled by ruff - # Ref: - "await-outside-async", # PLE1142 - "bad-str-strip-call", # PLE1310 - "bad-string-format-type", # PLE1307 - "bidirectional-unicode", # PLE2502 - "continue-in-finally", # PLE0116 - "duplicate-bases", # PLE0241 - "misplaced-bare-raise", # PLE0704 - "format-needs-mapping", # F502 - "function-redefined", # F811 - # Needed because ruff does not understand type of __all__ generated by a function - # "invalid-all-format", # PLE0605 - "invalid-all-object", # PLE0604 - "invalid-character-backspace", # PLE2510 - "invalid-character-esc", # PLE2513 - "invalid-character-nul", # PLE2514 - "invalid-character-sub", # PLE2512 - "invalid-character-zero-width-space", # PLE2515 - "logging-too-few-args", # PLE1206 - "logging-too-many-args", # PLE1205 - "missing-format-string-key", # F524 - "mixed-format-string", # F506 - "no-method-argument", # N805 - "no-self-argument", # N805 - "nonexistent-operator", # B002 - "nonlocal-without-binding", # PLE0117 - "not-in-loop", # F701, F702 - "notimplemented-raised", # F901 - "return-in-init", # PLE0101 - "return-outside-function", # F706 - "syntax-error", # E999 - "too-few-format-args", # F524 - "too-many-format-args", # F522 - "too-many-star-expressions", # F622 - "truncated-format-string", # F501 - "undefined-all-variable", # F822 - "undefined-variable", # F821 - "used-prior-global-declaration", # PLE0118 - "yield-inside-async-function", # PLE1700 - "yield-outside-function", # F704 - "anomalous-backslash-in-string", # W605 - "assert-on-string-literal", # PLW0129 - "assert-on-tuple", # F631 - "bad-format-string", # W1302, F - "bad-format-string-key", # W1300, F - "bare-except", # E722 - "binary-op-exception", # PLW0711 - "cell-var-from-loop", # B023 - # "dangerous-default-value", # B006, ruff catches new occurrences, needs more work - "duplicate-except", # B014 - "duplicate-key", # F601 - "duplicate-string-formatting-argument", # F - "duplicate-value", # F - "eval-used", # S307 - "exec-used", # S102 - "expression-not-assigned", # B018 - "f-string-without-interpolation", # F541 - "forgotten-debug-statement", # T100 - "format-string-without-interpolation", # F - # "global-statement", # PLW0603, ruff catches new occurrences, needs more work - "global-variable-not-assigned", # PLW0602 - "implicit-str-concat", # ISC001 - "import-self", # PLW0406 - "inconsistent-quotes", # Q000 - "invalid-envvar-default", # PLW1508 - "keyword-arg-before-vararg", # B026 - "logging-format-interpolation", # G - "logging-fstring-interpolation", # G - "logging-not-lazy", # G - "misplaced-future", # F404 - "named-expr-without-context", # PLW0131 - "nested-min-max", # PLW3301 - "pointless-statement", # B018 - "raise-missing-from", # B904 - "redefined-builtin", # A001 - "try-except-raise", # TRY302 - "unused-argument", # ARG001, we don't use it - "unused-format-string-argument", #F507 - "unused-format-string-key", # F504 - "unused-import", # F401 - "unused-variable", # F841 - "useless-else-on-loop", # PLW0120 - "wildcard-import", # F403 - "bad-classmethod-argument", # N804 - "consider-iterating-dictionary", # SIM118 - "empty-docstring", # D419 - "invalid-name", # N815 - "line-too-long", # E501, disabled globally - "missing-class-docstring", # D101 - "missing-final-newline", # W292 - "missing-function-docstring", # D103 - "missing-module-docstring", # D100 - "multiple-imports", #E401 - "singleton-comparison", # E711, E712 - "subprocess-run-check", # PLW1510 - "superfluous-parens", # UP034 - "ungrouped-imports", # I001 - "unidiomatic-typecheck", # E721 - "unnecessary-direct-lambda-call", # PLC3002 - "unnecessary-lambda-assignment", # PLC3001 - "unnecessary-pass", # PIE790 - "unneeded-not", # SIM208 - "useless-import-alias", # PLC0414 - "wrong-import-order", # I001 - "wrong-import-position", # E402 - "comparison-of-constants", # PLR0133 - "comparison-with-itself", # PLR0124 - "consider-alternative-union-syntax", # UP007 - "consider-merging-isinstance", # PLR1701 - "consider-using-alias", # UP006 - "consider-using-dict-comprehension", # C402 - "consider-using-generator", # C417 - "consider-using-get", # SIM401 - "consider-using-set-comprehension", # C401 - "consider-using-sys-exit", # PLR1722 - "consider-using-ternary", # SIM108 - "literal-comparison", # F632 - "property-with-parameters", # PLR0206 - "super-with-arguments", # UP008 - "too-many-branches", # PLR0912 - "too-many-return-statements", # PLR0911 - "too-many-statements", # PLR0915 - "trailing-comma-tuple", # COM818 - "unnecessary-comprehension", # C416 - "use-a-generator", # C417 - "use-dict-literal", # C406 - "use-list-literal", # C405 - "useless-object-inheritance", # UP004 - "useless-return", # PLR1711 - "no-else-break", # RET508 - "no-else-continue", # RET507 - "no-else-raise", # RET506 - "no-else-return", # RET505 - "broad-except", # BLE001 - "protected-access", # SLF001 - "broad-exception-raised", # TRY002 - "consider-using-f-string", # PLC0209 - # "no-self-use", # PLR6301 # Optional plugin, not enabled + # Handled by ruff + # Ref: + "await-outside-async", # PLE1142 + "bad-str-strip-call", # PLE1310 + "bad-string-format-type", # PLE1307 + "bidirectional-unicode", # PLE2502 + "continue-in-finally", # PLE0116 + "duplicate-bases", # PLE0241 + "misplaced-bare-raise", # PLE0704 + "format-needs-mapping", # F502 + "function-redefined", # F811 + # Needed because ruff does not understand type of __all__ generated by a function + # "invalid-all-format", # PLE0605 + "invalid-all-object", # PLE0604 + "invalid-character-backspace", # PLE2510 + "invalid-character-esc", # PLE2513 + "invalid-character-nul", # PLE2514 + "invalid-character-sub", # PLE2512 + "invalid-character-zero-width-space", # PLE2515 + "logging-too-few-args", # PLE1206 + "logging-too-many-args", # PLE1205 + "missing-format-string-key", # F524 + "mixed-format-string", # F506 + "no-method-argument", # N805 + "no-self-argument", # N805 + "nonexistent-operator", # B002 + "nonlocal-without-binding", # PLE0117 + "not-in-loop", # F701, F702 + "notimplemented-raised", # F901 + "return-in-init", # PLE0101 + "return-outside-function", # F706 + "syntax-error", # E999 + "too-few-format-args", # F524 + "too-many-format-args", # F522 + "too-many-star-expressions", # F622 + "truncated-format-string", # F501 + "undefined-all-variable", # F822 + "undefined-variable", # F821 + "used-prior-global-declaration", # PLE0118 + "yield-inside-async-function", # PLE1700 + "yield-outside-function", # F704 + "anomalous-backslash-in-string", # W605 + "assert-on-string-literal", # PLW0129 + "assert-on-tuple", # F631 + "bad-format-string", # W1302, F + "bad-format-string-key", # W1300, F + "bare-except", # E722 + "binary-op-exception", # PLW0711 + "cell-var-from-loop", # B023 + # "dangerous-default-value", # B006, ruff catches new occurrences, needs more work + "duplicate-except", # B014 + "duplicate-key", # F601 + "duplicate-string-formatting-argument", # F + "duplicate-value", # F + "eval-used", # S307 + "exec-used", # S102 + "expression-not-assigned", # B018 + "f-string-without-interpolation", # F541 + "forgotten-debug-statement", # T100 + "format-string-without-interpolation", # F + # "global-statement", # PLW0603, ruff catches new occurrences, needs more work + "global-variable-not-assigned", # PLW0602 + "implicit-str-concat", # ISC001 + "import-self", # PLW0406 + "inconsistent-quotes", # Q000 + "invalid-envvar-default", # PLW1508 + "keyword-arg-before-vararg", # B026 + "logging-format-interpolation", # G + "logging-fstring-interpolation", # G + "logging-not-lazy", # G + "misplaced-future", # F404 + "named-expr-without-context", # PLW0131 + "nested-min-max", # PLW3301 + "pointless-statement", # B018 + "raise-missing-from", # B904 + "redefined-builtin", # A001 + "try-except-raise", # TRY302 + "unused-argument", # ARG001, we don't use it + "unused-format-string-argument", #F507 + "unused-format-string-key", # F504 + "unused-import", # F401 + "unused-variable", # F841 + "useless-else-on-loop", # PLW0120 + "wildcard-import", # F403 + "bad-classmethod-argument", # N804 + "consider-iterating-dictionary", # SIM118 + "empty-docstring", # D419 + "invalid-name", # N815 + "line-too-long", # E501, disabled globally + "missing-class-docstring", # D101 + "missing-final-newline", # W292 + "missing-function-docstring", # D103 + "missing-module-docstring", # D100 + "multiple-imports", #E401 + "singleton-comparison", # E711, E712 + "subprocess-run-check", # PLW1510 + "superfluous-parens", # UP034 + "ungrouped-imports", # I001 + "unidiomatic-typecheck", # E721 + "unnecessary-direct-lambda-call", # PLC3002 + "unnecessary-lambda-assignment", # PLC3001 + "unnecessary-pass", # PIE790 + "unneeded-not", # SIM208 + "useless-import-alias", # PLC0414 + "wrong-import-order", # I001 + "wrong-import-position", # E402 + "comparison-of-constants", # PLR0133 + "comparison-with-itself", # PLR0124 + "consider-alternative-union-syntax", # UP007 + "consider-merging-isinstance", # PLR1701 + "consider-using-alias", # UP006 + "consider-using-dict-comprehension", # C402 + "consider-using-generator", # C417 + "consider-using-get", # SIM401 + "consider-using-set-comprehension", # C401 + "consider-using-sys-exit", # PLR1722 + "consider-using-ternary", # SIM108 + "literal-comparison", # F632 + "property-with-parameters", # PLR0206 + "super-with-arguments", # UP008 + "too-many-branches", # PLR0912 + "too-many-return-statements", # PLR0911 + "too-many-statements", # PLR0915 + "trailing-comma-tuple", # COM818 + "unnecessary-comprehension", # C416 + "use-a-generator", # C417 + "use-dict-literal", # C406 + "use-list-literal", # C405 + "useless-object-inheritance", # UP004 + "useless-return", # PLR1711 + "no-else-break", # RET508 + "no-else-continue", # RET507 + "no-else-raise", # RET506 + "no-else-return", # RET505 + "broad-except", # BLE001 + "protected-access", # SLF001 + "broad-exception-raised", # TRY002 + "consider-using-f-string", # PLC0209 + # "no-self-use", # PLR6301 # Optional plugin, not enabled - # Handled by mypy - # Ref: - "abstract-class-instantiated", - "arguments-differ", - "assigning-non-slot", - "assignment-from-no-return", - "assignment-from-none", - "bad-exception-cause", - "bad-format-character", - "bad-reversed-sequence", - "bad-super-call", - "bad-thread-instantiation", - "catching-non-exception", - "comparison-with-callable", - "deprecated-class", - "dict-iter-missing-items", - "format-combined-specification", - "global-variable-undefined", - "import-error", - "inconsistent-mro", - "inherit-non-class", - "init-is-generator", - "invalid-class-object", - "invalid-enum-extension", - "invalid-envvar-value", - "invalid-format-returned", - "invalid-hash-returned", - "invalid-metaclass", - "invalid-overridden-method", - "invalid-repr-returned", - "invalid-sequence-index", - "invalid-slice-index", - "invalid-slots-object", - "invalid-slots", - "invalid-star-assignment-target", - "invalid-str-returned", - "invalid-unary-operand-type", - "invalid-unicode-codec", - "isinstance-second-argument-not-valid-type", - "method-hidden", - "misplaced-format-function", - "missing-format-argument-key", - "missing-format-attribute", - "missing-kwoa", - "no-member", - "no-value-for-parameter", - "non-iterator-returned", - "non-str-assignment-to-dunder-name", - "nonlocal-and-global", - "not-a-mapping", - "not-an-iterable", - "not-async-context-manager", - "not-callable", - "not-context-manager", - "overridden-final-method", - "raising-bad-type", - "raising-non-exception", - "redundant-keyword-arg", - "relative-beyond-top-level", - "self-cls-assignment", - "signature-differs", - "star-needs-assignment-target", - "subclassed-final-class", - "super-without-brackets", - "too-many-function-args", - "typevar-double-variance", - "typevar-name-mismatch", - "unbalanced-dict-unpacking", - "unbalanced-tuple-unpacking", - "unexpected-keyword-arg", - "unhashable-member", - "unpacking-non-sequence", - "unsubscriptable-object", - "unsupported-assignment-operation", - "unsupported-binary-operation", - "unsupported-delete-operation", - "unsupported-membership-test", - "used-before-assignment", - "using-final-decorator-in-unsupported-version", - "wrong-exception-operation", + # Handled by mypy + # Ref: + "abstract-class-instantiated", + "arguments-differ", + "assigning-non-slot", + "assignment-from-no-return", + "assignment-from-none", + "bad-exception-cause", + "bad-format-character", + "bad-reversed-sequence", + "bad-super-call", + "bad-thread-instantiation", + "catching-non-exception", + "comparison-with-callable", + "deprecated-class", + "dict-iter-missing-items", + "format-combined-specification", + "global-variable-undefined", + "import-error", + "inconsistent-mro", + "inherit-non-class", + "init-is-generator", + "invalid-class-object", + "invalid-enum-extension", + "invalid-envvar-value", + "invalid-format-returned", + "invalid-hash-returned", + "invalid-metaclass", + "invalid-overridden-method", + "invalid-repr-returned", + "invalid-sequence-index", + "invalid-slice-index", + "invalid-slots-object", + "invalid-slots", + "invalid-star-assignment-target", + "invalid-str-returned", + "invalid-unary-operand-type", + "invalid-unicode-codec", + "isinstance-second-argument-not-valid-type", + "method-hidden", + "misplaced-format-function", + "missing-format-argument-key", + "missing-format-attribute", + "missing-kwoa", + "no-member", + "no-value-for-parameter", + "non-iterator-returned", + "non-str-assignment-to-dunder-name", + "nonlocal-and-global", + "not-a-mapping", + "not-an-iterable", + "not-async-context-manager", + "not-callable", + "not-context-manager", + "overridden-final-method", + "raising-bad-type", + "raising-non-exception", + "redundant-keyword-arg", + "relative-beyond-top-level", + "self-cls-assignment", + "signature-differs", + "star-needs-assignment-target", + "subclassed-final-class", + "super-without-brackets", + "too-many-function-args", + "typevar-double-variance", + "typevar-name-mismatch", + "unbalanced-dict-unpacking", + "unbalanced-tuple-unpacking", + "unexpected-keyword-arg", + "unhashable-member", + "unpacking-non-sequence", + "unsubscriptable-object", + "unsupported-assignment-operation", + "unsupported-binary-operation", + "unsupported-delete-operation", + "unsupported-membership-test", + "used-before-assignment", + "using-final-decorator-in-unsupported-version", + "wrong-exception-operation", ] enable = [ - #"useless-suppression", # temporarily every now and then to clean them up - "use-symbolic-message-instead", + #"useless-suppression", # temporarily every now and then to clean them up + "use-symbolic-message-instead", ] per-file-ignores = [ - # redefined-outer-name: Tests reference fixtures in the test function - # use-implicit-booleaness-not-comparison: Tests need to validate that a list - # or a dict is returned - "/tests/:redefined-outer-name,use-implicit-booleaness-not-comparison", + # redefined-outer-name: Tests reference fixtures in the test function + # use-implicit-booleaness-not-comparison: Tests need to validate that a list + # or a dict is returned + "/tests/:redefined-outer-name,use-implicit-booleaness-not-comparison", ] [tool.pylint.REPORTS] @@ -353,7 +352,7 @@ score = false [tool.pylint.TYPECHECK] ignored-classes = [ - "_CountingAttr", # for attrs + "_CountingAttr", # for attrs ] mixin-class-rgx = ".*[Mm]ix[Ii]n" @@ -362,9 +361,9 @@ expected-line-ending-format = "LF" [tool.pylint.EXCEPTIONS] overgeneral-exceptions = [ - "builtins.BaseException", - "builtins.Exception", - # "homeassistant.exceptions.HomeAssistantError", # too many issues + "builtins.BaseException", + "builtins.Exception", + # "homeassistant.exceptions.HomeAssistantError", # too many issues ] [tool.pylint.TYPING] @@ -373,6 +372,14 @@ runtime-typing = false [tool.pylint.CODE_STYLE] max-line-length-suggestions = 72 +[tool.pytest.ini_options] +testpaths = ["tests"] +norecursedirs = [".git", "testing_config"] +log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s" +log_date_format = "%Y-%m-%d %H:%M:%S" +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" + [tool.coverage.run] source = ["plugwise-beta"] @@ -391,164 +398,226 @@ exclude_lines = [ ] [tool.ruff] -required-version = ">=0.5.3" +required-version = ">=0.11.0" [tool.ruff.lint] select = [ - "A001", # Variable {name} is shadowing a Python builtin - "ASYNC210", # Async functions should not call blocking HTTP methods - "ASYNC220", # Async functions should not create subprocesses with blocking methods - "ASYNC221", # Async functions should not run processes with blocking methods - "ASYNC222", # Async functions should not wait on processes with blocking methods - "ASYNC230", # Async functions should not open files with blocking methods like open - "ASYNC251", # Async functions should not call time.sleep - "B002", # Python does not support the unary prefix increment - "B005", # Using .strip() with multi-character strings is misleading - "B007", # Loop control variable {name} not used within loop body - "B014", # Exception handler with duplicate exception - "B015", # Pointless comparison. Did you mean to assign a value? Otherwise, prepend assert or remove it. - "B017", # pytest.raises(BaseException) should be considered evil - "B018", # Found useless attribute access. Either assign it to a variable or remove it. - "B023", # Function definition does not bind loop variable {name} - "B026", # Star-arg unpacking after a keyword argument is strongly discouraged - "B032", # Possible unintentional type annotation (using :). Did you mean to assign (using =)? - "B904", # Use raise from to specify exception cause - "B905", # zip() without an explicit strict= parameter - "BLE", - "C", # complexity - "COM818", # Trailing comma on bare tuple prohibited - "D", # docstrings - "DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow() - "DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts) - "E", # pycodestyle - "F", # pyflakes/autoflake - "F541", # f-string without any placeholders - "FLY", # flynt - "FURB", # refurb - "G", # flake8-logging-format - "I", # isort - "INP", # flake8-no-pep420 - "ISC", # flake8-implicit-str-concat - "ICN001", # import concentions; {name} should be imported as {asname} - "LOG", # flake8-logging - "N804", # First argument of a class method should be named cls - "N805", # First argument of a method should be named self - "N815", # Variable {name} in class scope should not be mixedCase - "PERF", # Perflint - "PGH", # pygrep-hooks - "PIE", # flake8-pie - "PL", # pylint - "PT", # flake8-pytest-style - "PTH", # flake8-pathlib - "PYI", # flake8-pyi - "RET", # flake8-return - "RSE", # flake8-raise - "RUF005", # Consider iterable unpacking instead of concatenation - "RUF006", # Store a reference to the return value of asyncio.create_task - "RUF010", # Use explicit conversion flag - "RUF013", # PEP 484 prohibits implicit Optional - "RUF017", # Avoid quadratic list summation - "RUF018", # Avoid assignment expressions in assert statements - "RUF019", # Unnecessary key check before dictionary access - # "RUF100", # Unused `noqa` directive; temporarily every now and then to clean them up - "S102", # Use of exec detected - "S103", # bad-file-permissions - "S108", # hardcoded-temp-file - "S306", # suspicious-mktemp-usage - "S307", # suspicious-eval-usage - "S313", # suspicious-xmlc-element-tree-usage - "S314", # suspicious-xml-element-tree-usage - "S315", # suspicious-xml-expat-reader-usage - "S316", # suspicious-xml-expat-builder-usage - "S317", # suspicious-xml-sax-usage - "S318", # suspicious-xml-mini-dom-usage - "S319", # suspicious-xml-pull-dom-usage - "S601", # paramiko-call - "S602", # subprocess-popen-with-shell-equals-true - "S604", # call-with-shell-equals-true - "S608", # hardcoded-sql-expression - "S609", # unix-command-wildcard-injection - "SIM", # flake8-simplify - "SLF", # flake8-self - "SLOT", # flake8-slots - "T100", # Trace found: {name} used - "T20", # flake8-print - "TCH", # flake8-type-checking - "TID", # Tidy imports - "TRY", # tryceratops - "UP", # pyupgrade - "UP031", # Use format specifiers instead of percent format - "UP032", # Use f-string instead of `format` call - "W", # pycodestyle + "A001", # Variable {name} is shadowing a Python builtin + "ASYNC", # flake8-async + "B002", # Python does not support the unary prefix increment + "B005", # Using .strip() with multi-character strings is misleading + "B007", # Loop control variable {name} not used within loop body + "B009", # Do not call getattr with a constant attribute value. It is not any safer than normal property access. + "B014", # Exception handler with duplicate exception + "B015", # Pointless comparison. Did you mean to assign a value? Otherwise, prepend assert or remove it. + "B017", # pytest.raises(BaseException) should be considered evil + "B018", # Found useless attribute access. Either assign it to a variable or remove it. + "B023", # Function definition does not bind loop variable {name} + "B024", # `{name}` is an abstract base class, but it has no abstract methods or properties + "B026", # Star-arg unpacking after a keyword argument is strongly discouraged + "B032", # Possible unintentional type annotation (using :). Did you mean to assign (using =)? + "B035", # Dictionary comprehension uses static key + "B904", # Use raise from to specify exception cause + "B905", # zip() without an explicit strict= parameter + "BLE", + "C", # complexity + "COM818", # Trailing comma on bare tuple prohibited + "D", # docstrings + "DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow() + "DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts) + "E", # pycodestyle + "F", # pyflakes/autoflake + "F541", # f-string without any placeholders + "FLY", # flynt + "FURB", # refurb + "G", # flake8-logging-format + "I", # isort + "INP", # flake8-no-pep420 + "ISC", # flake8-implicit-str-concat + "ICN001", # import concentions; {name} should be imported as {asname} + "LOG", # flake8-logging + "N804", # First argument of a class method should be named cls + "N805", # First argument of a method should be named self + "N815", # Variable {name} in class scope should not be mixedCase + "PERF", # Perflint + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "PTH", # flake8-pathlib + "PYI", # flake8-pyi + "RET", # flake8-return + "RSE", # flake8-raise + "RUF005", # Consider iterable unpacking instead of concatenation + "RUF006", # Store a reference to the return value of asyncio.create_task + "RUF007", # Prefer itertools.pairwise() over zip() when iterating over successive pairs + "RUF008", # Do not use mutable default values for dataclass attributes + "RUF010", # Use explicit conversion flag + "RUF013", # PEP 484 prohibits implicit Optional + "RUF016", # Slice in indexed access to type {value_type} uses type {index_type} instead of an integer + "RUF017", # Avoid quadratic list summation + "RUF018", # Avoid assignment expressions in assert statements + "RUF019", # Unnecessary key check before dictionary access + "RUF020", # {never_like} | T is equivalent to T + "RUF021", # Parenthesize a and b expressions when chaining and and or together, to make the precedence clear + "RUF022", # Sort __all__ + "RUF023", # Sort __slots__ + "RUF024", # Do not pass mutable objects as values to dict.fromkeys + "RUF026", # default_factory is a positional-only argument to defaultdict + "RUF030", # print() call in assert statement is likely unintentional + "RUF032", # Decimal() called with float literal argument + "RUF033", # __post_init__ method with argument defaults + "RUF034", # Useless if-else condition + "RUF100", # Unused `noqa` directive + "RUF101", # noqa directives that use redirected rule codes + "RUF200", # Failed to parse pyproject.toml: {message} + "S102", # Use of exec detected + "S103", # bad-file-permissions + "S108", # hardcoded-temp-file + "S306", # suspicious-mktemp-usage + "S307", # suspicious-eval-usage + "S313", # suspicious-xmlc-element-tree-usage + "S314", # suspicious-xml-element-tree-usage + "S315", # suspicious-xml-expat-reader-usage + "S316", # suspicious-xml-expat-builder-usage + "S317", # suspicious-xml-sax-usage + "S318", # suspicious-xml-mini-dom-usage + "S319", # suspicious-xml-pull-dom-usage + "S601", # paramiko-call + "S602", # subprocess-popen-with-shell-equals-true + "S604", # call-with-shell-equals-true + "S608", # hardcoded-sql-expression + "S609", # unix-command-wildcard-injection + "SIM", # flake8-simplify + "SLF", # flake8-self + "SLOT", # flake8-slots + "T100", # Trace found: {name} used + "T20", # flake8-print + "TC", # flake8-type-checking + "TID", # Tidy imports + "TRY", # tryceratops + "UP", # pyupgrade + "UP031", # Use format specifiers instead of percent format + "UP032", # Use f-string instead of `format` call + "W", # pycodestyle ] ignore = [ - "D202", # No blank lines allowed after function docstring - "D203", # 1 blank line required before class docstring - "D213", # Multi-line docstring summary should start at the second line - "D406", # Section name should end with a newline - "D407", # Section name underlining - "E501", # line too long + "ASYNC109", # Async function definition with a `timeout` parameter Use `asyncio.timeout` instead + "ASYNC110", # Use `asyncio.Event` instead of awaiting `asyncio.sleep` in a `while` loop + "D202", # No blank lines allowed after function docstring + "D203", # 1 blank line required before class docstring + "D213", # Multi-line docstring summary should start at the second line + "D406", # Section name should end with a newline + "D407", # Section name underlining + "E501", # line too long - "PLC1901", # {existing} can be simplified to {replacement} as an empty string is falsey; too many false positives - "PLR0911", # Too many return statements ({returns} > {max_returns}) - "PLR0912", # Too many branches ({branches} > {max_branches}) - "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) - "PLR0915", # Too many statements ({statements} > {max_statements}) - "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable - "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target - "PT011", # pytest.raises({exception}) is too broad, set the `match` parameter or use a more specific exception - "PT018", # Assertion should be broken down into multiple parts - "RUF001", # String contains ambiguous unicode character. - "RUF002", # Docstring contains ambiguous unicode character. - "RUF003", # Comment contains ambiguous unicode character. - "RUF015", # Prefer next(...) over single element slice - "SIM102", # Use a single if statement instead of nested if statements - "SIM103", # Return the condition {condition} directly - "SIM108", # Use ternary operator {contents} instead of if-else-block - "SIM115", # Use context handler for opening files + "PLC1901", # {existing} can be simplified to {replacement} as an empty string is falsey; too many false positives + "PLR0911", # Too many return statements ({returns} > {max_returns}) + "PLR0912", # Too many branches ({branches} > {max_branches}) + "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) + "PLR0915", # Too many statements ({statements} > {max_statements}) + "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable + "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target + "PT011", # pytest.raises({exception}) is too broad, set the `match` parameter or use a more specific exception + "PT018", # Assertion should be broken down into multiple parts + "RUF001", # String contains ambiguous unicode character. + "RUF002", # Docstring contains ambiguous unicode character. + "RUF003", # Comment contains ambiguous unicode character. + "RUF015", # Prefer next(...) over single element slice + "SIM102", # Use a single if statement instead of nested if statements + "SIM103", # Return the condition {condition} directly + "SIM108", # Use ternary operator {contents} instead of if-else-block + "SIM115", # Use context handler for opening files - # Moving imports into type-checking blocks can mess with pytest.patch() - "TC001", # Move application import {} into a type-checking block - "TC002", # Move third-party import {} into a type-checking block - "TC003", # Move standard library import {} into a type-checking block + # Moving imports into type-checking blocks can mess with pytest.patch() + "TC001", # Move application import {} into a type-checking block + "TC002", # Move third-party import {} into a type-checking block + "TC003", # Move standard library import {} into a type-checking block + # Quotes for typing.cast generally not necessary, only for performance critical paths + "TC006", # Add quotes to type expression in typing.cast() - "TRY003", # Avoid specifying long messages outside the exception class - "TRY400", # Use `logging.exception` instead of `logging.error` - # Ignored due to performance: https://github.com/charliermarsh/ruff/issues/2923 - "UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` + "TRY003", # Avoid specifying long messages outside the exception class + "TRY400", # Use `logging.exception` instead of `logging.error` + # Ignored due to performance: https://github.com/charliermarsh/ruff/issues/2923 + "UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` - # May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules - "W191", - "E111", - "E114", - "E117", - "D206", - "D300", - "Q", - "COM812", - "COM819", + # May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules + "W191", + "E111", + "E114", + "E117", + "D206", + "D300", + "Q", + "COM812", + "COM819", - # Disabled because ruff does not understand type of __all__ generated by a function - "PLE0605" + # Disabled because ruff does not understand type of __all__ generated by a function + "PLE0605", ] [tool.ruff.lint.flake8-import-conventions.extend-aliases] voluptuous = "vol" +"homeassistant.components.air_quality.PLATFORM_SCHEMA" = "AIR_QUALITY_PLATFORM_SCHEMA" +"homeassistant.components.alarm_control_panel.PLATFORM_SCHEMA" = "ALARM_CONTROL_PANEL_PLATFORM_SCHEMA" "homeassistant.components.binary_sensor.PLATFORM_SCHEMA" = "BINARY_SENSOR_PLATFORM_SCHEMA" "homeassistant.components.button.PLATFORM_SCHEMA" = "BUTTON_PLATFORM_SCHEMA" +"homeassistant.components.calendar.PLATFORM_SCHEMA" = "CALENDAR_PLATFORM_SCHEMA" +"homeassistant.components.camera.PLATFORM_SCHEMA" = "CAMERA_PLATFORM_SCHEMA" "homeassistant.components.climate.PLATFORM_SCHEMA" = "CLIMATE_PLATFORM_SCHEMA" +"homeassistant.components.conversation.PLATFORM_SCHEMA" = "CONVERSATION_PLATFORM_SCHEMA" +"homeassistant.components.cover.PLATFORM_SCHEMA" = "COVER_PLATFORM_SCHEMA" +"homeassistant.components.date.PLATFORM_SCHEMA" = "DATE_PLATFORM_SCHEMA" +"homeassistant.components.datetime.PLATFORM_SCHEMA" = "DATETIME_PLATFORM_SCHEMA" +"homeassistant.components.device_tracker.PLATFORM_SCHEMA" = "DEVICE_TRACKER_PLATFORM_SCHEMA" +"homeassistant.components.event.PLATFORM_SCHEMA" = "EVENT_PLATFORM_SCHEMA" +"homeassistant.components.fan.PLATFORM_SCHEMA" = "FAN_PLATFORM_SCHEMA" +"homeassistant.components.geo_location.PLATFORM_SCHEMA" = "GEO_LOCATION_PLATFORM_SCHEMA" +"homeassistant.components.humidifier.PLATFORM_SCHEMA" = "HUMIDIFIER_PLATFORM_SCHEMA" +"homeassistant.components.image.PLATFORM_SCHEMA" = "IMAGE_PLATFORM_SCHEMA" +"homeassistant.components.image_processing.PLATFORM_SCHEMA" = "IMAGE_PROCESSING_PLATFORM_SCHEMA" +"homeassistant.components.lawn_mower.PLATFORM_SCHEMA" = "LAWN_MOWER_PLATFORM_SCHEMA" +"homeassistant.components.light.PLATFORM_SCHEMA" = "LIGHT_PLATFORM_SCHEMA" +"homeassistant.components.lock.PLATFORM_SCHEMA" = "LOCK_PLATFORM_SCHEMA" +"homeassistant.components.media_player.PLATFORM_SCHEMA" = "MEDIA_PLAYER_PLATFORM_SCHEMA" +"homeassistant.components.notify.PLATFORM_SCHEMA" = "NOTIFY_PLATFORM_SCHEMA" "homeassistant.components.number.PLATFORM_SCHEMA" = "NUMBER_PLATFORM_SCHEMA" +"homeassistant.components.remote.PLATFORM_SCHEMA" = "REMOTE_PLATFORM_SCHEMA" +"homeassistant.components.scene.PLATFORM_SCHEMA" = "SCENE_PLATFORM_SCHEMA" "homeassistant.components.select.PLATFORM_SCHEMA" = "SELECT_PLATFORM_SCHEMA" "homeassistant.components.sensor.PLATFORM_SCHEMA" = "SENSOR_PLATFORM_SCHEMA" +"homeassistant.components.siren.PLATFORM_SCHEMA" = "SIREN_PLATFORM_SCHEMA" +"homeassistant.components.stt.PLATFORM_SCHEMA" = "STT_PLATFORM_SCHEMA" "homeassistant.components.switch.PLATFORM_SCHEMA" = "SWITCH_PLATFORM_SCHEMA" +"homeassistant.components.text.PLATFORM_SCHEMA" = "TEXT_PLATFORM_SCHEMA" +"homeassistant.components.time.PLATFORM_SCHEMA" = "TIME_PLATFORM_SCHEMA" +"homeassistant.components.todo.PLATFORM_SCHEMA" = "TODO_PLATFORM_SCHEMA" +"homeassistant.components.tts.PLATFORM_SCHEMA" = "TTS_PLATFORM_SCHEMA" +"homeassistant.components.vacuum.PLATFORM_SCHEMA" = "VACUUM_PLATFORM_SCHEMA" +"homeassistant.components.valve.PLATFORM_SCHEMA" = "VALVE_PLATFORM_SCHEMA" +"homeassistant.components.update.PLATFORM_SCHEMA" = "UPDATE_PLATFORM_SCHEMA" +"homeassistant.components.wake_word.PLATFORM_SCHEMA" = "WAKE_WORD_PLATFORM_SCHEMA" +"homeassistant.components.water_heater.PLATFORM_SCHEMA" = "WATER_HEATER_PLATFORM_SCHEMA" +"homeassistant.components.weather.PLATFORM_SCHEMA" = "WEATHER_PLATFORM_SCHEMA" "homeassistant.core.DOMAIN" = "HOMEASSISTANT_DOMAIN" "homeassistant.helpers.area_registry" = "ar" +"homeassistant.helpers.category_registry" = "cr" "homeassistant.helpers.config_validation" = "cv" "homeassistant.helpers.device_registry" = "dr" "homeassistant.helpers.entity_registry" = "er" +"homeassistant.helpers.floor_registry" = "fr" "homeassistant.helpers.issue_registry" = "ir" +"homeassistant.helpers.label_registry" = "lr" +"homeassistant.util.color" = "color_util" "homeassistant.util.dt" = "dt_util" +"homeassistant.util.json" = "json_util" +"homeassistant.util.location" = "location_util" +"homeassistant.util.logging" = "logging_util" +"homeassistant.util.network" = "network_util" +"homeassistant.util.ulid" = "ulid_util" +"homeassistant.util.uuid" = "uuid_util" +"homeassistant.util.yaml" = "yaml_util" [tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false From 2222e63f7fa7215376f62a91777ed260d2c1b2db Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sat, 14 Jun 2025 10:21:57 +0200 Subject: [PATCH 11/12] Attempt to release requested changes (if present) --- .github/workflows/test.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 50f4232fa..0c33f0983 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,7 +43,6 @@ jobs: run: | if [[ "${{ contains(github.event.pull_request.labels.*.name, 'require-dev-pass') }}" == "true" ]]; then echo "strict_dev=true" >> $GITHUB_OUTPUT - else echo "strict_dev=false" >> $GITHUB_OUTPUT fi @@ -226,6 +225,11 @@ jobs: RELEASE_TESTS_FAILED="${{ needs.ha-core-release.outputs.release_failed }}" FAIL_COUNT=0 + # Get the action-bot's latest state + LAST_REVIEW_STATE=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" \ + | jq -r '[.[] | select(.user.login=="github-actions[bot]")] | last | .state') + if [[ $DEV_TESTS_FAILED == "true" ]]; then COMMENT_BODY=":x: **Error while testing for Development HA-core:**\n\n" else @@ -256,6 +260,21 @@ jobs: COMMENT_BODY+=":heavy_check_mark: **Success:** No problem with testing against released HA-core.\n" fi + # If everything is OK, don't approve - if requested changes before and everything is good now, approve + # to release the 'requested changes' bit + if [[ $FAIL_COUNT -eq 1 ]]; then + if [[ "$LAST_REVIEW_STATE" == "CHANGES_REQUESTED" ]]; then + echo "Lifting previous changes requested — submitting approval." + curl -s -X POST \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Content-Type: application/json" \ + --data "{\"event\": \"APPROVE\", \"body\": \"$COMMENT_BODY\"}" \ + "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" + else + echo "No prior changes requested — skipping approval." + fi + fi + if [[ $FAIL_COUNT -eq 1 ]]; then echo "Comment and approve the pull request" curl -s -X POST \ From cb80699271eee153d7afc338d4a00039b9576ccb Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sat, 14 Jun 2025 10:32:30 +0200 Subject: [PATCH 12/12] Oops :) --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0c33f0983..7073a67a8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -262,7 +262,7 @@ jobs: # If everything is OK, don't approve - if requested changes before and everything is good now, approve # to release the 'requested changes' bit - if [[ $FAIL_COUNT -eq 1 ]]; then + if [[ $FAIL_COUNT -eq 0 ]]; then if [[ "$LAST_REVIEW_STATE" == "CHANGES_REQUESTED" ]]; then echo "Lifting previous changes requested — submitting approval." curl -s -X POST \ @@ -283,6 +283,7 @@ jobs: --data "{\"event\": \"APPROVE\", \"body\": \"$COMMENT_BODY\"}" \ "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" fi + if [[ $FAIL_COUNT -eq 2 ]]; then echo "Requesting changes on the pull request" curl -s -X POST \