From 7fd6dce55ff3b5317ba87044b53f1bff87f38bbe Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Mon, 5 Feb 2024 11:37:39 -0500 Subject: [PATCH] Migrate to Ruff for lint and format (#4852) * Migrate to Ruff for lint and format * Fix pylint issues * DBus property sets into normal awaitable methods * Fix tests relying on separate tasks in connect * Fixes from feedback --- .devcontainer/devcontainer.json | 12 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/ci.yaml | 179 ++++------- .github/workflows/matchers/flake8.json | 30 -- .pre-commit-config.yaml | 29 +- .vscode/tasks.json | 18 +- pyproject.toml | 287 +++++++++++++++++- requirements.txt | 1 - requirements_tests.txt | 6 +- setup.cfg | 17 -- supervisor/api/os.py | 18 +- supervisor/api/supervisor.py | 2 +- supervisor/backups/validate.py | 2 +- supervisor/core.py | 16 +- supervisor/dbus/agent/__init__.py | 7 +- supervisor/dbus/agent/boards/green.py | 24 +- supervisor/dbus/agent/boards/yellow.py | 24 +- supervisor/dbus/network/setting/generate.py | 4 +- supervisor/dbus/systemd.py | 4 +- supervisor/dbus/udisks2/drive.py | 4 +- supervisor/docker/addon.py | 4 +- supervisor/docker/homeassistant.py | 2 +- supervisor/docker/monitor.py | 6 +- supervisor/homeassistant/api.py | 15 +- supervisor/host/apparmor.py | 5 +- supervisor/host/logs.py | 3 +- supervisor/host/sound.py | 14 +- supervisor/jobs/decorator.py | 7 +- supervisor/jobs/job_group.py | 2 +- supervisor/misc/scheduler.py | 3 +- supervisor/plugins/base.py | 2 +- supervisor/resolution/checks/backups.py | 15 +- supervisor/resolution/checks/free_space.py | 15 +- supervisor/resolution/evaluations/base.py | 7 +- .../resolution/evaluations/dns_server.py | 16 +- .../fixups/system_clear_full_backup.py | 2 +- supervisor/resolution/notify.py | 3 +- supervisor/utils/codenotary.py | 5 +- supervisor/utils/common.py | 5 +- supervisor/utils/dbus.py | 4 +- supervisor/utils/yaml.py | 2 +- tests/api/test_addons.py | 16 +- tests/api/test_jobs.py | 6 +- tests/api/test_network.py | 4 +- tests/api/test_os.py | 2 +- tests/api/test_resolution.py | 10 +- tests/api/test_store.py | 8 +- tests/api/test_supervisor.py | 4 +- tests/backups/test_manager.py | 20 +- tests/conftest.py | 43 +-- tests/dbus/agent/boards/test_green.py | 27 +- tests/dbus/agent/boards/test_yellow.py | 26 +- tests/dbus/agent/test_agent.py | 2 +- tests/dbus/agent/test_apparmor.py | 2 +- tests/dbus/agent/test_cgroup.py | 2 +- tests/dbus/agent/test_datadisk.py | 8 +- tests/dbus/agent/test_system.py | 6 +- tests/dbus/network/setting/test_init.py | 8 +- tests/dbus/network/test_connection.py | 2 +- tests/dbus/network/test_interface.py | 4 +- tests/dbus/network/test_network_manager.py | 4 +- tests/dbus/network/test_settings.py | 2 +- tests/dbus/network/test_wireless.py | 2 +- tests/dbus/test_systemd.py | 4 +- tests/dbus/test_timedate.py | 8 +- tests/dbus/udisks2/test_block.py | 4 +- tests/dbus/udisks2/test_drive.py | 4 +- tests/dbus/udisks2/test_manager.py | 6 +- tests/dbus_service_mocks/agent_apparmor.py | 3 - tests/dbus_service_mocks/agent_boards.py | 3 - .../dbus_service_mocks/agent_boards_green.py | 3 - .../agent_boards_supervised.py | 3 - .../dbus_service_mocks/agent_boards_yellow.py | 3 - tests/dbus_service_mocks/agent_cgroup.py | 3 - tests/dbus_service_mocks/agent_datadisk.py | 3 - tests/dbus_service_mocks/agent_system.py | 3 - tests/dbus_service_mocks/hostname.py | 3 - tests/dbus_service_mocks/logind.py | 3 - .../network_access_point.py | 3 - .../network_active_connection.py | 3 - .../network_connection_settings.py | 3 - tests/dbus_service_mocks/network_device.py | 3 - .../network_device_wireless.py | 3 - .../dbus_service_mocks/network_dns_manager.py | 3 - tests/dbus_service_mocks/network_ip4config.py | 3 - tests/dbus_service_mocks/network_ip6config.py | 3 - tests/dbus_service_mocks/network_manager.py | 3 - tests/dbus_service_mocks/network_settings.py | 3 - tests/dbus_service_mocks/os_agent.py | 3 - tests/dbus_service_mocks/rauc.py | 3 - tests/dbus_service_mocks/resolved.py | 3 - tests/dbus_service_mocks/systemd.py | 9 +- tests/dbus_service_mocks/systemd_unit.py | 7 +- tests/dbus_service_mocks/timedate.py | 3 - tests/dbus_service_mocks/udisks2_block.py | 3 - tests/dbus_service_mocks/udisks2_drive.py | 3 - .../dbus_service_mocks/udisks2_filesystem.py | 3 - tests/dbus_service_mocks/udisks2_loop.py | 3 - tests/dbus_service_mocks/udisks2_manager.py | 3 - tests/dbus_service_mocks/udisks2_partition.py | 3 - .../udisks2_partition_table.py | 3 - tests/docker/test_interface.py | 8 +- tests/homeassistant/test_core.py | 20 +- tests/host/test_connectivity.py | 2 +- tests/host/test_logs.py | 12 +- tests/host/test_manager.py | 6 +- tests/host/test_network.py | 4 +- tests/jobs/test_job_decorator.py | 24 +- tests/jobs/test_job_manager.py | 14 +- tests/os/test_data_disk.py | 4 +- tests/os/test_manager.py | 4 +- tests/plugins/test_plugin_base.py | 24 +- .../check/test_check_multiple_data_disks.py | 2 +- .../fixup/test_system_rename_data_disk.py | 4 +- tests/resolution/test_resolution_manager.py | 12 +- tests/security/test_module.py | 5 +- tests/store/test_custom_repository.py | 4 +- tests/store/test_store_manager.py | 16 +- tests/store/test_validate.py | 15 +- tests/test_core.py | 4 +- tests/test_coresys.py | 4 +- tox.ini | 8 +- 122 files changed, 694 insertions(+), 668 deletions(-) delete mode 100644 .github/workflows/matchers/flake8.json delete mode 100644 setup.cfg diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2642fb76e..d09213424 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,11 +10,13 @@ "customizations": { "vscode": { "extensions": [ - "ms-python.python", + "charliermarsh.ruff", "ms-python.pylint", "ms-python.vscode-pylance", "visualstudioexptteam.vscodeintellicode", - "esbenp.prettier-vscode" + "redhat.vscode-yaml", + "esbenp.prettier-vscode", + "GitHub.vscode-pull-request-github" ], "settings": { "terminal.integrated.profiles.linux": { @@ -28,9 +30,9 @@ "editor.formatOnType": true, "files.trimTrailingWhitespace": true, "python.pythonPath": "/usr/local/bin/python3", - "python.formatting.provider": "black", - "python.formatting.blackArgs": ["--target-version", "py312"], - "python.formatting.blackPath": "/usr/local/bin/black" + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff" + } } } }, diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index af9147e96..0a3480a4c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -52,7 +52,7 @@ - [ ] Local tests pass. **Your PR cannot be merged unless tests pass** - [ ] There is no commented out code in this PR. - [ ] I have followed the [development checklist][dev-checklist] -- [ ] The code has been formatted using Black (`black --fast supervisor tests`) +- [ ] The code has been formatted using Ruff (`ruff format supervisor tests`) - [ ] Tests have been added to verify that the new code works. If API endpoints of add-on configuration are added/changed: diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 50155648e..9f192a09c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -61,8 +61,8 @@ jobs: . venv/bin/activate pre-commit install-hooks - lint-black: - name: Check black + lint-ruff-format: + name: Check ruff-format runs-on: ubuntu-latest needs: prepare steps: @@ -85,10 +85,67 @@ jobs: run: | echo "Failed to restore Python virtual environment from cache" exit 1 - - name: Run black + - name: Restore pre-commit environment from cache + id: cache-precommit + uses: actions/cache@v4.0.0 + with: + path: ${{ env.PRE_COMMIT_CACHE }} + key: | + ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + - name: Fail job if cache restore failed + if: steps.cache-venv.outputs.cache-hit != 'true' + run: | + echo "Failed to restore Python virtual environment from cache" + exit 1 + - name: Run ruff-format run: | . venv/bin/activate - black --target-version py312 --check supervisor tests setup.py + pre-commit run --hook-stage manual ruff-format --all-files --show-diff-on-failure + env: + RUFF_OUTPUT_FORMAT: github + + lint-ruff: + name: Check ruff + runs-on: ubuntu-latest + needs: prepare + steps: + - name: Check out code from GitHub + uses: actions/checkout@v4.1.1 + - name: Set up Python ${{ needs.prepare.outputs.python-version }} + uses: actions/setup-python@v5.0.0 + id: python + with: + python-version: ${{ needs.prepare.outputs.python-version }} + - name: Restore Python virtual environment + id: cache-venv + uses: actions/cache@v4.0.0 + with: + path: venv + key: | + ${{ runner.os }}-venv-${{ needs.prepare.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }} + - name: Fail job if Python cache restore failed + if: steps.cache-venv.outputs.cache-hit != 'true' + run: | + echo "Failed to restore Python virtual environment from cache" + exit 1 + - name: Restore pre-commit environment from cache + id: cache-precommit + uses: actions/cache@v4.0.0 + with: + path: ${{ env.PRE_COMMIT_CACHE }} + key: | + ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + - name: Fail job if cache restore failed + if: steps.cache-venv.outputs.cache-hit != 'true' + run: | + echo "Failed to restore Python virtual environment from cache" + exit 1 + - name: Run ruff + run: | + . venv/bin/activate + pre-commit run --hook-stage manual ruff --all-files --show-diff-on-failure + env: + RUFF_OUTPUT_FORMAT: github lint-dockerfile: name: Check Dockerfile @@ -149,79 +206,6 @@ jobs: . venv/bin/activate pre-commit run --hook-stage manual check-executables-have-shebangs --all-files - lint-flake8: - name: Check flake8 - runs-on: ubuntu-latest - needs: prepare - steps: - - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 - - name: Set up Python ${{ needs.prepare.outputs.python-version }} - uses: actions/setup-python@v5.0.0 - id: python - with: - python-version: ${{ needs.prepare.outputs.python-version }} - - name: Restore Python virtual environment - id: cache-venv - uses: actions/cache@v4.0.0 - with: - path: venv - key: | - ${{ runner.os }}-venv-${{ needs.prepare.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }} - - name: Fail job if Python cache restore failed - if: steps.cache-venv.outputs.cache-hit != 'true' - run: | - echo "Failed to restore Python virtual environment from cache" - exit 1 - - name: Register flake8 problem matcher - run: | - echo "::add-matcher::.github/workflows/matchers/flake8.json" - - name: Run flake8 - run: | - . venv/bin/activate - flake8 supervisor tests - - lint-isort: - name: Check isort - runs-on: ubuntu-latest - needs: prepare - steps: - - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 - - name: Set up Python ${{ needs.prepare.outputs.python-version }} - uses: actions/setup-python@v5.0.0 - id: python - with: - python-version: ${{ needs.prepare.outputs.python-version }} - - name: Restore Python virtual environment - id: cache-venv - uses: actions/cache@v4.0.0 - with: - path: venv - key: | - ${{ runner.os }}-venv-${{ needs.prepare.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }} - - name: Fail job if Python cache restore failed - if: steps.cache-venv.outputs.cache-hit != 'true' - run: | - echo "Failed to restore Python virtual environment from cache" - exit 1 - - name: Restore pre-commit environment from cache - id: cache-precommit - uses: actions/cache@v4.0.0 - with: - path: ${{ env.PRE_COMMIT_CACHE }} - key: | - ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} - - name: Fail job if cache restore failed - if: steps.cache-venv.outputs.cache-hit != 'true' - run: | - echo "Failed to restore Python virtual environment from cache" - exit 1 - - name: Run isort - run: | - . venv/bin/activate - pre-commit run --hook-stage manual isort --all-files --show-diff-on-failure - lint-json: name: Check JSON runs-on: ubuntu-latest @@ -298,47 +282,6 @@ jobs: . venv/bin/activate pylint supervisor tests - lint-pyupgrade: - name: Check pyupgrade - runs-on: ubuntu-latest - needs: prepare - steps: - - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 - - name: Set up Python ${{ needs.prepare.outputs.python-version }} - uses: actions/setup-python@v5.0.0 - id: python - with: - python-version: ${{ needs.prepare.outputs.python-version }} - - name: Restore Python virtual environment - id: cache-venv - uses: actions/cache@v4.0.0 - with: - path: venv - key: | - ${{ runner.os }}-venv-${{ needs.prepare.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }} - - name: Fail job if Python cache restore failed - if: steps.cache-venv.outputs.cache-hit != 'true' - run: | - echo "Failed to restore Python virtual environment from cache" - exit 1 - - name: Restore pre-commit environment from cache - id: cache-precommit - uses: actions/cache@v4.0.0 - with: - path: ${{ env.PRE_COMMIT_CACHE }} - key: | - ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} - - name: Fail job if cache restore failed - if: steps.cache-venv.outputs.cache-hit != 'true' - run: | - echo "Failed to restore Python virtual environment from cache" - exit 1 - - name: Run pyupgrade - run: | - . venv/bin/activate - pre-commit run --hook-stage manual pyupgrade --all-files --show-diff-on-failure - pytest: runs-on: ubuntu-latest needs: prepare diff --git a/.github/workflows/matchers/flake8.json b/.github/workflows/matchers/flake8.json deleted file mode 100644 index 83163e728..000000000 --- a/.github/workflows/matchers/flake8.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "flake8-error", - "severity": "error", - "pattern": [ - { - "regexp": "^(.*):(\\d+):(\\d+):\\s(E\\d{3}\\s.*)$", - "file": 1, - "line": 2, - "column": 3, - "message": 4 - } - ] - }, - { - "owner": "flake8-warning", - "severity": "warning", - "pattern": [ - { - "regexp": "^(.*):(\\d+):(\\d+):\\s([CDFNW]\\d{3}\\s.*)$", - "file": 1, - "line": 2, - "column": 3, - "message": 4 - } - ] - } - ] -} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b55b7084b..c1f8252d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,34 +1,15 @@ repos: - - repo: https://github.com/psf/black - rev: 23.12.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.14 hooks: - - id: black + - id: ruff args: - - --safe - - --quiet - - --target-version - - py312 + - --fix + - id: ruff-format files: ^((supervisor|tests)/.+)?[^/]+\.py$ - - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 - hooks: - - id: flake8 - additional_dependencies: - - flake8-docstrings==1.7.0 - - pydocstyle==6.3.0 - files: ^(supervisor|script|tests)/.+\.py$ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-executables-have-shebangs stages: [manual] - id: check-json - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort - - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 - hooks: - - id: pyupgrade - args: [--py312-plus] diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a0994d4d7..88930b6e1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -58,9 +58,23 @@ "problemMatcher": [] }, { - "label": "Flake8", + "label": "Ruff Check", "type": "shell", - "command": "flake8 supervisor tests", + "command": "ruff check --fix supervisor tests", + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Ruff Format", + "type": "shell", + "command": "ruff format supervisor tests", "group": { "kind": "test", "isDefault": true diff --git a/pyproject.toml b/pyproject.toml index db39b5611..98ed85bc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ good-names = ["id", "i", "j", "k", "ex", "Run", "_", "fp", "T", "os"] [tool.pylint."MESSAGES CONTROL"] # Reasons disabled: -# format - handled by black +# format - handled by ruff # abstract-method - with intro of async there are always methods missing # cyclic-import - doesn't test if both import on load # duplicate-code - unavoidable @@ -71,6 +71,136 @@ disable = [ "too-many-statements", "unused-argument", "consider-using-with", + + # 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 + "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", # PGH001 + "exec-used", # S102 + # "expression-not-assigned", # B018, ruff catches new occurrences, needs more work + "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, ruff catches new occurrences, needs more work + "raise-missing-from", # TRY200 + # "redefined-builtin", # A001, ruff is way more stricter, needs work + "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 + "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, typing extension + "consider-merging-isinstance", # PLR1701 + # "consider-using-alias", # UP006, typing extension + "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-self-use", # PLR6301 # Optional plugin, not enabled ] [tool.pylint.REPORTS] @@ -97,16 +227,145 @@ filterwarnings = [ "ignore::pytest.PytestUnraisableExceptionWarning", ] -[tool.isort] -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -line_length = 88 -indent = " " -force_sort_within_sections = true -sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] -default_section = "THIRDPARTY" -forced_separate = "tests" -combine_as_imports = true -use_parentheses = true -known_first_party = ["supervisor", "tests"] +[tool.ruff] +select = [ + "B002", # Python does not support the unary prefix increment + "B007", # Loop control variable {name} not used within loop body + "B014", # Exception handler with duplicate exception + "B023", # Function definition does not bind loop variable {name} + "B026", # Star-arg unpacking after a keyword argument is strongly discouraged + "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 + "G", # flake8-logging-format + "I", # isort + "ICN001", # import concentions; {name} should be imported as {asname} + "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 + "PGH001", # No builtin eval() allowed + "PGH004", # Use specific rule codes when using noqa + "PLC0414", # Useless import alias. Import alias does not rename original package. + "PLC", # pylint + "PLE", # pylint + "PLR", # pylint + "PLW", # pylint + "Q000", # Double quotes found but single quotes preferred + "RUF006", # Store a reference to the return value of asyncio.create_task + "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 + "S320", # suspicious-xmle-tree-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 + "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass + "SIM117", # Merge with-statements that use the same scope + "SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys() + "SIM201", # Use {left} != {right} instead of not {left} == {right} + "SIM208", # Use {expr} instead of not (not {expr}) + "SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a} + "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'. + "SIM401", # Use get from dict with default instead of an if block + "T100", # Trace found: {name} used + "T20", # flake8-print + "TID251", # Banned imports + "TRY004", # Prefer TypeError exception for invalid type + "TRY200", # Use raise from to specify exception cause + "TRY302", # Remove exception handler; error is immediately re-raised + "UP", # pyupgrade + "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 + "E731", # do not assign a lambda expression, use a def + + # Ignore ignored, as the rule is now back in preview/nursery, which cannot + # be ignored anymore without warnings. + # https://github.com/astral-sh/ruff/issues/7491 + # "PLC1901", # Lots of false positives + + # False positives https://github.com/astral-sh/ruff/issues/5386 + "PLC0208", # Use a sequence type instead of a `set` when iterating over values + "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 + "UP006", # keep type annotation style as is + "UP007", # keep type annotation style as is + # 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", + "Q000", + "Q001", + "Q002", + "Q003", + "COM812", + "COM819", + "ISC001", + "ISC002", + + # Disabled because ruff does not understand type of __all__ generated by a function + "PLE0605", +] + +[tool.ruff.flake8-import-conventions.extend-aliases] +voluptuous = "vol" + +[tool.ruff.flake8-pytest-style] +fixture-parentheses = false + +[tool.ruff.flake8-tidy-imports.banned-api] +"pytz".msg = "use zoneinfo instead" + +[tool.ruff.isort] +force-sort-within-sections = true +section-order = [ + "future", + "standard-library", + "third-party", + "first-party", + "local-folder", +] +forced-separate = ["tests"] +known-first-party = ["supervisor", "tests"] +combine-as-imports = true +split-on-trailing-comma = false + +[tool.ruff.per-file-ignores] + +# DBus Service Mocks must use typing and names understood by dbus-fast +"tests/dbus_service_mocks/*.py" = ["F722", "F821", "N815"] + +[tool.ruff.mccabe] +max-complexity = 25 diff --git a/requirements.txt b/requirements.txt index 1300ca64a..952649199 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ aiodns==3.1.1 aiohttp==3.9.3 aiohttp-fast-url-dispatcher==0.3.0 -async_timeout==4.0.3 atomicwrites-homeassistant==1.4.1 attrs==23.2.0 awesomeversion==23.11.0 diff --git a/requirements_tests.txt b/requirements_tests.txt index fa0613df1..68de0af5a 100644 --- a/requirements_tests.txt +++ b/requirements_tests.txt @@ -1,16 +1,12 @@ -black==23.12.1 coverage==7.4.1 -flake8-docstrings==1.7.0 -flake8==7.0.0 pre-commit==3.6.0 -pydocstyle==6.3.0 pylint==3.0.3 pytest-aiohttp==1.0.5 pytest-asyncio==0.23.3 pytest-cov==4.1.0 pytest-timeout==2.2.0 pytest==7.4.4 -pyupgrade==3.15.0 +ruff==0.1.14 time-machine==2.13.0 typing_extensions==4.9.0 urllib3==2.2.0 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 23eb4307b..000000000 --- a/setup.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[flake8] -exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build -doctests = True -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 -per-file-ignores = - tests/dbus_service_mocks/*.py: F821,F722 diff --git a/supervisor/api/os.py b/supervisor/api/os.py index 216278969..6d754c9d0 100644 --- a/supervisor/api/os.py +++ b/supervisor/api/os.py @@ -130,13 +130,17 @@ class APIOS(CoreSysAttributes): body = await api_validate(SCHEMA_GREEN_OPTIONS, request) if ATTR_ACTIVITY_LED in body: - self.sys_dbus.agent.board.green.activity_led = body[ATTR_ACTIVITY_LED] + await self.sys_dbus.agent.board.green.set_activity_led( + body[ATTR_ACTIVITY_LED] + ) if ATTR_POWER_LED in body: - self.sys_dbus.agent.board.green.power_led = body[ATTR_POWER_LED] + await self.sys_dbus.agent.board.green.set_power_led(body[ATTR_POWER_LED]) if ATTR_SYSTEM_HEALTH_LED in body: - self.sys_dbus.agent.board.green.user_led = body[ATTR_SYSTEM_HEALTH_LED] + await self.sys_dbus.agent.board.green.set_user_led( + body[ATTR_SYSTEM_HEALTH_LED] + ) self.sys_dbus.agent.board.green.save_data() @@ -155,13 +159,15 @@ class APIOS(CoreSysAttributes): body = await api_validate(SCHEMA_YELLOW_OPTIONS, request) if ATTR_DISK_LED in body: - self.sys_dbus.agent.board.yellow.disk_led = body[ATTR_DISK_LED] + await self.sys_dbus.agent.board.yellow.set_disk_led(body[ATTR_DISK_LED]) if ATTR_HEARTBEAT_LED in body: - self.sys_dbus.agent.board.yellow.heartbeat_led = body[ATTR_HEARTBEAT_LED] + await self.sys_dbus.agent.board.yellow.set_heartbeat_led( + body[ATTR_HEARTBEAT_LED] + ) if ATTR_POWER_LED in body: - self.sys_dbus.agent.board.yellow.power_led = body[ATTR_POWER_LED] + await self.sys_dbus.agent.board.yellow.set_power_led(body[ATTR_POWER_LED]) self.sys_dbus.agent.board.yellow.save_data() self.sys_resolution.create_issue( diff --git a/supervisor/api/supervisor.py b/supervisor/api/supervisor.py index 04c94ba8b..dd8de4389 100644 --- a/supervisor/api/supervisor.py +++ b/supervisor/api/supervisor.py @@ -140,7 +140,7 @@ class APISupervisor(CoreSysAttributes): if ATTR_DIAGNOSTICS in body: self.sys_config.diagnostics = body[ATTR_DIAGNOSTICS] - self.sys_dbus.agent.diagnostics = body[ATTR_DIAGNOSTICS] + await self.sys_dbus.agent.set_diagnostics(body[ATTR_DIAGNOSTICS]) if body[ATTR_DIAGNOSTICS]: init_sentry(self.coresys) diff --git a/supervisor/backups/validate.py b/supervisor/backups/validate.py index c7c98c303..701002cfc 100644 --- a/supervisor/backups/validate.py +++ b/supervisor/backups/validate.py @@ -53,7 +53,7 @@ def unique_addons(addons_list): def v1_homeassistant( - homeassistant_data: dict[str, Any] | None + homeassistant_data: dict[str, Any] | None, ) -> dict[str, Any] | None: """Cleanup homeassistant artefacts from v1.""" if not homeassistant_data: diff --git a/supervisor/core.py b/supervisor/core.py index 6cb299ee6..2b5b0fa5a 100644 --- a/supervisor/core.py +++ b/supervisor/core.py @@ -5,8 +5,6 @@ from contextlib import suppress from datetime import timedelta import logging -import async_timeout - from .const import ( ATTR_STARTUP, RUN_SUPERVISOR_STATE, @@ -179,7 +177,15 @@ class Core(CoreSysAttributes): and not self.sys_dev and self.supported ): - self.sys_dbus.agent.diagnostics = self.sys_config.diagnostics + try: + await self.sys_dbus.agent.set_diagnostics(self.sys_config.diagnostics) + except Exception as err: # pylint: disable=broad-except + _LOGGER.warning( + "Could not set diagnostics to %s due to %s", + self.sys_config.diagnostics, + err, + ) + capture_exception(err) # Evaluate the system await self.sys_resolution.evaluate.evaluate_system() @@ -298,7 +304,7 @@ class Core(CoreSysAttributes): # Stage 1 try: - async with async_timeout.timeout(10): + async with asyncio.timeout(10): await asyncio.wait( [ self.sys_create_task(coro) @@ -314,7 +320,7 @@ class Core(CoreSysAttributes): # Stage 2 try: - async with async_timeout.timeout(10): + async with asyncio.timeout(10): await asyncio.wait( [ self.sys_create_task(coro) diff --git a/supervisor/dbus/agent/__init__.py b/supervisor/dbus/agent/__init__.py index 4cbff54d3..4da0223c7 100644 --- a/supervisor/dbus/agent/__init__.py +++ b/supervisor/dbus/agent/__init__.py @@ -1,5 +1,6 @@ """OS-Agent implementation for DBUS.""" import asyncio +from collections.abc import Awaitable import logging from typing import Any @@ -80,11 +81,9 @@ class OSAgent(DBusInterfaceProxy): """Return if diagnostics is enabled on OS-Agent.""" return self.properties[DBUS_ATTR_DIAGNOSTICS] - @diagnostics.setter - @dbus_property - def diagnostics(self, value: bool) -> None: + def set_diagnostics(self, value: bool) -> Awaitable[None]: """Enable or disable OS-Agent diagnostics.""" - asyncio.create_task(self.dbus.set_diagnostics(value)) + return self.dbus.set_diagnostics(value) @property def all(self) -> list[DBusInterface]: diff --git a/supervisor/dbus/agent/boards/green.py b/supervisor/dbus/agent/boards/green.py index 3eec7a9b7..360782bc6 100644 --- a/supervisor/dbus/agent/boards/green.py +++ b/supervisor/dbus/agent/boards/green.py @@ -1,6 +1,7 @@ """Green board management.""" import asyncio +from collections.abc import Awaitable from dbus_fast.aio.message_bus import MessageBus @@ -25,11 +26,10 @@ class Green(BoardProxy): """Get activity LED enabled.""" return self.properties[DBUS_ATTR_ACTIVITY_LED] - @activity_led.setter - def activity_led(self, enabled: bool) -> None: + def set_activity_led(self, enabled: bool) -> Awaitable[None]: """Enable/disable activity LED.""" self._data[ATTR_ACTIVITY_LED] = enabled - asyncio.create_task(self.dbus.Boards.Green.set_activity_led(enabled)) + return self.dbus.Boards.Green.set_activity_led(enabled) @property @dbus_property @@ -37,11 +37,10 @@ class Green(BoardProxy): """Get power LED enabled.""" return self.properties[DBUS_ATTR_POWER_LED] - @power_led.setter - def power_led(self, enabled: bool) -> None: + def set_power_led(self, enabled: bool) -> Awaitable[None]: """Enable/disable power LED.""" self._data[ATTR_POWER_LED] = enabled - asyncio.create_task(self.dbus.Boards.Green.set_power_led(enabled)) + return self.dbus.Boards.Green.set_power_led(enabled) @property @dbus_property @@ -49,17 +48,18 @@ class Green(BoardProxy): """Get user LED enabled.""" return self.properties[DBUS_ATTR_USER_LED] - @user_led.setter - def user_led(self, enabled: bool) -> None: + def set_user_led(self, enabled: bool) -> Awaitable[None]: """Enable/disable disk LED.""" self._data[ATTR_USER_LED] = enabled - asyncio.create_task(self.dbus.Boards.Green.set_user_led(enabled)) + return self.dbus.Boards.Green.set_user_led(enabled) async def connect(self, bus: MessageBus) -> None: """Connect to D-Bus.""" await super().connect(bus) # Set LEDs based on settings on connect - self.activity_led = self._data[ATTR_ACTIVITY_LED] - self.power_led = self._data[ATTR_POWER_LED] - self.user_led = self._data[ATTR_USER_LED] + await asyncio.gather( + self.set_activity_led(self._data[ATTR_ACTIVITY_LED]), + self.set_power_led(self._data[ATTR_POWER_LED]), + self.set_user_led(self._data[ATTR_USER_LED]), + ) diff --git a/supervisor/dbus/agent/boards/yellow.py b/supervisor/dbus/agent/boards/yellow.py index 98764f975..dae9dc641 100644 --- a/supervisor/dbus/agent/boards/yellow.py +++ b/supervisor/dbus/agent/boards/yellow.py @@ -1,6 +1,7 @@ """Yellow board management.""" import asyncio +from collections.abc import Awaitable from dbus_fast.aio.message_bus import MessageBus @@ -25,11 +26,10 @@ class Yellow(BoardProxy): """Get heartbeat LED enabled.""" return self.properties[DBUS_ATTR_HEARTBEAT_LED] - @heartbeat_led.setter - def heartbeat_led(self, enabled: bool) -> None: + def set_heartbeat_led(self, enabled: bool) -> Awaitable[None]: """Enable/disable heartbeat LED.""" self._data[ATTR_HEARTBEAT_LED] = enabled - asyncio.create_task(self.dbus.Boards.Yellow.set_heartbeat_led(enabled)) + return self.dbus.Boards.Yellow.set_heartbeat_led(enabled) @property @dbus_property @@ -37,11 +37,10 @@ class Yellow(BoardProxy): """Get power LED enabled.""" return self.properties[DBUS_ATTR_POWER_LED] - @power_led.setter - def power_led(self, enabled: bool) -> None: + def set_power_led(self, enabled: bool) -> Awaitable[None]: """Enable/disable power LED.""" self._data[ATTR_POWER_LED] = enabled - asyncio.create_task(self.dbus.Boards.Yellow.set_power_led(enabled)) + return self.dbus.Boards.Yellow.set_power_led(enabled) @property @dbus_property @@ -49,17 +48,18 @@ class Yellow(BoardProxy): """Get disk LED enabled.""" return self.properties[DBUS_ATTR_DISK_LED] - @disk_led.setter - def disk_led(self, enabled: bool) -> None: + def set_disk_led(self, enabled: bool) -> Awaitable[None]: """Enable/disable disk LED.""" self._data[ATTR_DISK_LED] = enabled - asyncio.create_task(self.dbus.Boards.Yellow.set_disk_led(enabled)) + return self.dbus.Boards.Yellow.set_disk_led(enabled) async def connect(self, bus: MessageBus) -> None: """Connect to D-Bus.""" await super().connect(bus) # Set LEDs based on settings on connect - self.disk_led = self._data[ATTR_DISK_LED] - self.heartbeat_led = self._data[ATTR_HEARTBEAT_LED] - self.power_led = self._data[ATTR_POWER_LED] + await asyncio.gather( + self.set_disk_led(self._data[ATTR_DISK_LED]), + self.set_heartbeat_led(self._data[ATTR_HEARTBEAT_LED]), + self.set_power_led(self._data[ATTR_POWER_LED]), + ) diff --git a/supervisor/dbus/network/setting/generate.py b/supervisor/dbus/network/setting/generate.py index db46ef0a3..13a40fad8 100644 --- a/supervisor/dbus/network/setting/generate.py +++ b/supervisor/dbus/network/setting/generate.py @@ -7,6 +7,8 @@ from uuid import uuid4 from dbus_fast import Variant +from ....host.const import InterfaceMethod, InterfaceType +from .. import NetworkManager from . import ( ATTR_ASSIGNED_MAC, CONF_ATTR_802_ETHERNET, @@ -19,8 +21,6 @@ from . import ( CONF_ATTR_PATH, CONF_ATTR_VLAN, ) -from .. import NetworkManager -from ....host.const import InterfaceMethod, InterfaceType if TYPE_CHECKING: from ....host.configuration import Interface diff --git a/supervisor/dbus/systemd.py b/supervisor/dbus/systemd.py index 6475029ed..60a7ae092 100644 --- a/supervisor/dbus/systemd.py +++ b/supervisor/dbus/systemd.py @@ -42,9 +42,7 @@ def systemd_errors(func): return await func(*args, **kwds) except DBusFatalError as err: if err.type == DBUS_ERR_SYSTEMD_NO_SUCH_UNIT: - # pylint: disable=raise-missing-from - raise DBusSystemdNoSuchUnit(str(err)) - # pylint: enable=raise-missing-from + raise DBusSystemdNoSuchUnit(str(err)) from None raise err return wrapper diff --git a/supervisor/dbus/udisks2/drive.py b/supervisor/dbus/udisks2/drive.py index d5f219c23..4ae02078b 100644 --- a/supervisor/dbus/udisks2/drive.py +++ b/supervisor/dbus/udisks2/drive.py @@ -1,6 +1,6 @@ """Interface to UDisks2 Drive over D-Bus.""" -from datetime import datetime, timezone +from datetime import UTC, datetime from dbus_fast.aio import MessageBus @@ -95,7 +95,7 @@ class UDisks2Drive(DBusInterfaceProxy): """Return time drive first detected.""" return datetime.fromtimestamp( self.properties[DBUS_ATTR_TIME_DETECTED] * 10**-6 - ).astimezone(timezone.utc) + ).astimezone(UTC) @property @dbus_property diff --git a/supervisor/docker/addon.py b/supervisor/docker/addon.py index cb5051117..b7065736a 100644 --- a/supervisor/docker/addon.py +++ b/supervisor/docker/addon.py @@ -233,10 +233,10 @@ class DockerAddon(DockerInterface): tmpfs = {} if self.addon.with_tmpfs: - tmpfs["/tmp"] = "" + tmpfs["/tmp"] = "" # noqa: S108 if not self.addon.host_ipc: - tmpfs["/dev/shm"] = "" + tmpfs["/dev/shm"] = "" # noqa: S108 # Return None if no tmpfs is present if tmpfs: diff --git a/supervisor/docker/homeassistant.py b/supervisor/docker/homeassistant.py index 0502420a2..56961f2c7 100644 --- a/supervisor/docker/homeassistant.py +++ b/supervisor/docker/homeassistant.py @@ -175,7 +175,7 @@ class DockerHomeAssistant(DockerInterface): ENV_TOKEN: self.sys_homeassistant.supervisor_token, ENV_TOKEN_OLD: self.sys_homeassistant.supervisor_token, }, - tmpfs={"/tmp": ""}, + tmpfs={"/tmp": ""}, # noqa: S108 oom_score_adj=-300, ) _LOGGER.info( diff --git a/supervisor/docker/monitor.py b/supervisor/docker/monitor.py index fbbe71d1c..525d295c0 100644 --- a/supervisor/docker/monitor.py +++ b/supervisor/docker/monitor.py @@ -1,4 +1,6 @@ """Supervisor docker monitor based on events.""" + +from contextlib import suppress from dataclasses import dataclass import logging from threading import Thread @@ -47,10 +49,8 @@ class DockerMonitor(CoreSysAttributes, Thread): async def unload(self): """Stop docker events monitor.""" self._events.close() - try: + with suppress(RuntimeError): self.join(timeout=5) - except RuntimeError: - pass _LOGGER.info("Stopped docker events monitor") diff --git a/supervisor/homeassistant/api.py b/supervisor/homeassistant/api.py index bbb525187..4c5cda35e 100644 --- a/supervisor/homeassistant/api.py +++ b/supervisor/homeassistant/api.py @@ -1,9 +1,9 @@ """Home Assistant control object.""" import asyncio -from contextlib import asynccontextmanager, suppress -from datetime import datetime, timedelta +from contextlib import AbstractAsyncContextManager, asynccontextmanager, suppress +from datetime import UTC, datetime, timedelta import logging -from typing import Any, AsyncContextManager +from typing import Any import aiohttp from aiohttp import hdrs @@ -39,9 +39,8 @@ class HomeAssistantAPI(CoreSysAttributes): ) async def ensure_access_token(self) -> None: """Ensure there is an access token.""" - if ( - self.access_token is not None - and self._access_token_expires > datetime.utcnow() + if self.access_token is not None and self._access_token_expires > datetime.now( + tz=UTC ): return @@ -63,7 +62,7 @@ class HomeAssistantAPI(CoreSysAttributes): _LOGGER.info("Updated Home Assistant API token") tokens = await resp.json() self.access_token = tokens["access_token"] - self._access_token_expires = datetime.utcnow() + timedelta( + self._access_token_expires = datetime.now(tz=UTC) + timedelta( seconds=tokens["expires_in"] ) @@ -78,7 +77,7 @@ class HomeAssistantAPI(CoreSysAttributes): timeout: int = 30, params: dict[str, str] | None = None, headers: dict[str, str] | None = None, - ) -> AsyncContextManager[aiohttp.ClientResponse]: + ) -> AbstractAsyncContextManager[aiohttp.ClientResponse]: """Async context manager to make a request with right auth.""" url = f"{self.sys_homeassistant.api_url}/{path}" headers = headers or {} diff --git a/supervisor/host/apparmor.py b/supervisor/host/apparmor.py index 2d17c8f67..51488a098 100644 --- a/supervisor/host/apparmor.py +++ b/supervisor/host/apparmor.py @@ -1,6 +1,7 @@ """AppArmor control for host.""" from __future__ import annotations +from contextlib import suppress import errno import logging from pathlib import Path @@ -62,10 +63,8 @@ class AppArmorControl(CoreSysAttributes): # Load profiles if self.available: for profile_name in self._profiles: - try: + with suppress(HostAppArmorError): await self._load_profile(profile_name) - except HostAppArmorError: - pass else: _LOGGER.warning("AppArmor is not enabled on host") diff --git a/supervisor/host/logs.py b/supervisor/host/logs.py index 7df3d58d3..27f7cab81 100644 --- a/supervisor/host/logs.py +++ b/supervisor/host/logs.py @@ -69,8 +69,7 @@ class LogsControl(CoreSysAttributes): ) async def get_boot_id(self, offset: int = 0) -> str: - """ - Get ID of a boot by offset. + """Get ID of a boot by offset. Current boot is offset = 0, negative numbers go that many in the past. Positive numbers count up from the oldest boot. diff --git a/supervisor/host/sound.py b/supervisor/host/sound.py index 4f64e952a..4b5530a57 100644 --- a/supervisor/host/sound.py +++ b/supervisor/host/sound.py @@ -155,11 +155,10 @@ class SoundControl(CoreSysAttributes): stream = pulse.source_output_info(index) else: stream = pulse.source_info(index) + elif application: + stream = pulse.sink_input_info(index) else: - if application: - stream = pulse.sink_input_info(index) - else: - stream = pulse.sink_info(index) + stream = pulse.sink_info(index) # Set volume pulse.volume_set_all_chans(stream, volume) @@ -190,11 +189,10 @@ class SoundControl(CoreSysAttributes): stream = pulse.source_output_info(index) else: stream = pulse.source_info(index) + elif application: + stream = pulse.sink_input_info(index) else: - if application: - stream = pulse.sink_input_info(index) - else: - stream = pulse.sink_info(index) + stream = pulse.sink_info(index) # Mute stream pulse.mute(stream, mute) diff --git a/supervisor/jobs/decorator.py b/supervisor/jobs/decorator.py index 67b219829..63eb5670b 100644 --- a/supervisor/jobs/decorator.py +++ b/supervisor/jobs/decorator.py @@ -1,12 +1,12 @@ """Job decorator.""" import asyncio from collections.abc import Callable +from contextlib import suppress from datetime import datetime, timedelta from functools import wraps import logging from typing import Any -from . import SupervisorJob from ..const import CoreState from ..coresys import CoreSys, CoreSysAttributes from ..exceptions import ( @@ -18,6 +18,7 @@ from ..exceptions import ( from ..host.const import HostFeature from ..resolution.const import MINIMUM_FREE_SPACE_THRESHOLD, ContextType, IssueType from ..utils.sentry import capture_exception +from . import SupervisorJob from .const import JobCondition, JobExecutionLimit from .job_group import JobGroup @@ -146,10 +147,8 @@ class Job(CoreSysAttributes): def _post_init(self, obj: JobGroup | CoreSysAttributes) -> JobGroup | None: """Runtime init.""" # Coresys - try: + with suppress(AttributeError): self.coresys = obj.coresys - except AttributeError: - pass if not self.coresys: raise RuntimeError(f"Job on {self.name} need to be an coresys object!") diff --git a/supervisor/jobs/job_group.py b/supervisor/jobs/job_group.py index 62dd0bbb0..50f034c7b 100644 --- a/supervisor/jobs/job_group.py +++ b/supervisor/jobs/job_group.py @@ -2,9 +2,9 @@ from asyncio import Lock -from . import SupervisorJob from ..coresys import CoreSys, CoreSysAttributes from ..exceptions import JobException, JobGroupExecutionLimitExceeded +from . import SupervisorJob class JobGroup(CoreSysAttributes): diff --git a/supervisor/misc/scheduler.py b/supervisor/misc/scheduler.py index 9e4ac5c61..8e34da2af 100644 --- a/supervisor/misc/scheduler.py +++ b/supervisor/misc/scheduler.py @@ -5,7 +5,6 @@ from datetime import date, datetime, time, timedelta import logging from uuid import UUID, uuid4 -import async_timeout import attr from ..const import CoreState @@ -113,7 +112,7 @@ class Scheduler(CoreSysAttributes): # Wait until all are shutdown try: - async with async_timeout.timeout(timeout): + async with asyncio.timeout(timeout): await asyncio.wait(running) except TimeoutError: _LOGGER.error("Timeout while waiting for jobs shutdown") diff --git a/supervisor/plugins/base.py b/supervisor/plugins/base.py index 259174713..b0e109657 100644 --- a/supervisor/plugins/base.py +++ b/supervisor/plugins/base.py @@ -102,7 +102,7 @@ class PluginBase(ABC, FileConfiguration, CoreSysAttributes): async def watchdog_container(self, event: DockerContainerStateEvent) -> None: """Process state changes in plugin container and restart if necessary.""" - if not (event.name == self.instance.name): + if event.name != self.instance.name: return if event.state in {ContainerState.FAILED, ContainerState.UNHEALTHY}: diff --git a/supervisor/resolution/checks/backups.py b/supervisor/resolution/checks/backups.py index 0641366ea..2c082600f 100644 --- a/supervisor/resolution/checks/backups.py +++ b/supervisor/resolution/checks/backups.py @@ -25,12 +25,15 @@ class CheckBackups(CheckBase): async def approve_check(self, reference: str | None = None) -> bool: """Approve check if it is affected by issue.""" - return 0 == len( - [ - backup - for backup in self.sys_backups.list_backups - if backup.sys_type == BackupType.FULL and backup.is_current - ] + return ( + len( + [ + backup + for backup in self.sys_backups.list_backups + if backup.sys_type == BackupType.FULL and backup.is_current + ] + ) + == 0 ) @property diff --git a/supervisor/resolution/checks/free_space.py b/supervisor/resolution/checks/free_space.py index f541cfd5d..94b3f7dfb 100644 --- a/supervisor/resolution/checks/free_space.py +++ b/supervisor/resolution/checks/free_space.py @@ -26,12 +26,15 @@ class CheckFreeSpace(CheckBase): return suggestions: list[SuggestionType] = [] - if MINIMUM_FULL_BACKUPS < len( - [ - backup - for backup in self.sys_backups.list_backups - if backup.sys_type == BackupType.FULL - ] + if ( + len( + [ + backup + for backup in self.sys_backups.list_backups + if backup.sys_type == BackupType.FULL + ] + ) + > MINIMUM_FULL_BACKUPS ): suggestions.append(SuggestionType.CLEAR_FULL_BACKUP) diff --git a/supervisor/resolution/evaluations/base.py b/supervisor/resolution/evaluations/base.py index 933263866..19d1d91d3 100644 --- a/supervisor/resolution/evaluations/base.py +++ b/supervisor/resolution/evaluations/base.py @@ -28,10 +28,9 @@ class EvaluateBase(ABC, CoreSysAttributes): self.on_failure, self.reason, ) - else: - if self.reason in self.sys_resolution.unsupported: - _LOGGER.info("Clearing %s as reason for unsupported", self.reason) - self.sys_resolution.dismiss_unsupported(self.reason) + elif self.reason in self.sys_resolution.unsupported: + _LOGGER.info("Clearing %s as reason for unsupported", self.reason) + self.sys_resolution.dismiss_unsupported(self.reason) @abstractmethod async def evaluate(self) -> bool: diff --git a/supervisor/resolution/evaluations/dns_server.py b/supervisor/resolution/evaluations/dns_server.py index efdb4ff90..02942de52 100644 --- a/supervisor/resolution/evaluations/dns_server.py +++ b/supervisor/resolution/evaluations/dns_server.py @@ -31,10 +31,14 @@ class EvaluateDNSServer(EvaluateBase): async def evaluate(self) -> None: """Run evaluation.""" - return not self.sys_plugins.dns.fallback and 0 < len( - [ - issue - for issue in self.sys_resolution.issues - if issue.context == ContextType.DNS_SERVER - ] + return ( + not self.sys_plugins.dns.fallback + and len( + [ + issue + for issue in self.sys_resolution.issues + if issue.context == ContextType.DNS_SERVER + ] + ) + > 0 ) diff --git a/supervisor/resolution/fixups/system_clear_full_backup.py b/supervisor/resolution/fixups/system_clear_full_backup.py index eaa12d469..faec085ec 100644 --- a/supervisor/resolution/fixups/system_clear_full_backup.py +++ b/supervisor/resolution/fixups/system_clear_full_backup.py @@ -23,7 +23,7 @@ class FixupSystemClearFullBackup(FixupBase): x for x in self.sys_backups.list_backups if x.sys_type == BackupType.FULL ] - if MINIMUM_FULL_BACKUPS >= len(full_backups): + if len(full_backups) <= MINIMUM_FULL_BACKUPS: return _LOGGER.info("Starting removal of old full backups") diff --git a/supervisor/resolution/notify.py b/supervisor/resolution/notify.py index 30113fc36..d282a43bb 100644 --- a/supervisor/resolution/notify.py +++ b/supervisor/resolution/notify.py @@ -1,5 +1,4 @@ -""" -Helper to notify Core about issues. +"""Helper to notify Core about issues. This helper creates persistant notification in the Core UI. In the future we want to remove this in favour of a "center" in the UI. diff --git a/supervisor/utils/codenotary.py b/supervisor/utils/codenotary.py index bebd6bc7c..f8138e8b2 100644 --- a/supervisor/utils/codenotary.py +++ b/supervisor/utils/codenotary.py @@ -9,11 +9,10 @@ from pathlib import Path import shlex from typing import Final -import async_timeout from dirhash import dirhash -from . import clean_env from ..exceptions import CodeNotaryBackendError, CodeNotaryError, CodeNotaryUntrusted +from . import clean_env _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -67,7 +66,7 @@ async def cas_validate( env=clean_env(), ) - async with async_timeout.timeout(15): + async with asyncio.timeout(15): data, error = await proc.communicate() except TimeoutError: raise CodeNotaryBackendError( diff --git a/supervisor/utils/common.py b/supervisor/utils/common.py index b3e8e88b6..e37b14b39 100644 --- a/supervisor/utils/common.py +++ b/supervisor/utils/common.py @@ -1,4 +1,5 @@ """Common utils.""" +from contextlib import suppress import logging from pathlib import Path from typing import Any @@ -101,7 +102,5 @@ class FileConfiguration: self.read_data() else: # write - try: + with suppress(ConfigurationFileError): write_json_or_yaml_file(self._file, self._data) - except ConfigurationFileError: - pass diff --git a/supervisor/utils/dbus.py b/supervisor/utils/dbus.py index 06fd762c6..3e56661fd 100644 --- a/supervisor/utils/dbus.py +++ b/supervisor/utils/dbus.py @@ -110,7 +110,7 @@ class DBus: ) return await getattr(proxy_interface, method)(*args) except DBusFastDBusError as err: - raise DBus.from_dbus_error(err) + raise DBus.from_dbus_error(err) from None except Exception as err: # pylint: disable=broad-except capture_exception(err) raise DBusFatalError(str(err)) from err @@ -134,7 +134,7 @@ class DBus: f"Can't parse introspect data: {err}", _LOGGER.error ) from err except DBusFastDBusError as err: - raise DBus.from_dbus_error(err) + raise DBus.from_dbus_error(err) from None except (EOFError, TimeoutError): _LOGGER.warning( "Busy system at %s - %s", self.bus_name, self.object_path diff --git a/supervisor/utils/yaml.py b/supervisor/utils/yaml.py index 544f89705..684fedc9d 100644 --- a/supervisor/utils/yaml.py +++ b/supervisor/utils/yaml.py @@ -8,7 +8,7 @@ from yaml import YAMLError, dump, load try: from yaml import CDumper as Dumper, CSafeLoader as SafeLoader except ImportError: - from yaml import SafeLoader, Dumper + from yaml import Dumper, SafeLoader from ..exceptions import YamlFileError diff --git a/tests/api/test_addons.py b/tests/api/test_addons.py index 06849a5a8..f391a5545 100644 --- a/tests/api/test_addons.py +++ b/tests/api/test_addons.py @@ -96,6 +96,7 @@ async def test_api_addon_start_healthcheck( assert install_addon_ssh.state == AddonState.STOPPED state_changes: list[AddonState] = [] + _container_events_task: asyncio.Task | None = None async def container_events(): nonlocal state_changes @@ -111,7 +112,8 @@ async def test_api_addon_start_healthcheck( ) async def container_events_task(*args, **kwargs): - asyncio.create_task(container_events()) + nonlocal _container_events_task + _container_events_task = asyncio.create_task(container_events()) with patch.object(DockerAddon, "run", new=container_events_task): resp = await api_client.post("/addons/local_ssh/start") @@ -137,6 +139,7 @@ async def test_api_addon_restart_healthcheck( assert install_addon_ssh.state == AddonState.STOPPED state_changes: list[AddonState] = [] + _container_events_task: asyncio.Task | None = None async def container_events(): nonlocal state_changes @@ -152,7 +155,8 @@ async def test_api_addon_restart_healthcheck( ) async def container_events_task(*args, **kwargs): - asyncio.create_task(container_events()) + nonlocal _container_events_task + _container_events_task = asyncio.create_task(container_events()) with patch.object(DockerAddon, "run", new=container_events_task): resp = await api_client.post("/addons/local_ssh/restart") @@ -180,6 +184,7 @@ async def test_api_addon_rebuild_healthcheck( assert install_addon_ssh.state == AddonState.STARTUP state_changes: list[AddonState] = [] + _container_events_task: asyncio.Task | None = None async def container_events(): nonlocal state_changes @@ -200,7 +205,8 @@ async def test_api_addon_rebuild_healthcheck( ) async def container_events_task(*args, **kwargs): - asyncio.create_task(container_events()) + nonlocal _container_events_task + _container_events_task = asyncio.create_task(container_events()) with patch.object( AddonBuild, "is_valid", new=PropertyMock(return_value=True) @@ -208,9 +214,7 @@ async def test_api_addon_rebuild_healthcheck( Addon, "need_build", new=PropertyMock(return_value=True) ), patch.object( CpuArch, "supported", new=PropertyMock(return_value=["amd64"]) - ), patch.object( - DockerAddon, "run", new=container_events_task - ): + ), patch.object(DockerAddon, "run", new=container_events_task): resp = await api_client.post("/addons/local_ssh/rebuild") assert state_changes == [AddonState.STOPPED, AddonState.STARTUP] diff --git a/tests/api/test_jobs.py b/tests/api/test_jobs.py index f90e54461..562405af4 100644 --- a/tests/api/test_jobs.py +++ b/tests/api/test_jobs.py @@ -93,8 +93,8 @@ async def test_jobs_tree_representation(api_client: TestClient, coresys: CoreSys await self.event.wait() test = TestClass(coresys) - asyncio.create_task(test.test_jobs_tree_outer()) - asyncio.create_task(test.test_jobs_tree_alt()) + outer_task = asyncio.create_task(test.test_jobs_tree_outer()) + alt_task = asyncio.create_task(test.test_jobs_tree_alt()) await asyncio.sleep(0) resp = await api_client.get("/jobs/info") @@ -150,6 +150,8 @@ async def test_jobs_tree_representation(api_client: TestClient, coresys: CoreSys "errors": [], }, ] + await outer_task + await alt_task async def test_job_manual_cleanup(api_client: TestClient, coresys: CoreSys): diff --git a/tests/api/test_network.py b/tests/api/test_network.py index 3c8f7a04a..0a6df77ba 100644 --- a/tests/api/test_network.py +++ b/tests/api/test_network.py @@ -128,7 +128,7 @@ async def test_api_network_interface_update( ) result = await resp.json() assert result["result"] == "ok" - assert network_manager_service.CheckConnectivity.calls == [tuple()] + assert network_manager_service.CheckConnectivity.calls == [()] assert len(connection_settings_service.Update.calls) == 1 await connection_settings_service.ping() @@ -221,7 +221,7 @@ async def test_api_network_reload( assert result["result"] == "ok" # Check that we forced NM to do an immediate connectivity check - assert network_manager_service.CheckConnectivity.calls == [tuple()] + assert network_manager_service.CheckConnectivity.calls == [()] async def test_api_network_vlan( diff --git a/tests/api/test_os.py b/tests/api/test_os.py index a4c56e32b..efec4647a 100644 --- a/tests/api/test_os.py +++ b/tests/api/test_os.py @@ -22,7 +22,7 @@ from tests.dbus_service_mocks.base import DBusServiceMock @pytest.fixture(name="boards_service") async def fixture_boards_service( - os_agent_services: dict[str, DBusServiceMock] + os_agent_services: dict[str, DBusServiceMock], ) -> BoardsService: """Return mock Boards service.""" yield os_agent_services["agent_boards"] diff --git a/tests/api/test_resolution.py b/tests/api/test_resolution.py index 172f0239a..b15023219 100644 --- a/tests/api/test_resolution.py +++ b/tests/api/test_resolution.py @@ -35,9 +35,9 @@ async def test_api_resolution_base(coresys: CoreSys, api_client): result = await resp.json() assert UnsupportedReason.OS in result["data"][ATTR_UNSUPPORTED] assert ( - SuggestionType.CLEAR_FULL_BACKUP == result["data"][ATTR_SUGGESTIONS][-1]["type"] + result["data"][ATTR_SUGGESTIONS][-1]["type"] == SuggestionType.CLEAR_FULL_BACKUP ) - assert IssueType.FREE_SPACE == result["data"][ATTR_ISSUES][-1]["type"] + assert result["data"][ATTR_ISSUES][-1]["type"] == IssueType.FREE_SPACE @pytest.mark.asyncio @@ -47,7 +47,7 @@ async def test_api_resolution_dismiss_suggestion(coresys: CoreSys, api_client): SuggestionType.CLEAR_FULL_BACKUP, ContextType.SYSTEM ) - assert SuggestionType.CLEAR_FULL_BACKUP == coresys.resolution.suggestions[-1].type + assert coresys.resolution.suggestions[-1].type == SuggestionType.CLEAR_FULL_BACKUP await api_client.delete(f"/resolution/suggestion/{clear_backup.uuid}") assert clear_backup not in coresys.resolution.suggestions @@ -87,7 +87,7 @@ async def test_api_resolution_dismiss_issue(coresys: CoreSys, api_client): IssueType.UPDATE_FAILED, ContextType.SYSTEM ) - assert IssueType.UPDATE_FAILED == coresys.resolution.issues[-1].type + assert coresys.resolution.issues[-1].type == IssueType.UPDATE_FAILED await api_client.delete(f"/resolution/issue/{updated_failed.uuid}") assert updated_failed not in coresys.resolution.issues @@ -99,7 +99,7 @@ async def test_api_resolution_unhealthy(coresys: CoreSys, api_client): resp = await api_client.get("/resolution/info") result = await resp.json() - assert UnhealthyReason.DOCKER == result["data"][ATTR_UNHEALTHY][-1] + assert result["data"][ATTR_UNHEALTHY][-1] == UnhealthyReason.DOCKER @pytest.mark.asyncio diff --git a/tests/api/test_store.py b/tests/api/test_store.py index 878e6d418..c0c4e4d7f 100644 --- a/tests/api/test_store.py +++ b/tests/api/test_store.py @@ -45,10 +45,8 @@ async def test_api_store( @pytest.mark.asyncio async def test_api_store_addons(api_client: TestClient, store_addon: AddonStore): """Test /store/addons REST API.""" - print("test") resp = await api_client.get("/store/addons") result = await resp.json() - print(result) assert result["data"]["addons"][-1]["slug"] == store_addon.slug @@ -139,6 +137,7 @@ async def test_api_store_update_healthcheck( assert install_addon_ssh.need_update is True state_changes: list[AddonState] = [] + _container_events_task: asyncio.Task | None = None async def container_events(): nonlocal state_changes @@ -174,7 +173,8 @@ async def test_api_store_update_healthcheck( ) async def container_events_task(*args, **kwargs): - asyncio.create_task(container_events()) + nonlocal _container_events_task + _container_events_task = asyncio.create_task(container_events()) with patch.object(DockerAddon, "run", new=container_events_task), patch.object( DockerInterface, "install" @@ -186,3 +186,5 @@ async def test_api_store_update_healthcheck( assert state_changes == [AddonState.STOPPED, AddonState.STARTUP] assert install_addon_ssh.state == AddonState.STARTED assert resp.status == 200 + + await _container_events_task diff --git a/tests/api/test_supervisor.py b/tests/api/test_supervisor.py index c28b49b62..b4fb8f6d2 100644 --- a/tests/api/test_supervisor.py +++ b/tests/api/test_supervisor.py @@ -69,9 +69,7 @@ async def test_api_supervisor_options_repositories_skipped_on_error( "supervisor.store.repository.Repository.load", side_effect=git_error ), patch( "supervisor.store.repository.Repository.validate", return_value=False - ), patch( - "supervisor.store.repository.Repository.remove" - ): + ), patch("supervisor.store.repository.Repository.remove"): response = await api_client.post( "/supervisor/options", json={"addons_repositories": [REPO_URL]} ) diff --git a/tests/backups/test_manager.py b/tests/backups/test_manager.py index 5ec632c7c..af92d3ec3 100644 --- a/tests/backups/test_manager.py +++ b/tests/backups/test_manager.py @@ -806,6 +806,7 @@ async def test_backup_with_healthcheck( assert install_addon_ssh.state == AddonState.STARTUP state_changes: list[AddonState] = [] + _container_events_task: asyncio.Task | None = None async def container_events(): nonlocal state_changes @@ -841,7 +842,8 @@ async def test_backup_with_healthcheck( ) async def container_events_task(*args, **kwargs): - asyncio.create_task(container_events()) + nonlocal _container_events_task + _container_events_task = asyncio.create_task(container_events()) with patch.object(DockerAddon, "run", new=container_events_task), patch.object( AddonModel, "backup_mode", new=PropertyMock(return_value=AddonBackupMode.COLD) @@ -855,6 +857,8 @@ async def test_backup_with_healthcheck( assert install_addon_ssh.state == AddonState.STARTED assert coresys.core.state == CoreState.RUNNING + await _container_events_task + async def test_restore_with_healthcheck( coresys: CoreSys, @@ -877,6 +881,7 @@ async def test_restore_with_healthcheck( homeassistant=False, addons=["local_ssh"] ) state_changes: list[AddonState] = [] + _container_events_task: asyncio.Task | None = None async def container_events(): nonlocal state_changes @@ -911,7 +916,8 @@ async def test_restore_with_healthcheck( ) async def container_events_task(*args, **kwargs): - asyncio.create_task(container_events()) + nonlocal _container_events_task + _container_events_task = asyncio.create_task(container_events()) with patch.object(DockerAddon, "run", new=container_events_task), patch.object( DockerAddon, "is_running", return_value=False @@ -924,6 +930,8 @@ async def test_restore_with_healthcheck( assert install_addon_ssh.state == AddonState.STARTED assert coresys.core.state == CoreState.RUNNING + await _container_events_task + def _make_backup_message_for_assert( *, @@ -1077,9 +1085,7 @@ async def test_restore_progress( HomeAssistant, "restore" ), patch.object(HomeAssistantCore, "update"), patch.object( AddonModel, "_validate_availability" - ), patch.object( - AddonModel, "with_ingress", new=PropertyMock(return_value=False) - ): + ), patch.object(AddonModel, "with_ingress", new=PropertyMock(return_value=False)): await coresys.backups.do_restore_full(full_backup) await asyncio.sleep(0) @@ -1382,9 +1388,7 @@ async def test_restore_only_reloads_ingress_on_change( HomeAssistantCore, "is_running", new=mock_is_running ), patch.object(AddonModel, "_validate_availability"), patch.object( DockerAddon, "attach" - ), patch.object( - HomeAssistantAPI, "make_request" - ) as make_request: + ), patch.object(HomeAssistantAPI, "make_request") as make_request: make_request.return_value.__aenter__.return_value.status = 200 # Has ingress before and after - not called diff --git a/tests/conftest.py b/tests/conftest.py index 1092fe89f..01e633416 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -98,9 +98,7 @@ async def docker() -> DockerAPI: ), patch( "supervisor.docker.manager.DockerConfig", return_value=MagicMock(), - ), patch( - "supervisor.docker.manager.DockerAPI.unload" - ): + ), patch("supervisor.docker.manager.DockerAPI.unload"): docker_obj = DockerAPI(MagicMock()) with patch("supervisor.docker.monitor.DockerMonitor.load"): await docker_obj.load() @@ -184,7 +182,7 @@ async def network_manager( @pytest.fixture async def network_manager_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> NetworkManagerService: """Return Network Manager service mock.""" yield network_manager_services["network_manager"] @@ -192,7 +190,7 @@ async def network_manager_service( @pytest.fixture(name="connection_settings_service") async def fixture_connection_settings_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> ConnectionSettingsService: """Return mock connection settings service.""" yield network_manager_services["network_connection_settings"] @@ -276,19 +274,24 @@ async def fixture_all_dbus_services( ) -> dict[str, DBusServiceMock | dict[str, DBusServiceMock]]: """Mock all dbus services supervisor uses.""" yield ( - await mock_dbus_services( - { - "hostname": None, - "logind": None, - "rauc": None, - "resolved": None, - "systemd": None, - "systemd_unit": None, - "timedate": None, - }, - dbus_session_bus, + ( + await mock_dbus_services( + { + "hostname": None, + "logind": None, + "rauc": None, + "resolved": None, + "systemd": None, + "systemd_unit": None, + "timedate": None, + }, + dbus_session_bus, + ) ) - ) | network_manager_services | udisks2_services | os_agent_services + | network_manager_services + | udisks2_services + | os_agent_services + ) @pytest.fixture @@ -599,7 +602,7 @@ async def backups( ATTR_SLUG: slug, ATTR_DATE: utcnow().isoformat(), ATTR_TYPE: BackupType.PARTIAL - if "1" == slug[-1] or "5" == slug[-1] + if slug[-1] == "1" or slug[-1] == "5" else BackupType.FULL, } coresys.backups._backups[backup.slug] = backup @@ -618,9 +621,7 @@ async def journald_logs(coresys: CoreSys) -> MagicMock: LogsControl, "get_identifiers", return_value=["hassio_supervisor", "hassos-config", "kernel"], - ), patch.object( - LogsControl, "journald_logs", new=MagicMock() - ) as logs: + ), patch.object(LogsControl, "journald_logs", new=MagicMock()) as logs: await coresys.host.logs.load() yield logs diff --git a/tests/dbus/agent/boards/test_green.py b/tests/dbus/agent/boards/test_green.py index 7e670a858..10cee120f 100644 --- a/tests/dbus/agent/boards/test_green.py +++ b/tests/dbus/agent/boards/test_green.py @@ -1,6 +1,5 @@ """Test Green board.""" -# pylint: disable=import-error -import asyncio + from unittest.mock import patch from dbus_fast.aio.message_bus import MessageBus @@ -22,12 +21,7 @@ async def fixture_green_service(dbus_session_bus: MessageBus) -> GreenService: async def test_dbus_green(green_service: GreenService, dbus_session_bus: MessageBus): """Test Green board load.""" - with patch("supervisor.utils.common.Path.is_file", return_value=True), patch( - "supervisor.utils.common.read_json_file", - return_value={"activity_led": False, "user_led": False}, - ): - green = Green() - + green = Green() await green.connect(dbus_session_bus) assert green.name == "Green" @@ -35,8 +29,12 @@ async def test_dbus_green(green_service: GreenService, dbus_session_bus: Message assert green.power_led is True assert green.user_led is True - await asyncio.sleep(0) - await green_service.ping() + with patch("supervisor.utils.common.Path.is_file", return_value=True), patch( + "supervisor.utils.common.read_json_file", + return_value={"activity_led": False, "user_led": False}, + ): + green = Green() + await green.connect(dbus_session_bus) assert green.activity_led is False assert green.user_led is False @@ -49,8 +47,7 @@ async def test_dbus_green_set_activity_led( green = Green() await green.connect(dbus_session_bus) - green.activity_led = False - await asyncio.sleep(0) # Set property via dbus is separate async task + await green.set_activity_led(False) await green_service.ping() assert green.activity_led is False @@ -62,8 +59,7 @@ async def test_dbus_green_set_power_led( green = Green() await green.connect(dbus_session_bus) - green.power_led = False - await asyncio.sleep(0) # Set property via dbus is separate async task + await green.set_power_led(False) await green_service.ping() assert green.power_led is False @@ -75,7 +71,6 @@ async def test_dbus_green_set_user_led( green = Green() await green.connect(dbus_session_bus) - green.user_led = False - await asyncio.sleep(0) # Set property via dbus is separate async task + await green.set_user_led(False) await green_service.ping() assert green.user_led is False diff --git a/tests/dbus/agent/boards/test_yellow.py b/tests/dbus/agent/boards/test_yellow.py index 3195bf937..5cd052916 100644 --- a/tests/dbus/agent/boards/test_yellow.py +++ b/tests/dbus/agent/boards/test_yellow.py @@ -1,6 +1,5 @@ """Test Yellow board.""" -# pylint: disable=import-error -import asyncio + from unittest.mock import patch from dbus_fast.aio.message_bus import MessageBus @@ -22,11 +21,7 @@ async def fixture_yellow_service(dbus_session_bus: MessageBus) -> YellowService: async def test_dbus_yellow(yellow_service: YellowService, dbus_session_bus: MessageBus): """Test Yellow board load.""" - with patch("supervisor.utils.common.Path.is_file", return_value=True), patch( - "supervisor.utils.common.read_json_file", - return_value={"disk_led": False, "heartbeat_led": False}, - ): - yellow = Yellow() + yellow = Yellow() await yellow.connect(dbus_session_bus) assert yellow.name == "Yellow" @@ -34,8 +29,12 @@ async def test_dbus_yellow(yellow_service: YellowService, dbus_session_bus: Mess assert yellow.heartbeat_led is True assert yellow.power_led is True - await asyncio.sleep(0) - await yellow_service.ping() + with patch("supervisor.utils.common.Path.is_file", return_value=True), patch( + "supervisor.utils.common.read_json_file", + return_value={"disk_led": False, "heartbeat_led": False}, + ): + yellow = Yellow() + await yellow.connect(dbus_session_bus) assert yellow.disk_led is False assert yellow.heartbeat_led is False @@ -48,8 +47,7 @@ async def test_dbus_yellow_set_disk_led( yellow = Yellow() await yellow.connect(dbus_session_bus) - yellow.disk_led = False - await asyncio.sleep(0) # Set property via dbus is separate async task + await yellow.set_disk_led(False) await yellow_service.ping() assert yellow.disk_led is False @@ -61,8 +59,7 @@ async def test_dbus_yellow_set_heartbeat_led( yellow = Yellow() await yellow.connect(dbus_session_bus) - yellow.heartbeat_led = False - await asyncio.sleep(0) # Set property via dbus is separate async task + await yellow.set_heartbeat_led(False) await yellow_service.ping() assert yellow.heartbeat_led is False @@ -74,7 +71,6 @@ async def test_dbus_yellow_set_power_led( yellow = Yellow() await yellow.connect(dbus_session_bus) - yellow.power_led = False - await asyncio.sleep(0) # Set property via dbus is separate async task + await yellow.set_power_led(False) await yellow_service.ping() assert yellow.power_led is False diff --git a/tests/dbus/agent/test_agent.py b/tests/dbus/agent/test_agent.py index bc05214ca..37ae7f412 100644 --- a/tests/dbus/agent/test_agent.py +++ b/tests/dbus/agent/test_agent.py @@ -12,7 +12,7 @@ from tests.dbus_service_mocks.os_agent import OSAgent as OSAgentService @pytest.fixture(name="os_agent_service") async def fixture_os_agent_service( - os_agent_services: dict[str, DBusServiceMock] + os_agent_services: dict[str, DBusServiceMock], ) -> OSAgentService: """Mock OS Agent dbus service.""" yield os_agent_services["os_agent"] diff --git a/tests/dbus/agent/test_apparmor.py b/tests/dbus/agent/test_apparmor.py index a165f6103..857eb62ef 100644 --- a/tests/dbus/agent/test_apparmor.py +++ b/tests/dbus/agent/test_apparmor.py @@ -14,7 +14,7 @@ from tests.dbus_service_mocks.base import DBusServiceMock @pytest.fixture(name="apparmor_service", autouse=True) async def fixture_apparmor_service( - os_agent_services: dict[str, DBusServiceMock] + os_agent_services: dict[str, DBusServiceMock], ) -> AppArmorService: """Mock AppArmor dbus service.""" yield os_agent_services["agent_apparmor"] diff --git a/tests/dbus/agent/test_cgroup.py b/tests/dbus/agent/test_cgroup.py index a4a6b57f7..e67be97cc 100644 --- a/tests/dbus/agent/test_cgroup.py +++ b/tests/dbus/agent/test_cgroup.py @@ -12,7 +12,7 @@ from tests.dbus_service_mocks.base import DBusServiceMock @pytest.fixture(name="cgroup_service", autouse=True) async def fixture_cgroup_service( - os_agent_services: dict[str, DBusServiceMock] + os_agent_services: dict[str, DBusServiceMock], ) -> CGroupService: """Mock CGroup dbus service.""" yield os_agent_services["agent_cgroup"] diff --git a/tests/dbus/agent/test_datadisk.py b/tests/dbus/agent/test_datadisk.py index 22a5937ed..a2be502af 100644 --- a/tests/dbus/agent/test_datadisk.py +++ b/tests/dbus/agent/test_datadisk.py @@ -1,5 +1,5 @@ """Test Datadisk/Agent dbus interface.""" -# pylint: disable=import-error + from pathlib import Path from dbus_fast.aio.message_bus import MessageBus @@ -14,7 +14,7 @@ from tests.dbus_service_mocks.base import DBusServiceMock @pytest.fixture(name="datadisk_service", autouse=True) async def fixture_datadisk_service( - os_agent_services: dict[str, DBusServiceMock] + os_agent_services: dict[str, DBusServiceMock], ) -> DataDiskService: """Mock DataDisk dbus service.""" yield os_agent_services["agent_datadisk"] @@ -71,7 +71,7 @@ async def test_dbus_osagent_datadisk_reload_device( await os_agent.connect(dbus_session_bus) assert await os_agent.datadisk.reload_device() is None - assert datadisk_service.ReloadDevice.calls == [tuple()] + assert datadisk_service.ReloadDevice.calls == [()] async def test_dbus_osagent_datadisk_mark_data_move( @@ -87,4 +87,4 @@ async def test_dbus_osagent_datadisk_mark_data_move( await os_agent.connect(dbus_session_bus) assert await os_agent.datadisk.mark_data_move() is None - assert datadisk_service.MarkDataMove.calls == [tuple()] + assert datadisk_service.MarkDataMove.calls == [()] diff --git a/tests/dbus/agent/test_system.py b/tests/dbus/agent/test_system.py index a7bc7d9e5..b80458987 100644 --- a/tests/dbus/agent/test_system.py +++ b/tests/dbus/agent/test_system.py @@ -1,5 +1,5 @@ """Test System/Agent dbus interface.""" -# pylint: disable=import-error + from dbus_fast.aio.message_bus import MessageBus import pytest @@ -12,7 +12,7 @@ from tests.dbus_service_mocks.base import DBusServiceMock @pytest.fixture(name="system_service", autouse=True) async def fixture_system_service( - os_agent_services: dict[str, DBusServiceMock] + os_agent_services: dict[str, DBusServiceMock], ) -> SystemService: """Mock System dbus service.""" yield os_agent_services["agent_system"] @@ -31,4 +31,4 @@ async def test_dbus_osagent_system_wipe( await os_agent.connect(dbus_session_bus) assert await os_agent.system.schedule_wipe_device() is None - assert system_service.ScheduleWipeDevice.calls == [tuple()] + assert system_service.ScheduleWipeDevice.calls == [()] diff --git a/tests/dbus/network/setting/test_init.py b/tests/dbus/network/setting/test_init.py index 6018fa52f..0947c3551 100644 --- a/tests/dbus/network/setting/test_init.py +++ b/tests/dbus/network/setting/test_init.py @@ -2,8 +2,8 @@ from unittest.mock import MagicMock +from dbus_fast import Variant from dbus_fast.aio.message_bus import MessageBus -from dbus_fast.signature import Variant import pytest from supervisor.dbus.network.interface import NetworkInterface @@ -20,7 +20,7 @@ from tests.dbus_service_mocks.network_connection_settings import ( @pytest.fixture(name="connection_settings_service", autouse=True) async def fixture_connection_settings_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> ConnectionSettingsService: """Mock Connection Settings service.""" yield network_manager_services["network_connection_settings"] @@ -125,9 +125,9 @@ async def test_watching_updated_signal( settings = NetworkSetting("/org/freedesktop/NetworkManager/Settings/1") await settings.connect(dbus_session_bus) - connection_settings_service.GetSettings.calls == [tuple()] + assert connection_settings_service.GetSettings.calls == [()] connection_settings_service.Updated() await connection_settings_service.ping() await connection_settings_service.ping() - assert connection_settings_service.GetSettings.calls == [tuple(), tuple()] + assert connection_settings_service.GetSettings.calls == [(), ()] diff --git a/tests/dbus/network/test_connection.py b/tests/dbus/network/test_connection.py index d5af1327c..36e0370aa 100644 --- a/tests/dbus/network/test_connection.py +++ b/tests/dbus/network/test_connection.py @@ -16,7 +16,7 @@ from tests.dbus_service_mocks.network_active_connection import ( @pytest.fixture(name="active_connection_service", autouse=True) async def fixture_active_connection_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> ActiveConnectionService: """Mock Active Connection service.""" yield network_manager_services["network_active_connection"] diff --git a/tests/dbus/network/test_interface.py b/tests/dbus/network/test_interface.py index 57a876a97..93074f95d 100644 --- a/tests/dbus/network/test_interface.py +++ b/tests/dbus/network/test_interface.py @@ -17,7 +17,7 @@ from tests.dbus_service_mocks.network_device import Device as DeviceService @pytest.fixture(name="device_eth0_service") async def fixture_device_eth0_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> DeviceService: """Mock Device eth0 service.""" yield network_manager_services["network_device"][ @@ -27,7 +27,7 @@ async def fixture_device_eth0_service( @pytest.fixture(name="device_wlan0_service") async def fixture_device_wlan0_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> DeviceService: """Mock Device wlan0 service.""" yield network_manager_services["network_device"][ diff --git a/tests/dbus/network/test_network_manager.py b/tests/dbus/network/test_network_manager.py index a71239311..0ef1a30ac 100644 --- a/tests/dbus/network/test_network_manager.py +++ b/tests/dbus/network/test_network_manager.py @@ -60,6 +60,7 @@ async def test_network_manager_version( network_manager_service: NetworkManagerService, network_manager: NetworkManager ): """Test if version validate work.""" + # pylint: disable=protected-access await network_manager._validate_version() assert network_manager.version == "1.22.10" @@ -67,6 +68,7 @@ async def test_network_manager_version( with pytest.raises(HostNotSupportedError): await network_manager._validate_version() assert network_manager.version == "1.13.9" + # pylint: enable=protected-access async def test_check_connectivity( @@ -79,7 +81,7 @@ async def test_check_connectivity( assert network_manager_service.CheckConnectivity.calls == [] assert await network_manager.check_connectivity(force=True) == 4 - assert network_manager_service.CheckConnectivity.calls == [tuple()] + assert network_manager_service.CheckConnectivity.calls == [()] async def test_activate_connection( diff --git a/tests/dbus/network/test_settings.py b/tests/dbus/network/test_settings.py index 7df8c5e13..51b7fae55 100644 --- a/tests/dbus/network/test_settings.py +++ b/tests/dbus/network/test_settings.py @@ -54,7 +54,7 @@ async def test_reload_connections( await settings.connect(dbus_session_bus) assert await settings.reload_connections() is True - assert settings_service.ReloadConnections.calls == [tuple()] + assert settings_service.ReloadConnections.calls == [()] async def test_dbus_network_settings_connect_error( diff --git a/tests/dbus/network/test_wireless.py b/tests/dbus/network/test_wireless.py index 5a97ad7cd..3fe1ddd21 100644 --- a/tests/dbus/network/test_wireless.py +++ b/tests/dbus/network/test_wireless.py @@ -15,7 +15,7 @@ from tests.dbus_service_mocks.network_device_wireless import ( @pytest.fixture(name="device_wireless_service", autouse=True) async def fixture_device_wireless_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> DeviceWirelessService: """Mock Device Wireless service.""" yield network_manager_services["network_device_wireless"] diff --git a/tests/dbus/test_systemd.py b/tests/dbus/test_systemd.py index 3a7b1bac2..607c94e86 100644 --- a/tests/dbus/test_systemd.py +++ b/tests/dbus/test_systemd.py @@ -42,7 +42,7 @@ async def test_reboot(systemd_service: SystemdService, dbus_session_bus: Message await systemd.connect(dbus_session_bus) assert await systemd.reboot() is None - assert systemd_service.Reboot.calls == [tuple()] + assert systemd_service.Reboot.calls == [()] async def test_power_off(systemd_service: SystemdService, dbus_session_bus: MessageBus): @@ -56,7 +56,7 @@ async def test_power_off(systemd_service: SystemdService, dbus_session_bus: Mess await systemd.connect(dbus_session_bus) assert await systemd.power_off() is None - assert systemd_service.PowerOff.calls == [tuple()] + assert systemd_service.PowerOff.calls == [()] async def test_start_unit( diff --git a/tests/dbus/test_timedate.py b/tests/dbus/test_timedate.py index 75957f76d..819953b70 100644 --- a/tests/dbus/test_timedate.py +++ b/tests/dbus/test_timedate.py @@ -1,6 +1,6 @@ """Test TimeDate dbus interface.""" # pylint: disable=import-error -from datetime import datetime, timezone +from datetime import UTC, datetime from dbus_fast.aio.message_bus import MessageBus import pytest @@ -29,9 +29,7 @@ async def test_timedate_info( await timedate.connect(dbus_session_bus) - assert timedate.dt_utc == datetime( - 2021, 5, 19, 8, 36, 54, 405718, tzinfo=timezone.utc - ) + assert timedate.dt_utc == datetime(2021, 5, 19, 8, 36, 54, 405718, tzinfo=UTC) assert timedate.ntp is True assert timedate.dt_utc.isoformat() == "2021-05-19T08:36:54.405718+00:00" @@ -53,7 +51,7 @@ async def test_dbus_settime( timedate_service.SetTime.calls.clear() timedate = TimeDate() - test_dt = datetime(2021, 5, 19, 8, 36, 54, 405718, tzinfo=timezone.utc) + test_dt = datetime(2021, 5, 19, 8, 36, 54, 405718, tzinfo=UTC) with pytest.raises(DBusNotConnectedError): await timedate.set_time(test_dt) diff --git a/tests/dbus/udisks2/test_block.py b/tests/dbus/udisks2/test_block.py index 96e963a5a..23a2621f9 100644 --- a/tests/dbus/udisks2/test_block.py +++ b/tests/dbus/udisks2/test_block.py @@ -22,7 +22,7 @@ from tests.dbus_service_mocks.udisks2_block import Block as BlockService @pytest.fixture(name="block_sda_service") async def fixture_block_sda_service( - udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> BlockService: """Mock sda Block service.""" yield udisks2_services["udisks2_block"][ @@ -32,7 +32,7 @@ async def fixture_block_sda_service( @pytest.fixture(name="block_sda1_service") async def fixture_block_sda1_service( - udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> BlockService: """Mock sda1 Block service.""" yield udisks2_services["udisks2_block"][ diff --git a/tests/dbus/udisks2/test_drive.py b/tests/dbus/udisks2/test_drive.py index b61781d16..9ca0c78b9 100644 --- a/tests/dbus/udisks2/test_drive.py +++ b/tests/dbus/udisks2/test_drive.py @@ -1,6 +1,6 @@ """Test UDisks2 Drive.""" -from datetime import datetime, timezone +from datetime import UTC, datetime from dbus_fast import Variant from dbus_fast.aio.message_bus import MessageBus @@ -59,7 +59,7 @@ async def test_drive_info( assert ssk.vendor == "SSK" assert ssk.model == "SSK Storage" assert ssk.size == 250059350016 - assert ssk.time_detected == datetime(2023, 2, 8, 23, 1, 44, 240492, timezone.utc) + assert ssk.time_detected == datetime(2023, 2, 8, 23, 1, 44, 240492, UTC) assert ssk.ejectable is False drive_ssk_storage_service.emit_properties_changed({"Ejectable": True}) diff --git a/tests/dbus/udisks2/test_manager.py b/tests/dbus/udisks2/test_manager.py index 46f6dbbe3..47a3eb9a4 100644 --- a/tests/dbus/udisks2/test_manager.py +++ b/tests/dbus/udisks2/test_manager.py @@ -21,7 +21,7 @@ from tests.dbus_service_mocks.udisks2_manager import ( @pytest.fixture(name="udisks2_manager_service") async def fixture_udisks2_manager_service( - udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> UDisks2ManagerService: """Mock UDisks2 Manager service.""" yield udisks2_services["udisks2_manager"] @@ -76,7 +76,9 @@ async def test_udisks2_manager_info( udisks2_manager_service.emit_properties_changed({}, ["SupportedFilesystems"]) await udisks2_manager_service.ping() await udisks2_manager_service.ping() - await udisks2_manager_service.ping() # Three pings: signal, get all properties and get block devices + await ( + udisks2_manager_service.ping() + ) # Three pings: signal, get all properties and get block devices assert udisks2.supported_filesystems == [ "ext4", "vfat", diff --git a/tests/dbus_service_mocks/agent_apparmor.py b/tests/dbus_service_mocks/agent_apparmor.py index 650627cb2..101669258 100644 --- a/tests/dbus_service_mocks/agent_apparmor.py +++ b/tests/dbus_service_mocks/agent_apparmor.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return AppArmor() -# pylint: disable=invalid-name - - class AppArmor(DBusServiceMock): """AppArmor mock. diff --git a/tests/dbus_service_mocks/agent_boards.py b/tests/dbus_service_mocks/agent_boards.py index 8b30a9db5..47eae25ca 100644 --- a/tests/dbus_service_mocks/agent_boards.py +++ b/tests/dbus_service_mocks/agent_boards.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Boards() -# pylint: disable=invalid-name - - class Boards(DBusServiceMock): """Boards mock. diff --git a/tests/dbus_service_mocks/agent_boards_green.py b/tests/dbus_service_mocks/agent_boards_green.py index 6bb55ad28..a18da3dc7 100644 --- a/tests/dbus_service_mocks/agent_boards_green.py +++ b/tests/dbus_service_mocks/agent_boards_green.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Green() -# pylint: disable=invalid-name - - class Green(DBusServiceMock): """Green mock. diff --git a/tests/dbus_service_mocks/agent_boards_supervised.py b/tests/dbus_service_mocks/agent_boards_supervised.py index bc698a8ca..a2df158a4 100644 --- a/tests/dbus_service_mocks/agent_boards_supervised.py +++ b/tests/dbus_service_mocks/agent_boards_supervised.py @@ -10,9 +10,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Supervised() -# pylint: disable=invalid-name - - class Supervised(DBusServiceMock): """Supervised mock. diff --git a/tests/dbus_service_mocks/agent_boards_yellow.py b/tests/dbus_service_mocks/agent_boards_yellow.py index 0274ddbfa..bb123ec1c 100644 --- a/tests/dbus_service_mocks/agent_boards_yellow.py +++ b/tests/dbus_service_mocks/agent_boards_yellow.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Yellow() -# pylint: disable=invalid-name - - class Yellow(DBusServiceMock): """Yellow mock. diff --git a/tests/dbus_service_mocks/agent_cgroup.py b/tests/dbus_service_mocks/agent_cgroup.py index c8e86e58f..35610a3ed 100644 --- a/tests/dbus_service_mocks/agent_cgroup.py +++ b/tests/dbus_service_mocks/agent_cgroup.py @@ -10,9 +10,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return CGroup() -# pylint: disable=invalid-name - - class CGroup(DBusServiceMock): """CGroup mock. diff --git a/tests/dbus_service_mocks/agent_datadisk.py b/tests/dbus_service_mocks/agent_datadisk.py index b49b5c34c..2830e8fd2 100644 --- a/tests/dbus_service_mocks/agent_datadisk.py +++ b/tests/dbus_service_mocks/agent_datadisk.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return DataDisk() -# pylint: disable=invalid-name - - class DataDisk(DBusServiceMock): """DataDisk mock. diff --git a/tests/dbus_service_mocks/agent_system.py b/tests/dbus_service_mocks/agent_system.py index 7dec60ce5..b7ea50ce7 100644 --- a/tests/dbus_service_mocks/agent_system.py +++ b/tests/dbus_service_mocks/agent_system.py @@ -10,9 +10,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return System() -# pylint: disable=invalid-name - - class System(DBusServiceMock): """System mock. diff --git a/tests/dbus_service_mocks/hostname.py b/tests/dbus_service_mocks/hostname.py index e86882e63..d8f98499b 100644 --- a/tests/dbus_service_mocks/hostname.py +++ b/tests/dbus_service_mocks/hostname.py @@ -14,9 +14,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Hostname() -# pylint: disable=invalid-name - - class Hostname(DBusServiceMock): """Hostname mock. diff --git a/tests/dbus_service_mocks/logind.py b/tests/dbus_service_mocks/logind.py index 99a0e1a39..04d7ce77c 100644 --- a/tests/dbus_service_mocks/logind.py +++ b/tests/dbus_service_mocks/logind.py @@ -10,9 +10,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Logind() -# pylint: disable=invalid-name,missing-function-docstring - - class Logind(DBusServiceMock): """Logind mock. diff --git a/tests/dbus_service_mocks/network_access_point.py b/tests/dbus_service_mocks/network_access_point.py index 954dd66c1..1339629df 100644 --- a/tests/dbus_service_mocks/network_access_point.py +++ b/tests/dbus_service_mocks/network_access_point.py @@ -16,9 +16,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return AccessPoint(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - @dataclass(slots=True) class AccessPointFixture: """Access Point fixture.""" diff --git a/tests/dbus_service_mocks/network_active_connection.py b/tests/dbus_service_mocks/network_active_connection.py index c6e3bf970..c7263968d 100644 --- a/tests/dbus_service_mocks/network_active_connection.py +++ b/tests/dbus_service_mocks/network_active_connection.py @@ -15,9 +15,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return ActiveConnection(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - @dataclass(slots=True) class ActiveConnectionFixture: """Active Connection fixture.""" diff --git a/tests/dbus_service_mocks/network_connection_settings.py b/tests/dbus_service_mocks/network_connection_settings.py index 8e00929ed..6c480846b 100644 --- a/tests/dbus_service_mocks/network_connection_settings.py +++ b/tests/dbus_service_mocks/network_connection_settings.py @@ -87,9 +87,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return ConnectionSettings(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - class ConnectionSettings(DBusServiceMock): """Connection Settings mock. diff --git a/tests/dbus_service_mocks/network_device.py b/tests/dbus_service_mocks/network_device.py index 454e2a6dc..61cd2b1f1 100644 --- a/tests/dbus_service_mocks/network_device.py +++ b/tests/dbus_service_mocks/network_device.py @@ -17,9 +17,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Device(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - @dataclass(slots=True) class DeviceFixture: """Device fixture.""" diff --git a/tests/dbus_service_mocks/network_device_wireless.py b/tests/dbus_service_mocks/network_device_wireless.py index 184631d26..e8fe13b6f 100644 --- a/tests/dbus_service_mocks/network_device_wireless.py +++ b/tests/dbus_service_mocks/network_device_wireless.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return DeviceWireless() -# pylint: disable=invalid-name - - class DeviceWireless(DBusServiceMock): """Device Wireless mock. diff --git a/tests/dbus_service_mocks/network_dns_manager.py b/tests/dbus_service_mocks/network_dns_manager.py index d639d5aeb..9028f3955 100644 --- a/tests/dbus_service_mocks/network_dns_manager.py +++ b/tests/dbus_service_mocks/network_dns_manager.py @@ -13,9 +13,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return DnsManager() -# pylint: disable=invalid-name - - class DnsManager(DBusServiceMock): """DNS Manager mock. diff --git a/tests/dbus_service_mocks/network_ip4config.py b/tests/dbus_service_mocks/network_ip4config.py index 79350dd4a..54faa2610 100644 --- a/tests/dbus_service_mocks/network_ip4config.py +++ b/tests/dbus_service_mocks/network_ip4config.py @@ -13,9 +13,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return IP4Config() -# pylint: disable=invalid-name - - class IP4Config(DBusServiceMock): """IP4Config mock. diff --git a/tests/dbus_service_mocks/network_ip6config.py b/tests/dbus_service_mocks/network_ip6config.py index dc2a0ecc0..72df2b4dd 100644 --- a/tests/dbus_service_mocks/network_ip6config.py +++ b/tests/dbus_service_mocks/network_ip6config.py @@ -13,9 +13,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return IP6Config() -# pylint: disable=invalid-name - - class IP6Config(DBusServiceMock): """IP6Config mock. diff --git a/tests/dbus_service_mocks/network_manager.py b/tests/dbus_service_mocks/network_manager.py index 12af2204a..b5191e0d7 100644 --- a/tests/dbus_service_mocks/network_manager.py +++ b/tests/dbus_service_mocks/network_manager.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return NetworkManager() -# pylint: disable=invalid-name - - class NetworkManager(DBusServiceMock): """Network Manager mock. diff --git a/tests/dbus_service_mocks/network_settings.py b/tests/dbus_service_mocks/network_settings.py index c7f2e2c7a..68761016c 100644 --- a/tests/dbus_service_mocks/network_settings.py +++ b/tests/dbus_service_mocks/network_settings.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Settings() -# pylint: disable=invalid-name - - class Settings(DBusServiceMock): """Settings mock. diff --git a/tests/dbus_service_mocks/os_agent.py b/tests/dbus_service_mocks/os_agent.py index eb9f9920a..9f1caa416 100644 --- a/tests/dbus_service_mocks/os_agent.py +++ b/tests/dbus_service_mocks/os_agent.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return OSAgent() -# pylint: disable=invalid-name - - class OSAgent(DBusServiceMock): """OS-agent mock. diff --git a/tests/dbus_service_mocks/rauc.py b/tests/dbus_service_mocks/rauc.py index 72b302882..944a03b61 100644 --- a/tests/dbus_service_mocks/rauc.py +++ b/tests/dbus_service_mocks/rauc.py @@ -13,9 +13,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Rauc() -# pylint: disable=invalid-name - - class Rauc(DBusServiceMock): """Rauc mock. diff --git a/tests/dbus_service_mocks/resolved.py b/tests/dbus_service_mocks/resolved.py index 91633f6dc..e4267c4ed 100644 --- a/tests/dbus_service_mocks/resolved.py +++ b/tests/dbus_service_mocks/resolved.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Resolved() -# pylint: disable=invalid-name - - class Resolved(DBusServiceMock): """Resolved mock. diff --git a/tests/dbus_service_mocks/systemd.py b/tests/dbus_service_mocks/systemd.py index 46b15dd0a..1e2062066 100644 --- a/tests/dbus_service_mocks/systemd.py +++ b/tests/dbus_service_mocks/systemd.py @@ -14,9 +14,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Systemd() -# pylint: disable=invalid-name,missing-function-docstring,raising-bad-type - - class Systemd(DBusServiceMock): """Systemd mock. @@ -668,7 +665,7 @@ class Systemd(DBusServiceMock): def StopUnit(self, name: "s", mode: "s") -> "o": """Stop a service unit.""" if isinstance(self.response_stop_unit, DBusError): - raise self.response_stop_unit + raise self.response_stop_unit # pylint: disable=raising-bad-type if self.mock_systemd_unit: self.mock_systemd_unit.active_state = "inactive" return self.response_stop_unit @@ -677,7 +674,7 @@ class Systemd(DBusServiceMock): def ReloadOrRestartUnit(self, name: "s", mode: "s") -> "o": """Reload or restart a service unit.""" if isinstance(self.response_reload_or_restart_unit, DBusError): - raise self.response_reload_or_restart_unit + raise self.response_reload_or_restart_unit # pylint: disable=raising-bad-type if self.mock_systemd_unit: self.mock_systemd_unit.active_state = "active" return self.response_reload_or_restart_unit @@ -695,7 +692,7 @@ class Systemd(DBusServiceMock): ) -> "o": """Start a transient service unit.""" if isinstance(self.response_start_transient_unit, DBusError): - raise self.response_start_transient_unit + raise self.response_start_transient_unit # pylint: disable=raising-bad-type if self.mock_systemd_unit: self.mock_systemd_unit.active_state = "active" return self.response_start_transient_unit diff --git a/tests/dbus_service_mocks/systemd_unit.py b/tests/dbus_service_mocks/systemd_unit.py index 247f07d70..a37e0d94b 100644 --- a/tests/dbus_service_mocks/systemd_unit.py +++ b/tests/dbus_service_mocks/systemd_unit.py @@ -13,9 +13,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return SystemdUnit(object_path or DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name,missing-function-docstring - - class SystemdUnit(DBusServiceMock): """Systemd Unit mock. @@ -196,7 +193,7 @@ class SystemdUnit(DBusServiceMock): @dbus_property(access=PropertyAccess.READ) def RequiresMountsFor(self) -> "as": """Get RequiresMountsFor.""" - return ["/tmp"] + return ["/tmp"] # noqa: S108 @dbus_property(access=PropertyAccess.READ) def Documentation(self) -> "as": @@ -206,7 +203,7 @@ class SystemdUnit(DBusServiceMock): @dbus_property(access=PropertyAccess.READ) def Description(self) -> "s": """Get Description.""" - return "/tmp/yellow" + return "/tmp/yellow" # noqa: S108 @dbus_property(access=PropertyAccess.READ) def AccessSELinuxContext(self) -> "s": diff --git a/tests/dbus_service_mocks/timedate.py b/tests/dbus_service_mocks/timedate.py index 745b36f5d..27f94279b 100644 --- a/tests/dbus_service_mocks/timedate.py +++ b/tests/dbus_service_mocks/timedate.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return TimeDate() -# pylint: disable=invalid-name - - class TimeDate(DBusServiceMock): """TimeDate mock. diff --git a/tests/dbus_service_mocks/udisks2_block.py b/tests/dbus_service_mocks/udisks2_block.py index fb899ed55..65eeb0add 100644 --- a/tests/dbus_service_mocks/udisks2_block.py +++ b/tests/dbus_service_mocks/udisks2_block.py @@ -17,9 +17,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Block(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - @dataclass(slots=True) class BlockFixture: """Block fixture.""" diff --git a/tests/dbus_service_mocks/udisks2_drive.py b/tests/dbus_service_mocks/udisks2_drive.py index dff23a2af..96b569b7c 100644 --- a/tests/dbus_service_mocks/udisks2_drive.py +++ b/tests/dbus_service_mocks/udisks2_drive.py @@ -17,9 +17,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Drive(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - @dataclass(slots=True) class DriveFixture: """Drive fixture.""" diff --git a/tests/dbus_service_mocks/udisks2_filesystem.py b/tests/dbus_service_mocks/udisks2_filesystem.py index afb0638ca..7202a7fe0 100644 --- a/tests/dbus_service_mocks/udisks2_filesystem.py +++ b/tests/dbus_service_mocks/udisks2_filesystem.py @@ -16,9 +16,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Filesystem(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - @dataclass(slots=True) class FilesystemFixture: """Filesystem fixture.""" diff --git a/tests/dbus_service_mocks/udisks2_loop.py b/tests/dbus_service_mocks/udisks2_loop.py index 6ba275d8d..9184263f7 100644 --- a/tests/dbus_service_mocks/udisks2_loop.py +++ b/tests/dbus_service_mocks/udisks2_loop.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Loop() -# pylint: disable=invalid-name - - class Loop(DBusServiceMock): """Loop mock. diff --git a/tests/dbus_service_mocks/udisks2_manager.py b/tests/dbus_service_mocks/udisks2_manager.py index 5b23640d1..ece7ad2d0 100644 --- a/tests/dbus_service_mocks/udisks2_manager.py +++ b/tests/dbus_service_mocks/udisks2_manager.py @@ -12,9 +12,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return UDisks2Manager() -# pylint: disable=invalid-name - - class UDisks2Manager(DBusServiceMock): """UDisks2 Manager mock. diff --git a/tests/dbus_service_mocks/udisks2_partition.py b/tests/dbus_service_mocks/udisks2_partition.py index d1d0ffa01..e2c43ddfe 100644 --- a/tests/dbus_service_mocks/udisks2_partition.py +++ b/tests/dbus_service_mocks/udisks2_partition.py @@ -16,9 +16,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return Partition(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - @dataclass(slots=True) class PartitionFixture: """Partition fixture.""" diff --git a/tests/dbus_service_mocks/udisks2_partition_table.py b/tests/dbus_service_mocks/udisks2_partition_table.py index a7419f689..55342bbd3 100644 --- a/tests/dbus_service_mocks/udisks2_partition_table.py +++ b/tests/dbus_service_mocks/udisks2_partition_table.py @@ -15,9 +15,6 @@ def setup(object_path: str | None = None) -> DBusServiceMock: return PartitionTable(object_path if object_path else DEFAULT_OBJECT_PATH) -# pylint: disable=invalid-name - - @dataclass(slots=True) class PartitionTableFixture: """PartitionTable fixture.""" diff --git a/tests/docker/test_interface.py b/tests/docker/test_interface.py index 1fc962c27..7c11ee65b 100644 --- a/tests/docker/test_interface.py +++ b/tests/docker/test_interface.py @@ -204,9 +204,7 @@ async def test_attach_container_failure(coresys: CoreSys): ), patch( "supervisor.docker.manager.DockerAPI.images", new=PropertyMock(return_value=image_collection), - ), patch.object( - type(coresys.bus), "fire_event" - ) as fire_event: + ), patch.object(type(coresys.bus), "fire_event") as fire_event: await coresys.homeassistant.core.instance.attach(AwesomeVersion("2022.7.3")) assert not [ event @@ -228,9 +226,7 @@ async def test_attach_total_failure(coresys: CoreSys): ), patch( "supervisor.docker.manager.DockerAPI.images", new=PropertyMock(return_value=image_collection), - ), pytest.raises( - DockerError - ): + ), pytest.raises(DockerError): await coresys.homeassistant.core.instance.attach(AwesomeVersion("2022.7.3")) diff --git a/tests/homeassistant/test_core.py b/tests/homeassistant/test_core.py index acf167b8a..a52422f64 100644 --- a/tests/homeassistant/test_core.py +++ b/tests/homeassistant/test_core.py @@ -39,9 +39,7 @@ async def test_update_fails_if_out_of_date(coresys: CoreSys): type(coresys.plugins.audio), "need_update", new=PropertyMock(return_value=True) ), patch.object( type(coresys.plugins.audio), "update", side_effect=AudioUpdateError - ), pytest.raises( - HomeAssistantJobError - ): + ), pytest.raises(HomeAssistantJobError): await coresys.homeassistant.core.update() @@ -56,9 +54,7 @@ async def test_install_landingpage_docker_error( Updater, "image_homeassistant", new=PropertyMock(return_value="homeassistant") ), patch.object( DockerInterface, "arch", new=PropertyMock(return_value=CpuArch.AMD64) - ), patch( - "supervisor.homeassistant.core.asyncio.sleep" - ) as sleep, patch( + ), patch("supervisor.homeassistant.core.asyncio.sleep") as sleep, patch( "supervisor.security.module.cas_validate", side_effect=[CodeNotaryError, None], ): @@ -81,9 +77,7 @@ async def test_install_landingpage_other_error( Updater, "image_homeassistant", new=PropertyMock(return_value="homeassistant") ), patch.object( DockerInterface, "arch", new=PropertyMock(return_value=CpuArch.AMD64) - ), patch( - "supervisor.homeassistant.core.asyncio.sleep" - ) as sleep: + ), patch("supervisor.homeassistant.core.asyncio.sleep") as sleep: await coresys.homeassistant.core.install_landingpage() sleep.assert_awaited_once_with(30) @@ -104,9 +98,7 @@ async def test_install_docker_error( Updater, "version_homeassistant", new=PropertyMock(return_value="2022.7.3") ), patch.object( DockerInterface, "arch", new=PropertyMock(return_value=CpuArch.AMD64) - ), patch( - "supervisor.homeassistant.core.asyncio.sleep" - ) as sleep, patch( + ), patch("supervisor.homeassistant.core.asyncio.sleep") as sleep, patch( "supervisor.security.module.cas_validate", side_effect=[CodeNotaryError, None], ): @@ -131,9 +123,7 @@ async def test_install_other_error( Updater, "version_homeassistant", new=PropertyMock(return_value="2022.7.3") ), patch.object( DockerInterface, "arch", new=PropertyMock(return_value=CpuArch.AMD64) - ), patch( - "supervisor.homeassistant.core.asyncio.sleep" - ) as sleep: + ), patch("supervisor.homeassistant.core.asyncio.sleep") as sleep: await coresys.homeassistant.core.install() sleep.assert_awaited_once_with(30) diff --git a/tests/host/test_connectivity.py b/tests/host/test_connectivity.py index d8fa13992..5ea4bfffc 100644 --- a/tests/host/test_connectivity.py +++ b/tests/host/test_connectivity.py @@ -36,7 +36,7 @@ async def test_connectivity_connected( await coresys.host.network.check_connectivity(force=True) assert coresys.host.network.connectivity - assert network_manager_service.CheckConnectivity.calls == [tuple()] + assert network_manager_service.CheckConnectivity.calls == [()] @pytest.mark.parametrize("force", [True, False]) diff --git a/tests/host/test_logs.py b/tests/host/test_logs.py index ee3002496..de90fa235 100644 --- a/tests/host/test_logs.py +++ b/tests/host/test_logs.py @@ -51,22 +51,22 @@ async def test_boot_ids(coresys: CoreSys, journald_gateway: MagicMock): return_value=load_fixture("logs_boot_ids.txt") ) - assert TEST_BOOT_IDS == await coresys.host.logs.get_boot_ids() + assert await coresys.host.logs.get_boot_ids() == TEST_BOOT_IDS # Boot ID query should not be run again, mock a failure for it to ensure journald_gateway.side_effect = TimeoutError() - assert TEST_BOOT_IDS == await coresys.host.logs.get_boot_ids() + assert await coresys.host.logs.get_boot_ids() == TEST_BOOT_IDS - assert "b1c386a144fd44db8f855d7e907256f8" == await coresys.host.logs.get_boot_id(0) + assert await coresys.host.logs.get_boot_id(0) == "b1c386a144fd44db8f855d7e907256f8" # -1 is previous boot. We have 2 boots so -2 is too far - assert "b2aca10d5ca54fb1b6fb35c85a0efca9" == await coresys.host.logs.get_boot_id(-1) + assert await coresys.host.logs.get_boot_id(-1) == "b2aca10d5ca54fb1b6fb35c85a0efca9" with pytest.raises(ValueError): await coresys.host.logs.get_boot_id(-2) # 1 is oldest boot and count up from there. We have 2 boots so 3 is too far - assert "b2aca10d5ca54fb1b6fb35c85a0efca9" == await coresys.host.logs.get_boot_id(1) - assert "b1c386a144fd44db8f855d7e907256f8" == await coresys.host.logs.get_boot_id(2) + assert await coresys.host.logs.get_boot_id(1) == "b2aca10d5ca54fb1b6fb35c85a0efca9" + assert await coresys.host.logs.get_boot_id(2) == "b1c386a144fd44db8f855d7e907256f8" with pytest.raises(ValueError): await coresys.host.logs.get_boot_id(3) diff --git a/tests/host/test_manager.py b/tests/host/test_manager.py index 4b8784739..2f03d9339 100644 --- a/tests/host/test_manager.py +++ b/tests/host/test_manager.py @@ -14,7 +14,7 @@ from tests.dbus_service_mocks.systemd import Systemd as SystemdService @pytest.fixture(name="systemd_service") async def fixture_systemd_service( - all_dbus_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + all_dbus_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> SystemdService: """Return systemd service mock.""" yield all_dbus_services["systemd"] @@ -39,7 +39,7 @@ async def test_load(coresys: CoreSys, systemd_service: SystemdService): sound_update.assert_called_once() - assert systemd_service.ListUnits.calls == [tuple()] + assert systemd_service.ListUnits.calls == [()] async def test_reload(coresys: CoreSys, systemd_service: SystemdService): @@ -55,4 +55,4 @@ async def test_reload(coresys: CoreSys, systemd_service: SystemdService): connect.assert_not_called() sound_update.assert_called_once() - assert systemd_service.ListUnits.calls == [tuple()] + assert systemd_service.ListUnits.calls == [()] diff --git a/tests/host/test_network.py b/tests/host/test_network.py index df5928922..b5f08035c 100644 --- a/tests/host/test_network.py +++ b/tests/host/test_network.py @@ -31,7 +31,7 @@ from tests.dbus_service_mocks.network_manager import ( @pytest.fixture(name="active_connection_service") async def fixture_active_connection_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> ActiveConnectionService: """Return mock active connection service.""" yield network_manager_services["network_active_connection"] @@ -39,7 +39,7 @@ async def fixture_active_connection_service( @pytest.fixture(name="wireless_service") async def fixture_wireless_service( - network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> DeviceWirelessService: """Return mock device wireless service.""" yield network_manager_services["network_device_wireless"] diff --git a/tests/jobs/test_job_decorator.py b/tests/jobs/test_job_decorator.py index a3e174be8..4356e7110 100644 --- a/tests/jobs/test_job_decorator.py +++ b/tests/jobs/test_job_decorator.py @@ -1,5 +1,4 @@ """Test the condition decorators.""" -# pylint: disable=protected-access,import-error import asyncio from datetime import datetime, timedelta from unittest.mock import ANY, AsyncMock, Mock, PropertyMock, patch @@ -23,6 +22,7 @@ from supervisor.jobs import JobSchedulerOptions, SupervisorJob from supervisor.jobs.const import JobExecutionLimit from supervisor.jobs.decorator import Job, JobCondition from supervisor.jobs.job_group import JobGroup +from supervisor.os.manager import OSManager from supervisor.plugins.audio import PluginAudio from supervisor.resolution.const import UnhealthyReason from supervisor.utils.dt import utcnow @@ -159,11 +159,11 @@ async def test_haos(coresys: CoreSys): return True test = TestClass(coresys) - coresys.os._available = True - assert await test.execute() + with patch.object(OSManager, "available", new=PropertyMock(return_value=True)): + assert await test.execute() - coresys.os._available = False - assert not await test.execute() + with patch.object(OSManager, "available", new=PropertyMock(return_value=False)): + assert not await test.execute() coresys.jobs.ignore_conditions = [JobCondition.HAOS] assert await test.execute() @@ -490,8 +490,15 @@ async def test_plugins_updated(coresys: CoreSys): return True test = TestClass(coresys) - assert 0 == len( - [plugin.slug for plugin in coresys.plugins.all_plugins if plugin.need_update] + assert ( + len( + [ + plugin.slug + for plugin in coresys.plugins.all_plugins + if plugin.need_update + ] + ) + == 0 ) assert await test.execute() @@ -956,6 +963,7 @@ async def test_internal_jobs_no_notify(coresys: CoreSys): return True test1 = TestClass(coresys) + # pylint: disable-next=protected-access client = coresys.homeassistant.websocket._client client.async_send_command.reset_mock() @@ -965,7 +973,7 @@ async def test_internal_jobs_no_notify(coresys: CoreSys): await test1.execute_default() await asyncio.sleep(0) - client.async_send_command.call_count == 2 + assert client.async_send_command.call_count == 2 client.async_send_command.assert_called_with( { "type": "supervisor/event", diff --git a/tests/jobs/test_job_manager.py b/tests/jobs/test_job_manager.py index d6fd38591..77c8d0354 100644 --- a/tests/jobs/test_job_manager.py +++ b/tests/jobs/test_job_manager.py @@ -5,7 +5,6 @@ from unittest.mock import ANY import pytest -# pylint: disable=protected-access,import-error from supervisor.coresys import CoreSys from supervisor.exceptions import JobStartException @@ -51,9 +50,8 @@ async def test_job_done(coresys: CoreSys): assert not coresys.jobs.is_job assert job.done - with pytest.raises(JobStartException): - with job.start(): - pass + with pytest.raises(JobStartException), job.start(): + pass async def test_job_start_bad_parent(coresys: CoreSys): @@ -61,10 +59,8 @@ async def test_job_start_bad_parent(coresys: CoreSys): job = coresys.jobs.new_job(TEST_JOB) job2 = coresys.jobs.new_job(f"{TEST_JOB}_2") - with job.start(): - with pytest.raises(JobStartException): - with job2.start(): - pass + with job.start(), pytest.raises(JobStartException), job2.start(): + pass with job2.start(): assert coresys.jobs.current == job2 @@ -93,6 +89,7 @@ async def test_notify_on_change(coresys: CoreSys): job.progress = 50 await asyncio.sleep(0) + # pylint: disable=protected-access coresys.homeassistant.websocket._client.async_send_command.assert_called_with( { "type": "supervisor/event", @@ -225,3 +222,4 @@ async def test_notify_on_change(coresys: CoreSys): }, } ) + # pylint: enable=protected-access diff --git a/tests/os/test_data_disk.py b/tests/os/test_data_disk.py index c45e15709..bd721f38d 100644 --- a/tests/os/test_data_disk.py +++ b/tests/os/test_data_disk.py @@ -158,7 +158,7 @@ async def test_datadisk_migrate_mark_data_move( shutdown.assert_called_once() assert datadisk_service.ChangeDevice.calls == [] - assert datadisk_service.MarkDataMove.calls == [tuple()] + assert datadisk_service.MarkDataMove.calls == [()] assert block_service.Format.calls == [ ("gpt", {"auth.no_user_interaction": Variant("b", True)}) ] @@ -266,7 +266,7 @@ async def test_datadisk_migrate_between_external_renames( await coresys.os.datadisk.migrate_disk("Generic-Flash-Disk-61BCDDB6") - assert datadisk_service.MarkDataMove.calls == [tuple()] + assert datadisk_service.MarkDataMove.calls == [()] assert sdb1_partition_service.SetName.calls == [ ("hassos-data-external-old", {"auth.no_user_interaction": Variant("b", True)}) ] diff --git a/tests/os/test_manager.py b/tests/os/test_manager.py index 95eed7a34..8da4a148a 100644 --- a/tests/os/test_manager.py +++ b/tests/os/test_manager.py @@ -68,9 +68,7 @@ async def test_update_fails_if_out_of_date(coresys: CoreSys) -> None: type(coresys.supervisor), "need_update", new=PropertyMock(return_value=True) ), patch.object( type(coresys.os), "available", new=PropertyMock(return_value=True) - ), pytest.raises( - HassOSJobError - ): + ), pytest.raises(HassOSJobError): await coresys.os.update() diff --git a/tests/plugins/test_plugin_base.py b/tests/plugins/test_plugin_base.py index 1e5a7abff..c65d5e6f2 100644 --- a/tests/plugins/test_plugin_base.py +++ b/tests/plugins/test_plugin_base.py @@ -202,17 +202,13 @@ async def test_plugin_load_running_container( type(coresys.bus), "register_event" ) as register_event, patch.object( type(plugin.instance), "attach" - ) as attach, patch.object( - type(plugin), "install" - ) as install, patch.object( + ) as attach, patch.object(type(plugin), "install") as install, patch.object( type(plugin), "start" ) as start, patch.object( type(plugin.instance), "get_latest_version", return_value=test_version, - ), patch.object( - type(plugin.instance), "is_running", return_value=True - ): + ), patch.object(type(plugin.instance), "is_running", return_value=True): await plugin.load() register_event.assert_any_call( BusEvent.DOCKER_CONTAINER_STATE_CHANGE, plugin.watchdog_container @@ -238,17 +234,13 @@ async def test_plugin_load_stopped_container( type(coresys.bus), "register_event" ) as register_event, patch.object( type(plugin.instance), "attach" - ) as attach, patch.object( - type(plugin), "install" - ) as install, patch.object( + ) as attach, patch.object(type(plugin), "install") as install, patch.object( type(plugin), "start" ) as start, patch.object( type(plugin.instance), "get_latest_version", return_value=test_version, - ), patch.object( - type(plugin.instance), "is_running", return_value=False - ): + ), patch.object(type(plugin.instance), "is_running", return_value=False): await plugin.load() register_event.assert_any_call( BusEvent.DOCKER_CONTAINER_STATE_CHANGE, plugin.watchdog_container @@ -274,17 +266,13 @@ async def test_plugin_load_missing_container( type(coresys.bus), "register_event" ) as register_event, patch.object( type(plugin.instance), "attach", side_effect=DockerError() - ) as attach, patch.object( - type(plugin), "install" - ) as install, patch.object( + ) as attach, patch.object(type(plugin), "install") as install, patch.object( type(plugin), "start" ) as start, patch.object( type(plugin.instance), "get_latest_version", return_value=test_version, - ), patch.object( - type(plugin.instance), "is_running", return_value=False - ): + ), patch.object(type(plugin.instance), "is_running", return_value=False): await plugin.load() register_event.assert_any_call( BusEvent.DOCKER_CONTAINER_STATE_CHANGE, plugin.watchdog_container diff --git a/tests/resolution/check/test_check_multiple_data_disks.py b/tests/resolution/check/test_check_multiple_data_disks.py index 720554f70..c5dfe2844 100644 --- a/tests/resolution/check/test_check_multiple_data_disks.py +++ b/tests/resolution/check/test_check_multiple_data_disks.py @@ -17,7 +17,7 @@ from tests.dbus_service_mocks.udisks2_block import Block as BlockService @pytest.fixture(name="sda1_block_service") async def fixture_sda1_block_service( - udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> BlockService: """Return sda1 block service.""" yield udisks2_services["udisks2_block"][ diff --git a/tests/resolution/fixup/test_system_rename_data_disk.py b/tests/resolution/fixup/test_system_rename_data_disk.py index 8cad18fe0..2a78c08e1 100644 --- a/tests/resolution/fixup/test_system_rename_data_disk.py +++ b/tests/resolution/fixup/test_system_rename_data_disk.py @@ -19,7 +19,7 @@ from tests.dbus_service_mocks.udisks2_manager import ( @pytest.fixture(name="sda1_filesystem_service") async def fixture_sda1_filesystem_service( - udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> FilesystemService: """Return sda1 filesystem service.""" return udisks2_services["udisks2_filesystem"][ @@ -29,7 +29,7 @@ async def fixture_sda1_filesystem_service( @pytest.fixture(name="udisks2_service") async def fixture_udisks2_service( - udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]] + udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> UDisks2ManagerService: """Return udisks2 manager service.""" return udisks2_services["udisks2_manager"] diff --git a/tests/resolution/test_resolution_manager.py b/tests/resolution/test_resolution_manager.py index 9ff3f1696..3f928aedc 100644 --- a/tests/resolution/test_resolution_manager.py +++ b/tests/resolution/test_resolution_manager.py @@ -40,7 +40,7 @@ async def test_resolution_dismiss_suggestion(coresys: CoreSys): SuggestionType.CLEAR_FULL_BACKUP, ContextType.SYSTEM ) - assert SuggestionType.CLEAR_FULL_BACKUP == coresys.resolution.suggestions[-1].type + assert coresys.resolution.suggestions[-1].type == SuggestionType.CLEAR_FULL_BACKUP coresys.resolution.dismiss_suggestion(clear_backup) assert clear_backup not in coresys.resolution.suggestions @@ -83,7 +83,7 @@ async def test_resolution_dismiss_issue(coresys: CoreSys): IssueType.UPDATE_FAILED, ContextType.SYSTEM ) - assert IssueType.UPDATE_FAILED == coresys.resolution.issues[-1].type + assert coresys.resolution.issues[-1].type == IssueType.UPDATE_FAILED coresys.resolution.dismiss_issue(updated_failed) assert updated_failed not in coresys.resolution.issues @@ -101,12 +101,12 @@ async def test_resolution_create_issue_suggestion(coresys: CoreSys): [SuggestionType.EXECUTE_REPAIR], ) - assert IssueType.UPDATE_ROLLBACK == coresys.resolution.issues[-1].type - assert ContextType.CORE == coresys.resolution.issues[-1].context + assert coresys.resolution.issues[-1].type == IssueType.UPDATE_ROLLBACK + assert coresys.resolution.issues[-1].context == ContextType.CORE assert coresys.resolution.issues[-1].reference == "slug" - assert SuggestionType.EXECUTE_REPAIR == coresys.resolution.suggestions[-1].type - assert ContextType.CORE == coresys.resolution.suggestions[-1].context + assert coresys.resolution.suggestions[-1].type == SuggestionType.EXECUTE_REPAIR + assert coresys.resolution.suggestions[-1].context == ContextType.CORE @pytest.mark.asyncio diff --git a/tests/security/test_module.py b/tests/security/test_module.py index 89d45f2ed..f80f93232 100644 --- a/tests/security/test_module.py +++ b/tests/security/test_module.py @@ -55,9 +55,8 @@ async def test_force_content_trust(coresys: CoreSys): with patch( "supervisor.security.module.cas_validate", AsyncMock(side_effect=CodeNotaryError), - ) as cas_validate: - with pytest.raises(CodeNotaryError): - await coresys.security.verify_content("test@mail.com", "ffffffffffffff") + ) as cas_validate, pytest.raises(CodeNotaryError): + await coresys.security.verify_content("test@mail.com", "ffffffffffffff") async def test_integrity_check_disabled(coresys: CoreSys): diff --git a/tests/store/test_custom_repository.py b/tests/store/test_custom_repository.py index ff6fe0e9f..335692825 100644 --- a/tests/store/test_custom_repository.py +++ b/tests/store/test_custom_repository.py @@ -245,9 +245,7 @@ async def test_error_adding_duplicate( "supervisor.store.repository.Repository.validate", return_value=True ), patch( "supervisor.store.repository.Repository.load", return_value=None - ), pytest.raises( - StoreError - ): + ), pytest.raises(StoreError): await store_manager.add_repository(repository.source) diff --git a/tests/store/test_store_manager.py b/tests/store/test_store_manager.py index 8961c406e..8edc698e8 100644 --- a/tests/store/test_store_manager.py +++ b/tests/store/test_store_manager.py @@ -29,9 +29,7 @@ async def test_default_load(coresys: CoreSys): "supervisor.store.repository.Repository.load", return_value=None ), patch.object( type(coresys.config), "addons_repositories", return_value=[] - ), patch( - "pathlib.Path.exists", return_value=True - ): + ), patch("pathlib.Path.exists", return_value=True): await store_manager.load() assert len(store_manager.all) == 4 @@ -62,9 +60,7 @@ async def test_load_with_custom_repository(coresys: CoreSys): type(coresys.config), "addons_repositories", return_value=[] ), patch( "supervisor.store.repository.Repository.validate", return_value=True - ), patch( - "pathlib.Path.exists", return_value=True - ): + ), patch("pathlib.Path.exists", return_value=True): await store_manager.load() assert len(store_manager.all) == 5 @@ -168,9 +164,7 @@ async def test_update_unavailable_addon( HomeAssistant, "version", new=PropertyMock(return_value=AwesomeVersion("2022.1.1")), - ), patch( - "shutil.disk_usage", return_value=(42, 42, (1024.0**3)) - ): + ), patch("shutil.disk_usage", return_value=(42, 42, (1024.0**3))): with pytest.raises(AddonsNotSupportedError): await coresys.addons.update("local_ssh", backup=True) @@ -224,9 +218,7 @@ async def test_install_unavailable_addon( HomeAssistant, "version", new=PropertyMock(return_value=AwesomeVersion("2022.1.1")), - ), patch( - "shutil.disk_usage", return_value=(42, 42, (1024.0**3)) - ), pytest.raises( + ), patch("shutil.disk_usage", return_value=(42, 42, (1024.0**3))), pytest.raises( AddonsNotSupportedError ): await coresys.addons.install("local_ssh") diff --git a/tests/store/test_validate.py b/tests/store/test_validate.py index fcf895b4a..b780d839d 100644 --- a/tests/store/test_validate.py +++ b/tests/store/test_validate.py @@ -23,12 +23,15 @@ async def test_default_config(config: dict[Any]): assert "core" in conf[ATTR_REPOSITORIES] assert "local" in conf[ATTR_REPOSITORIES] assert "https://github.com/hassio-addons/repository" in conf[ATTR_REPOSITORIES] - assert 1 == len( - [ - repo - for repo in conf[ATTR_REPOSITORIES] - if repo == "https://github.com/esphome/home-assistant-addon" - ] + assert ( + len( + [ + repo + for repo in conf[ATTR_REPOSITORIES] + if repo == "https://github.com/esphome/home-assistant-addon" + ] + ) + == 1 ) diff --git a/tests/test_core.py b/tests/test_core.py index 80800d0f8..3b4dfa4fc 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -72,9 +72,7 @@ async def test_adjust_system_datetime_if_time_behind(coresys: CoreSys): SystemControl, "set_datetime" ) as mock_set_datetime, patch.object( InfoCenter, "dt_synchronized", new=PropertyMock(return_value=False) - ), patch.object( - Supervisor, "check_connectivity" - ) as mock_check_connectivity: + ), patch.object(Supervisor, "check_connectivity") as mock_check_connectivity: await coresys.core._adjust_system_datetime() mock_retrieve_whoami.assert_called_once() mock_set_datetime.assert_called_once() diff --git a/tests/test_coresys.py b/tests/test_coresys.py index 70e12add9..560b70bd0 100644 --- a/tests/test_coresys.py +++ b/tests/test_coresys.py @@ -40,7 +40,5 @@ def test_custom_user_agent(coresys: CoreSys): """Test custom useragent.""" assert ( "HomeAssistantSupervisor/99.9.9dev" - in coresys.websession._default_headers[ # pylint: disable=protected-access - USER_AGENT - ] + in coresys.websession._default_headers[USER_AGENT] # pylint: disable=protected-access ) diff --git a/tox.ini b/tox.ini index 537a4e4bc..7eac17699 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = lint, tests, black +envlist = lint, tests, ruff [testenv] deps = @@ -10,7 +10,7 @@ deps = basepython = python3 ignore_errors = True commands = - flake8 supervisor tests + ruff check --fix supervisor tests pylint --rcfile pylintrc supervisor tests [testenv:tests] @@ -18,7 +18,7 @@ basepython = python3 commands = pytest --timeout=10 tests -[testenv:black] +[testenv:ruff] basepython = python3 commands = - black --target-version py312 --check supervisor tests setup.py + ruff format supervisor tests