From 98b6467a4367a45f93df5a50f5d286e11cf37d65 Mon Sep 17 00:00:00 2001 From: Marc-Olivier Arsenault Date: Wed, 6 Dec 2023 20:40:22 -0500 Subject: [PATCH] move to ruff from black (#241) * move to ruff from black * add setup.cfg back for pytest --- .devcontainer/devcontainer.json | 6 +-- .github/workflows/ci.yml | 25 ++++++---- .gitignore | 1 + .pre-commit-config.yaml | 29 +++--------- .ruff.toml | 48 ++++++++++++++++++++ .vscode/settings.json | 11 +++-- custom_components/moonraker/__init__.py | 37 ++++++--------- custom_components/moonraker/api.py | 3 +- custom_components/moonraker/binary_sensor.py | 8 ++-- custom_components/moonraker/button.py | 10 ++-- custom_components/moonraker/camera.py | 4 +- custom_components/moonraker/config_flow.py | 17 ++----- custom_components/moonraker/const.py | 2 +- custom_components/moonraker/entity.py | 7 +-- custom_components/moonraker/number.py | 13 +++--- custom_components/moonraker/sensor.py | 36 +++++++-------- custom_components/moonraker/switch.py | 8 ++-- docs/conf.py | 1 + requirements.txt | 1 + scripts/lint | 7 +++ setup.cfg | 38 +--------------- tests/__init__.py | 1 + tests/conftest.py | 20 ++++---- tests/test_api.py | 6 +-- tests/test_binary_sensor.py | 6 ++- tests/test_button.py | 10 ++-- tests/test_camera.py | 34 +++++++------- tests/test_config_flow.py | 18 ++++---- tests/test_init.py | 13 +++--- tests/test_number.py | 8 ++-- tests/test_sensor.py | 29 ++++++++++-- tests/test_switch.py | 13 +++--- 32 files changed, 250 insertions(+), 220 deletions(-) create mode 100644 .ruff.toml create mode 100755 scripts/lint diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a9cea87..16a4236 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,15 +8,13 @@ "extensions": [ "ms-python.python", "github.vscode-pull-request-github", - "ms-python.black-formatter", - "ms-python.flake8", - "ms-python.isort", + "charliermarsh.ruff", "ms-python.vscode-pylance", "ryanluker.vscode-coverage-gutters" ], "settings": { "[python]": { - "editor.defaultFormatter": "ms-python.black-formatter" + "editor.defaultFormatter": "charliermarsh.ruff" }, "files.eol": "\n", "editor.tabSize": 4, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4759c3..d978dd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,19 +22,24 @@ jobs: - name: Hassfest validation uses: "home-assistant/actions/hassfest@master" - style: + ruff: + name: "Ruff" runs-on: "ubuntu-latest" - name: Check style formatting steps: - - uses: "actions/checkout@v4" - - uses: "actions/setup-python@v4" - - uses: isort/isort-action@v1.1.0 - - uses: jpetrucciani/black-check@master + - name: "Checkout the repository" + uses: "actions/checkout@v4.1.0" + + - name: "Set up Python" + uses: actions/setup-python@v4.7.1 with: - python-version: "3.x" - - run: python3 -m pip install flake8 - - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - - run: flake8 . --count --max-complexity=15 --max-line-length=127 --statistics + python-version: "3.11" + cache: "pip" + + - name: "Install requirements" + run: python3 -m pip install -r requirements.txt + + - name: "Run" + run: python3 -m ruff check . tests: runs-on: "ubuntu-latest" diff --git a/.gitignore b/.gitignore index bad3ccb..09eddc3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ secrets.yaml blueprints docs/_build coverage.xml +.ruff_cache diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d9f07f8..9b8c8dc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,31 +4,14 @@ repos: hooks: - id: pyupgrade args: [--py39-plus] - - repo: https://github.com/PyCQA/autoflake - rev: v2.0.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.6 hooks: - - id: autoflake + - id: ruff args: - - --in-place - - --remove-all-unused-imports - - repo: https://github.com/psf/black - rev: 22.12.0 - hooks: - - id: black - args: - - --quiet - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - args: - - --max-complexity=15 - - --max-line-length=127 - - --select=E9,F63,F7,F82 - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort + - --fix + - id: ruff-format + files: ^((homeassistant|pylint|script|tests)/.+)?[^/]+\.py$ - repo: https://github.com/pre-commit/mirrors-prettier rev: v2.7.1 hooks: diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..260b188 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,48 @@ +# The contents of this file is based on https://github.com/home-assistant/core/blob/dev/pyproject.toml + +target-version = "py310" + +select = [ + "B007", # Loop control variable {name} not used within loop body + "B014", # Exception handler with duplicate exception + "C", # complexity + "D", # docstrings + "E", # pycodestyle + "F", # pyflakes/autoflake + "ICN001", # import concentions; {name} should be imported as {asname} + "PGH004", # Use specific rule codes when using noqa + "PLC0414", # Useless import alias. Import alias does not rename original package. + "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} + "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 + "T20", # flake8-print + "TRY004", # Prefer TypeError exception for invalid type + "RUF006", # Store a reference to the return value of asyncio.create_task + "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 + "D404", # First word of the docstring should not be This + "D406", # Section name should end with a newline + "D407", # Section name underlining + "D411", # Missing blank line before section + "E501", # line too long + "E731", # do not assign a lambda expression, use a def +] + +[flake8-pytest-style] +fixture-parentheses = false + +[pyupgrade] +keep-runtime-typing = true + +[mccabe] +max-complexity = 25 diff --git a/.vscode/settings.json b/.vscode/settings.json index 016409e..e0792a3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,8 @@ { - "[python]": { - "editor.defaultFormatter": "ms-python.black-formatter", - "editor.formatOnSave": true - }, - "python.formatting.provider": "none" + // Please keep this file in sync with settings in home-assistant/.devcontainer/devcontainer.json + // Added --no-cov to work around TypeError: message must be set + // https://github.com/microsoft/vscode-python/issues/14067 + "python.testing.pytestArgs": ["--no-cov"], + // https://code.visualstudio.com/docs/python/testing#_pytest-configuration-settings + "python.testing.pytestEnabled": false } diff --git a/custom_components/moonraker/__init__.py b/custom_components/moonraker/__init__.py index f183963..949c00a 100755 --- a/custom_components/moonraker/__init__.py +++ b/custom_components/moonraker/__init__.py @@ -1,10 +1,8 @@ -""" -Moonraker integration for Home Assistant -""" +"""Moonraker integration for Home Assistant.""" import asyncio -from datetime import timedelta import logging import os.path +from datetime import timedelta import async_timeout from homeassistant.config_entries import ConfigEntry @@ -12,22 +10,13 @@ from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.helpers.update_coordinator import (DataUpdateCoordinator, + UpdateFailed) from .api import MoonrakerApiClient -from .const import ( - CONF_API_KEY, - CONF_PORT, - CONF_PRINTER_NAME, - CONF_TLS, - CONF_URL, - DOMAIN, - HOSTNAME, - METHODS, - OBJ, - PLATFORMS, - TIMEOUT, -) +from .const import (CONF_API_KEY, CONF_PORT, CONF_PRINTER_NAME, CONF_TLS, + CONF_URL, DOMAIN, HOSTNAME, METHODS, OBJ, PLATFORMS, + TIMEOUT) from .sensor import SENSORS SCAN_INTERVAL = timedelta(seconds=30) @@ -43,6 +32,7 @@ async def async_setup(_hass: HomeAssistant, _config: Config): def get_user_name(hass: HomeAssistant, entry: ConfigEntry): + """Get username.""" device_registry = dr.async_get(hass) device_entries = dr.async_entries_for_config_entry(device_registry, entry.entry_id) @@ -159,7 +149,7 @@ def __init__( async def _async_update_data(self): """Update data via library.""" - data = dict() + data = {} for updater in self.updaters: data.update(await updater(self)) @@ -256,26 +246,27 @@ async def _async_send_data(self, query_path: METHODS, query_obj) -> None: async def async_fetch_data( self, query_path: METHODS, query_obj: dict[str:any] = None, quiet: bool = False ): - """Fetch data from moonraker""" + """Fetch data from moonraker.""" return await self._async_fetch_data(query_path, query_obj, quiet=quiet) async def async_send_data( self, query_path: METHODS, query_obj: dict[str:any] = None ): - """Send data to moonraker""" + """Send data to moonraker.""" return await self._async_send_data(query_path, query_obj) def add_data_updater(self, updater): + """Update the data.""" self.updaters.append(updater) def load_sensor_data(self, sensor_list): - """Loading sensor data, so we can poll the right object""" + """Load sensor data, so we can poll the right object.""" for sensor in sensor_list: for subscriptions in sensor.subscriptions: self.add_query_objects(subscriptions[0], subscriptions[1]) def add_query_objects(self, query_object: str, result_key: str): - """Building the list of object we want to retreive from the server""" + """Build the list of object we want to retreive from the server.""" if query_object not in self.query_obj[OBJ]: self.query_obj[OBJ][query_object] = [] if result_key not in self.query_obj[OBJ][query_object]: diff --git a/custom_components/moonraker/api.py b/custom_components/moonraker/api.py index cda40e7..1d1791e 100755 --- a/custom_components/moonraker/api.py +++ b/custom_components/moonraker/api.py @@ -4,11 +4,12 @@ class MoonrakerApiClient(MoonrakerListener): - """Moonraker communication API""" + """Moonraker communication API.""" def __init__( self, url, session, port: int = 7125, api_key: str = None, tls: bool = False ): + """Init.""" self.running = False if api_key == "": api_key = None diff --git a/custom_components/moonraker/binary_sensor.py b/custom_components/moonraker/binary_sensor.py index 2024116..1807b10 100644 --- a/custom_components/moonraker/binary_sensor.py +++ b/custom_components/moonraker/binary_sensor.py @@ -3,10 +3,7 @@ from dataclasses import dataclass from homeassistant.components.binary_sensor import ( - BinarySensorDeviceClass, - BinarySensorEntity, - BinarySensorEntityDescription, -) + BinarySensorDeviceClass, BinarySensorEntity, BinarySensorEntityDescription) from .const import DOMAIN, METHODS from .entity import BaseMoonrakerEntity @@ -30,7 +27,7 @@ async def async_setup_entry(hass, entry, async_add_devices): async def async_setup_optional_binary_sensors(coordinator, entry, async_add_entities): - """Setup optional binary sensor platform.""" + """Set optional binary sensor platform.""" sensors = [] object_list = await coordinator.async_fetch_data(METHODS.PRINTER_OBJECTS_LIST) @@ -80,4 +77,5 @@ def __init__( @property def is_on(self) -> bool: + """Return state.""" return self.is_on_fn(self) diff --git a/custom_components/moonraker/button.py b/custom_components/moonraker/button.py index 5940ff5..022d18b 100644 --- a/custom_components/moonraker/button.py +++ b/custom_components/moonraker/button.py @@ -2,7 +2,8 @@ from collections.abc import Callable from dataclasses import dataclass -from homeassistant.components.button import ButtonEntity, ButtonEntityDescription +from homeassistant.components.button import (ButtonEntity, + ButtonEntityDescription) from .const import DOMAIN, METHODS from .entity import BaseMoonrakerEntity @@ -88,19 +89,19 @@ class MoonrakerButtonDescription(ButtonEntityDescription): async def async_setup_entry(hass, entry, async_add_entities): - """Setup sensor platform.""" + """Set sensor platform.""" coordinator = hass.data[DOMAIN][entry.entry_id] await async_setup_basic_buttons(coordinator, entry, async_add_entities) await async_setup_macros(coordinator, entry, async_add_entities) async def async_setup_basic_buttons(coordinator, entry, async_add_entities): - """Setup optional button platform.""" + """Set optional button platform.""" async_add_entities([MoonrakerButton(coordinator, entry, desc) for desc in BUTTONS]) async def async_setup_macros(coordinator, entry, async_add_entities): - """Setup optional button platform.""" + """Set optional button platform.""" cmds = await coordinator.async_fetch_data(METHODS.PRINTER_GCODE_HELP) macros = [] @@ -128,6 +129,7 @@ class MoonrakerButton(BaseMoonrakerEntity, ButtonEntity): """MoonrakerSensor Sensor class.""" def __init__(self, coordinator, entry, description): + """Intit.""" super().__init__(coordinator, entry) self.coordinator = coordinator self._attr_unique_id = f"{entry.entry_id}_{description.key}" diff --git a/custom_components/moonraker/camera.py b/custom_components/moonraker/camera.py index 80878f6..0e26bb8 100755 --- a/custom_components/moonraker/camera.py +++ b/custom_components/moonraker/camera.py @@ -97,7 +97,7 @@ class PreviewCamera(Camera): _attr_is_streaming = False def __init__(self, config_entry, coordinator, session) -> None: - """Initialize as a subclass of Camera for the Thumbnail Preview""" + """Initialize as a subclass of Camera for the Thumbnail Preview.""" super().__init__() self._attr_device_info = DeviceInfo( @@ -114,7 +114,7 @@ def __init__(self, config_entry, coordinator, session) -> None: async def async_camera_image( self, width: int | None = None, height: int | None = None ) -> bytes | None: - """Return current camera image""" + """Return current camera image.""" if ( self.coordinator.data["status"]["print_stats"]["state"] != PRINTSTATES.PRINTING.value diff --git a/custom_components/moonraker/config_flow.py b/custom_components/moonraker/config_flow.py index 0d484af..18471e1 100755 --- a/custom_components/moonraker/config_flow.py +++ b/custom_components/moonraker/config_flow.py @@ -2,21 +2,14 @@ import logging import async_timeout +import voluptuous as vol from homeassistant import config_entries from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util import network, slugify -import voluptuous as vol from .api import MoonrakerApiClient -from .const import ( - CONF_API_KEY, - CONF_PORT, - CONF_PRINTER_NAME, - CONF_TLS, - CONF_URL, - DOMAIN, - TIMEOUT, -) +from .const import (CONF_API_KEY, CONF_PORT, CONF_PRINTER_NAME, CONF_TLS, + CONF_URL, DOMAIN, TIMEOUT) _LOGGER = logging.getLogger(__name__) @@ -98,13 +91,13 @@ async def _test_host(self, host: str): return network.is_host_valid(host) async def _test_port(self, port): - if not port == "": + if port != "": if not port.isdigit() or int(port) > 65535 or int(port) <= 1: return False return True async def _test_api_key(self, api_key): - if not api_key == "": + if api_key != "": if not api_key.isalnum() or len(api_key) != 32: return False return True diff --git a/custom_components/moonraker/const.py b/custom_components/moonraker/const.py index 6c64c8f..fd4f09b 100644 --- a/custom_components/moonraker/const.py +++ b/custom_components/moonraker/const.py @@ -63,7 +63,7 @@ class ExtendedEnum(Enum): @classmethod def list(cls): """Return a list of all enum values.""" - return list(map(lambda c: c.value, cls)) + return [c.value for c in cls] class PRINTSTATES(ExtendedEnum): diff --git a/custom_components/moonraker/entity.py b/custom_components/moonraker/entity.py index 46a49a9..b3e0026 100755 --- a/custom_components/moonraker/entity.py +++ b/custom_components/moonraker/entity.py @@ -1,4 +1,4 @@ -"""Base class entity for Moonraker""" +"""Base class entity for Moonraker.""" from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -6,16 +6,17 @@ class BaseMoonrakerEntity(CoordinatorEntity): - """Base class entity for Moonraker""" + """Base class entity for Moonraker.""" def __init__(self, coordinator, config_entry): + """Init.""" super().__init__(coordinator) self.config_entry = config_entry self.api_device_name = coordinator.api_device_name @property def device_info(self): - """Entity device info""" + """Entity device info.""" return DeviceInfo( identifiers={(DOMAIN, self.config_entry.entry_id)}, name=self.api_device_name, diff --git a/custom_components/moonraker/number.py b/custom_components/moonraker/number.py index dd10d57..52e8ad4 100644 --- a/custom_components/moonraker/number.py +++ b/custom_components/moonraker/number.py @@ -1,12 +1,10 @@ """Number platform for Moonraker integration.""" -from dataclasses import dataclass import logging +from dataclasses import dataclass -from homeassistant.components.number import ( - NumberEntity, - NumberEntityDescription, - NumberMode, -) +from homeassistant.components.number import (NumberEntity, + NumberEntityDescription, + NumberMode) from homeassistant.core import callback from .const import DOMAIN, METHODS, OBJ @@ -30,7 +28,7 @@ async def async_setup_entry(hass, entry, async_add_devices): async def async_setup_output_pin(coordinator, entry, async_add_entities): - """Setup optional binary sensor platform.""" + """Set optional binary sensor platform.""" object_list = await coordinator.async_fetch_data(METHODS.PRINTER_OBJECTS_LIST) @@ -90,6 +88,7 @@ def __init__( self._attr_icon = description.icon async def async_set_native_value(self, value: float) -> None: + """Set native Value.""" await self.coordinator.async_send_data( METHODS.PRINTER_GCODE_SCRIPT, {"script": f"SET_PIN PIN={self.pin} VALUE={round(value/100, 2)}"}, diff --git a/custom_components/moonraker/sensor.py b/custom_components/moonraker/sensor.py index 63a2f13..718f352 100755 --- a/custom_components/moonraker/sensor.py +++ b/custom_components/moonraker/sensor.py @@ -1,16 +1,14 @@ """Sensor platform for Moonraker integration.""" +import logging from collections.abc import Callable from dataclasses import dataclass from datetime import datetime, timedelta, timezone -import logging -from homeassistant.components.sensor import ( - SensorDeviceClass, - SensorEntity, - SensorEntityDescription, - SensorStateClass, -) -from homeassistant.const import PERCENTAGE, UnitOfLength, UnitOfTemperature, UnitOfTime +from homeassistant.components.sensor import (SensorDeviceClass, SensorEntity, + SensorEntityDescription, + SensorStateClass) +from homeassistant.const import (PERCENTAGE, UnitOfLength, UnitOfTemperature, + UnitOfTime) from homeassistant.core import callback from .const import DOMAIN, METHODS, PRINTERSTATES, PRINTSTATES @@ -324,7 +322,7 @@ class MoonrakerSensorDescription(SensorEntityDescription): async def async_setup_entry(hass, entry, async_add_entities): - """Setup sensor platform.""" + """Set sensor platform.""" coordinator = hass.data[DOMAIN][entry.entry_id] await async_setup_basic_sensor(coordinator, entry, async_add_entities) @@ -333,13 +331,13 @@ async def async_setup_entry(hass, entry, async_add_entities): async def async_setup_basic_sensor(coordinator, entry, async_add_entities): - """Setup basic sensor platform.""" + """Set basic sensor platform.""" coordinator.load_sensor_data(SENSORS) async_add_entities([MoonrakerSensor(coordinator, entry, desc) for desc in SENSORS]) async def async_setup_optional_sensors(coordinator, entry, async_add_entities): - """Setup optional sensor platform.""" + """Set optional sensor platform.""" temperature_keys = [ "temperature_sensor", @@ -422,6 +420,7 @@ async def _history_updater(coordinator): async def async_setup_history_sensors(coordinator, entry, async_add_entities): + """Set history sensors.""" history = await coordinator.async_fetch_data(METHODS.SERVER_HISTORY_TOTALS) if history.get("error"): return @@ -482,6 +481,7 @@ class MoonrakerSensor(BaseMoonrakerEntity, SensorEntity): """MoonrakerSensor Sensor class.""" def __init__(self, coordinator, entry, description): + """Init.""" super().__init__(coordinator, entry) self.coordinator = coordinator self.status_key = description.status_key @@ -500,7 +500,7 @@ def _handle_coordinator_update(self) -> None: self.async_write_ha_state() def empty_result_when_not_printing(self, value=""): - """Return empty string when not printing""" + """Return empty string when not printing.""" if ( self.coordinator.data["status"]["print_stats"]["state"] != PRINTSTATES.PRINTING.value @@ -510,9 +510,9 @@ def empty_result_when_not_printing(self, value=""): def calculate_pct_job(data) -> float: - """ - Get a pct estimate of the job based on a mix of progress value and fillament used. - This strategy is inline with Mainsail estimate + """Get a pct estimate of the job based on a mix of progress value and fillament used. + + This strategy is inline with Mainsail estimate. """ print_expected_duration = data["estimated_time"] filament_used = data["status"]["print_stats"]["filament_used"] @@ -536,7 +536,7 @@ def calculate_pct_job(data) -> float: def calculate_eta(data): - """Calculate ETA of current print""" + """Calculate ETA of current print.""" percent_job = calculate_pct_job(data) if ( data["status"]["print_stats"]["print_duration"] <= 0 @@ -555,7 +555,7 @@ def calculate_eta(data): def calculate_current_layer(data): - """Calculate current layer""" + """Calculate current layer.""" if ( data["status"]["print_stats"]["state"] != PRINTSTATES.PRINTING.value @@ -588,7 +588,7 @@ def calculate_current_layer(data): def convert_time(time_s): - """Convert time in seconds to a human readable string""" + """Convert time in seconds to a human readable string.""" return ( f"{round(time_s // 3600)}h {round(time_s % 3600 // 60)}m {round(time_s % 60)}s" ) diff --git a/custom_components/moonraker/switch.py b/custom_components/moonraker/switch.py index 8e86049..2b5ad68 100644 --- a/custom_components/moonraker/switch.py +++ b/custom_components/moonraker/switch.py @@ -1,7 +1,8 @@ """Switch platform for Moonraker integration.""" from dataclasses import dataclass -from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.components.switch import (SwitchEntity, + SwitchEntityDescription) from .const import DOMAIN, METHODS, OBJ from .entity import BaseMoonrakerEntity @@ -33,7 +34,7 @@ async def _power_device_updater(coordinator): async def async_setup_output_pin(coordinator, entry, async_add_entities): - """Setup optional binary sensor platform.""" + """Set optional binary sensor platform.""" object_list = await coordinator.async_fetch_data(METHODS.PRINTER_OBJECTS_LIST) @@ -67,7 +68,7 @@ async def async_setup_output_pin(coordinator, entry, async_add_entities): async def async_setup_power_device(coordinator, entry, async_add_entities): - """Setup optional binary sensor platform.""" + """Set optional binary sensor platform.""" power_devices = await coordinator.async_fetch_data( METHODS.MACHINE_DEVICE_POWER_DEVICES @@ -145,6 +146,7 @@ class MoonrakerDigitalOutputPin(MoonrakerSwitchSensor): """Moonraker power device switch class.""" def __init__(self, coordinator, entry, description) -> None: + """Init.""" super().__init__(coordinator, entry, description) self.pin = description.sensor_name.replace("output_pin ", "") diff --git a/docs/conf.py b/docs/conf.py index 63ca707..79c3aed 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,4 @@ +"""Config for Shpinx.""" # Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: diff --git a/requirements.txt b/requirements.txt index fd14fe8..d9b19b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ sphinx==7.2.6 colorlog==6.8.0 urllib3==2.1.0 furo==2023.9.10 +ruff==0.1.7 diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 0000000..9b5b1df --- /dev/null +++ b/scripts/lint @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +ruff check . --fix diff --git a/setup.cfg b/setup.cfg index 60d8eb0..e97ecca 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,43 +1,7 @@ -[flake8] -exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build -doctests = True -# To work with Black -max-line-length = 88 -# E501: line too long -# W503: Line break occurred before a binary operator -# E203: Whitespace before ':' -# D202 No blank lines allowed after function docstring -# W504 line break after binary operator -ignore = - E501, - W503, - E203, - D202, - W504 - -[isort] -# https://github.com/timothycrosley/isort -# https://github.com/timothycrosley/isort/wiki/isort-Settings -# splits long import on multiple lines indented by 4 spaces -multi_line_output = 3 -include_trailing_comma=True -force_grid_wrap=0 -use_parentheses=True -line_length=88 -indent = " " -# by default isort don't check module indexes -not_skip = __init__.py -# will group `import x` and `from x import` of the same module. -force_sort_within_sections = true -sections = FUTURE,STDLIB,INBETWEENS,THIRDPARTY,FIRSTPARTY,LOCALFOLDER -default_section = THIRDPARTY -known_first_party = custom_components.integration_blueprint, tests -combine_as_imports = true - [tool:pytest] testpaths = tests norecursedirs = .git addopts = --strict-markers --cov=custom_components -asyncio_mode = auto \ No newline at end of file +asyncio_mode = auto diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..d420712 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests.""" diff --git a/tests/conftest.py b/tests/conftest.py index bef87a0..643f641 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,7 @@ # Remove to enable selective use of this fixture @pytest.fixture(autouse=True) def auto_enable_custom_integrations(enable_custom_integrations): - """Auto enable custom integrations""" + """Auto enable custom integrations.""" del enable_custom_integrations yield @@ -18,6 +18,7 @@ def auto_enable_custom_integrations(enable_custom_integrations): @pytest.fixture(autouse=True) def expected_lingering_timers() -> bool: """Temporary ability to bypass test failures. + Parametrize to True to bypass the pytest failure. @pytest.mark.parametrize("expected_lingering_timers", [True]) This should be removed when all lingering timers have been cleaned up. @@ -39,7 +40,7 @@ def skip_notifications_fixture(): @pytest.fixture(name="get_data") def get_data_fixture(): - """Get data Fixture""" + """Get data Fixture.""" return { "eventtime": 128684.342831779, "status": { @@ -206,7 +207,7 @@ def get_data_fixture(): @pytest.fixture(name="get_printer_info") def get_printer_info_fixture(): - """Get printer info fixture""" + """Get printer info fixture.""" return { "state": "ready", "state_message": "Printer is ready", @@ -222,7 +223,7 @@ def get_printer_info_fixture(): @pytest.fixture(name="get_gcode_help") def get_gcode_help_fixture(): - """Get gcode help fixture""" + """Get gcode help fixture.""" return { "SET_PAUSE_NEXT_LAYER": "Enable a pause if the next layer is reached", "SET_PAUSE_AT_LAYER": "Enable/disable a pause if a given layer number is reached", @@ -236,7 +237,7 @@ def get_gcode_help_fixture(): @pytest.fixture(name="get_printer_objects_list") def get_printer_objects_list_fixture(): - """Get printer objects list fixture""" + """Get printer objects list fixture.""" return { "objects": [ "webhooks", @@ -284,7 +285,7 @@ def get_printer_objects_list_fixture(): @pytest.fixture(name="get_camera_info") def get_camera_info_fixture(): - """Get camera info fixture""" + """Get camera info fixture.""" return { "webcams": [ { @@ -305,7 +306,7 @@ def get_camera_info_fixture(): @pytest.fixture(name="get_history") def get_history_fixture(): - """Get history fixture""" + """Get history fixture.""" return { "job_totals": { "total_jobs": 3, @@ -320,7 +321,7 @@ def get_history_fixture(): @pytest.fixture(name="get_power_devices") def get_power_devices_fixture(): - """Get power devices fixture""" + """Get power devices fixture.""" return { "devices": [ { @@ -349,7 +350,7 @@ def get_default_api_response_fixure( get_gcode_help, get_power_devices, ): - """Get all the default fixture returned by moonraker""" + """Get all the default fixture returned by moonraker.""" return { **get_data, **get_printer_info, @@ -363,6 +364,7 @@ def get_default_api_response_fixure( @pytest.fixture(name="_moonraker_default_mock", autouse=True) def get_moonraker_default_mock(get_default_api_response): + """Return mock for moonraker API.""" with patch( "moonraker_api.MoonrakerClient.call_method", return_value=get_default_api_response, diff --git a/tests/test_api.py b/tests/test_api.py index 7bc8f2d..7d1da10 100755 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,11 +1,11 @@ -""" API Tests""" +"""API Tests.""" from unittest.mock import patch from custom_components.moonraker.api import MoonrakerApiClient async def test_connect_client(): - """Test connect client""" + """Test connect client.""" with patch("moonraker_api.MoonrakerClient"), patch( "moonraker_api.websockets.websocketclient.WebsocketClient.connect" ), patch("moonraker_api.websockets.websocketclient.WebsocketClient.disconnect"): @@ -18,7 +18,7 @@ async def test_connect_client(): async def test_none_port_connect_client(): - """Test connect client""" + """Test connect client.""" with patch("moonraker_api.MoonrakerClient"), patch( "moonraker_api.websockets.websocketclient.WebsocketClient.connect" ), patch("moonraker_api.websockets.websocketclient.WebsocketClient.disconnect"): diff --git a/tests/test_binary_sensor.py b/tests/test_binary_sensor.py index 3748640..719fdd1 100644 --- a/tests/test_binary_sensor.py +++ b/tests/test_binary_sensor.py @@ -1,4 +1,4 @@ -""" Binary_sensor Tests""" +"""Binary_sensor Tests.""" from unittest.mock import patch import pytest @@ -18,6 +18,7 @@ def bypass_connect_client_fixture(): async def test_runout_filament_sensor_missing(hass, get_data, get_printer_objects_list): + """test.""" get_data["status"].pop("filament_switch_sensor filament_sensor_1", None) get_data["status"].pop("filament_switch_sensor filament_sensor_2", None) get_printer_objects_list["objects"].remove( @@ -39,6 +40,7 @@ async def test_runout_filament_sensor_missing(hass, get_data, get_printer_object async def test_runout_filament_sensor(hass): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) @@ -49,6 +51,7 @@ async def test_runout_filament_sensor(hass): async def test_multiple_runout_filament_sensor(hass): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) @@ -62,6 +65,7 @@ async def test_multiple_runout_filament_sensor(hass): async def test_runout_filament_sensor_off(hass, get_data): + """test.""" get_data["status"]["filament_switch_sensor filament_sensor_1"][ "filament_detected" ] = False diff --git a/tests/test_button.py b/tests/test_button.py index f600de9..c46d598 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -1,10 +1,11 @@ -""" Button Tests""" +"""Button Tests.""" from unittest.mock import patch -from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +import pytest +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN +from homeassistant.components.button import SERVICE_PRESS from homeassistant.const import ATTR_ENTITY_ID from homeassistant.helpers import entity_registry as er -import pytest from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.moonraker import async_setup_entry @@ -34,6 +35,7 @@ def bypass_connect_client_fixture(): ], ) async def test_buttons(hass, button, method): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) @@ -54,6 +56,7 @@ async def test_buttons(hass, button, method): async def test_gcode_macro(hass): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) @@ -76,6 +79,7 @@ async def test_gcode_macro(hass): async def test_disabled_buttons(hass): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) diff --git a/tests/test_camera.py b/tests/test_camera.py index 0c08fa8..22cd44f 100755 --- a/tests/test_camera.py +++ b/tests/test_camera.py @@ -1,11 +1,11 @@ -"""test moonraker camera""" +"""test moonraker camera.""" import datetime as dt from unittest.mock import patch -from PIL import Image +import pytest from homeassistant.components import camera from homeassistant.helpers import entity_registry as er -import pytest +from PIL import Image from pytest_homeassistant_custom_component.common import ( MockConfigEntry, async_fire_time_changed, @@ -25,7 +25,7 @@ def bypass_connect_client_fixture(): async def test_camera_services(hass, caplog): - """Test camera services""" + """Test camera services.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) @@ -40,7 +40,7 @@ async def test_camera_services(hass, caplog): async def test_camera_services_full_path(hass, get_camera_info, caplog): - """Test camera services""" + """Test camera services.""" get_camera_info["webcams"][0][ "stream_url" ] = "http://1.2.3.4/webcam/?action=2stream" @@ -60,7 +60,7 @@ async def test_camera_services_full_path(hass, get_camera_info, caplog): async def test_two_cameras_services(hass, get_camera_info): - """Test cameras Services""" + """Test cameras Services.""" get_camera_info["webcams"].append( { "name": "webcam2", @@ -80,7 +80,7 @@ async def test_two_cameras_services(hass, get_camera_info): async def test_two_cameras_same_name_services(hass, get_camera_info): - """Test two cameras same name""" + """Test two cameras same name.""" get_camera_info["webcams"].append( { "name": "webcam", @@ -100,7 +100,7 @@ async def test_two_cameras_same_name_services(hass, get_camera_info): async def test_setup_thumbnail_camera(hass, get_data): - """Test setup thumbnail camera""" + """Test setup thumbnail camera.""" get_data["status"]["print_stats"]["filename"] = "CE3E3V2_picture_frame_holder.gcode" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") @@ -115,7 +115,7 @@ async def test_setup_thumbnail_camera(hass, get_data): async def test_hardcoded_camera_empty_list(hass, get_default_api_response): - """Test hardcoded camera""" + """Test hardcoded camera.""" get_default_api_response["webcams"] = [] config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") @@ -130,7 +130,7 @@ async def test_hardcoded_camera_empty_list(hass, get_default_api_response): async def test_hardcoded_camera_API_error(hass, get_default_api_response): - """Test hardcoded camera""" + """Test hardcoded camera.""" get_default_api_response["webcams"] = None config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") @@ -147,7 +147,7 @@ async def test_hardcoded_camera_API_error(hass, get_default_api_response): async def test_thumbnail_camera_image( hass, aioclient_mock, get_data, _moonraker_default_mock ): - """Test thumbnail camera image""" + """Test thumbnail camera image.""" get_data["status"]["print_stats"]["filename"] = "CE3E3V2_picture_frame_holder.gcode" @@ -167,7 +167,7 @@ async def test_thumbnail_camera_image( async def test_thumbnail_camera_from_img_to_none(hass): - """Test thumbnail camera from img to none""" + """Test thumbnail camera from img to none.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) @@ -179,7 +179,7 @@ async def test_thumbnail_camera_from_img_to_none(hass): async def test_thumbnail_no_thumbnail(hass, get_data): - """Test setup thumbnail camera""" + """Test setup thumbnail camera.""" get_data["status"]["print_stats"]["filename"] = "" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") @@ -194,7 +194,7 @@ async def test_thumbnail_no_thumbnail(hass, get_data): async def test_thumbnail_not_printing(hass, aioclient_mock, get_data): - """Test setup thumbnail camera""" + """Test setup thumbnail camera.""" get_data["status"]["print_stats"]["state"] = PRINTSTATES.STANDBY.value config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") @@ -217,7 +217,7 @@ async def test_thumbnail_no_thumbnail_after_update( aioclient_mock, get_data, ): - """Test setup thumbnail camera""" + """Test setup thumbnail camera.""" get_data["status"]["print_stats"]["filename"] = "CE3E3V2_picture_frame_holder.gcode" @@ -249,7 +249,7 @@ async def test_thumbnail_no_thumbnail_after_update( async def test_thumbnail_data_failing( hass, get_data, get_printer_info, get_camera_info ): - """Test setup thumbnail camera""" + """Test setup thumbnail camera.""" get_data["status"]["print_stats"]["filename"] = "CE3E3V2_picture_frame_holder.gcode" del get_data["thumbnails"] @@ -269,7 +269,7 @@ async def test_thumbnail_data_failing( async def test_thumbnail_on_subfolder(hass, get_data, aioclient_mock): - """Test thumbnail on subfolder""" + """Test thumbnail on subfolder.""" get_data["status"]["print_stats"][ "filename" diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 8f807bd..69089cd 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -1,8 +1,8 @@ """Test moonraker config flow.""" from unittest.mock import patch -from homeassistant import config_entries, data_entry_flow import pytest +from homeassistant import config_entries, data_entry_flow from custom_components.moonraker.const import ( CONF_API_KEY, @@ -25,7 +25,7 @@ def bypass_connect_client_fixture(): @pytest.fixture(name="error_connect_client") def error_connect_client_fixture(): - """Throw error when trying to connect""" + """Throw error when trying to connect.""" with patch("custom_components.moonraker.MoonrakerApiClient.start"): raise Exception @@ -78,7 +78,7 @@ async def test_tmp_failing_config_flow(hass): @pytest.mark.usefixtures("bypass_connect_client") async def test_server_host_with_protocol(hass): - """Test server host when it has protocol""" + """Test server host when it has protocol.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -228,7 +228,7 @@ async def test_server_port_when_good_port(hass): @pytest.mark.usefixtures("bypass_connect_client") async def test_server_port_when_port_empty(hass): - """Test server port is left empty""" + """Test server port is left empty.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -250,7 +250,7 @@ async def test_server_port_when_port_empty(hass): @pytest.mark.usefixtures("bypass_connect_client") async def test_server_api_key_weird_char(hass): - """Test api key when contains weird characters""" + """Test api key when contains weird characters.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -263,7 +263,7 @@ async def test_server_api_key_weird_char(hass): @pytest.mark.usefixtures("bypass_connect_client") async def test_server_api_key_too_short(hass): - """Test api key when it's too short""" + """Test api key when it's too short.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -276,7 +276,7 @@ async def test_server_api_key_too_short(hass): @pytest.mark.usefixtures("bypass_connect_client") async def test_server_api_key_too_long(hass): - """Test api key when it's too long""" + """Test api key when it's too long.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -290,7 +290,7 @@ async def test_server_api_key_too_long(hass): @pytest.mark.usefixtures("bypass_connect_client") async def test_server_api_key_when_good(hass): - """Test api key when it's good""" + """Test api key when it's good.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -317,7 +317,7 @@ async def test_server_api_key_when_good(hass): @pytest.mark.usefixtures("bypass_connect_client") async def test_server_api_key_when_empty(hass): - """Test api key when it's empty""" + """Test api key when it's empty.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) diff --git a/tests/test_init.py b/tests/test_init.py index 3840d1e..71cfc05 100755 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -1,9 +1,9 @@ """Test moonraker setup process.""" from unittest.mock import patch +import pytest from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.update_coordinator import UpdateFailed -import pytest from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.moonraker import ( @@ -79,7 +79,7 @@ async def test_setup_unload_and_reload_entry_with_name(hass): async def test_async_send_data_exception(hass): - """Test async_post_exception""" + """Test async_post_exception.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) @@ -89,10 +89,9 @@ async def test_async_send_data_exception(hass): "moonraker_api.MoonrakerClient.call_method", side_effect=UpdateFailed, return_value={"result": "error"}, - ): - with pytest.raises(UpdateFailed): - coordinator = hass.data[DOMAIN][config_entry.entry_id] - assert await coordinator.async_send_data(METHODS.PRINTER_EMERGENCY_STOP) + ), pytest.raises(UpdateFailed): + coordinator = hass.data[DOMAIN][config_entry.entry_id] + assert await coordinator.async_send_data(METHODS.PRINTER_EMERGENCY_STOP) assert await async_unload_entry(hass, config_entry) @@ -111,7 +110,7 @@ async def test_setup_entry_exception(hass): def load_data(endpoint, *args, **kwargs): - """Load data""" + """Load data.""" if endpoint == "printer.info": return {"hostname": "mainsail"} diff --git a/tests/test_number.py b/tests/test_number.py index 643fdc1..a2e2589 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -1,9 +1,10 @@ -""" Number Tests""" +"""Number Tests.""" from unittest.mock import patch -from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, SERVICE_SET_VALUE -from homeassistant.const import ATTR_ENTITY_ID import pytest +from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN +from homeassistant.components.number import SERVICE_SET_VALUE +from homeassistant.const import ATTR_ENTITY_ID from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.moonraker import async_setup_entry @@ -25,6 +26,7 @@ def bypass_connect_client_fixture(): [("mainsail_output_pin_pwm"), ("mainsail_output_pin_CAPITALIZED")], ) async def test_number_set_value(hass, number, get_default_api_response): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index d1d4b68..75e9496 100755 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -2,8 +2,8 @@ import datetime as dt from unittest.mock import patch -from homeassistant.helpers.entity_registry import async_get as get_entity_registry import pytest +from homeassistant.helpers.entity_registry import async_get as get_entity_registry from pytest_homeassistant_custom_component.common import ( MockConfigEntry, async_fire_time_changed, @@ -28,7 +28,7 @@ def bypass_connect_client_fixture(): @pytest.fixture(name="data_for_calculate_pct") def data_for_calculate_pct_fixture(): - """data_for_calculate_pct""" + """data_for_calculate_pct.""" return { "estimated_time": 10, "status": { @@ -63,7 +63,9 @@ async def test_sensor_services_update(hass, get_data): assert hass.states.get("sensor.mainsail_bed_target").state == "100.0" -# test all sensors +"""test all sensors""" + + @pytest.mark.parametrize( "sensor, value", [ @@ -113,6 +115,7 @@ async def test_sensors( sensor, value, ): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) @@ -142,6 +145,7 @@ async def test_sensors_not_printing( value, get_data, ): + """test.""" get_data["status"]["print_stats"]["state"] = PRINTSTATES.STANDBY.value config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) @@ -152,6 +156,7 @@ async def test_sensors_not_printing( async def test_opt_sensor_missing(hass, get_data, get_printer_objects_list): + """test.""" get_data["status"].pop("temperature_sensor mcu_temp", None) get_printer_objects_list["objects"].remove("temperature_sensor mcu_temp") @@ -165,6 +170,7 @@ async def test_opt_sensor_missing(hass, get_data, get_printer_objects_list): async def test_eta(hass): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) @@ -181,6 +187,7 @@ async def test_eta(hass): async def test_slicer_time_left(hass, get_data): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) @@ -199,6 +206,7 @@ async def test_slicer_time_left(hass, get_data): async def test_eta_no_current_data(hass, get_data): + """test.""" get_data["status"]["print_stats"]["print_duration"] = 0 config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") @@ -212,20 +220,25 @@ async def test_eta_no_current_data(hass, get_data): async def test_calculate_pct_job(data_for_calculate_pct): + """test.""" assert calculate_pct_job(data_for_calculate_pct) == 0.55 async def test_calculate_pct_job_no_time(data_for_calculate_pct): + """test.""" data_for_calculate_pct["estimated_time"] = 0 assert calculate_pct_job(data_for_calculate_pct) == 0.5 async def test_calculate_pct_job_no_filament(data_for_calculate_pct): + """test.""" data_for_calculate_pct["filament_total"] = 0 assert calculate_pct_job(data_for_calculate_pct) == 0.6 async def test_calculate_pct_job_no_filament_no_time(data_for_calculate_pct): + """test_calculate_pct_job_no_filament_no_time.""" + data_for_calculate_pct["filament_total"] = 0 data_for_calculate_pct["estimated_time"] = 0 assert calculate_pct_job(data_for_calculate_pct) == 0 @@ -239,6 +252,8 @@ async def test_no_history_data( get_camera_info, get_gcode_help, ): + """Test no history.""" + get_history = {"error": "method not found"} with patch( @@ -262,6 +277,7 @@ async def test_no_history_data( async def test_double_sensor_data(hass, get_data, get_printer_objects_list): + """test.""" get_printer_objects_list["objects"].append("heater_fan controller_fan") get_data["status"]["heater_fan controller_fan"] = {"speed": 0.1234} @@ -286,6 +302,7 @@ async def test_double_sensor_data(hass, get_data, get_printer_objects_list): async def test_no_fan_sensor(hass, get_data, get_printer_objects_list): + """test.""" get_data["status"].pop("fan") get_printer_objects_list["objects"].remove("fan") @@ -299,6 +316,7 @@ async def test_no_fan_sensor(hass, get_data, get_printer_objects_list): async def test_rounding_fan(hass, get_data): + """test.""" get_data["status"]["fan"]["speed"] = 0.33333333333 config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") @@ -311,6 +329,7 @@ async def test_rounding_fan(hass, get_data): async def test_current_layer_in_info(): + """test.""" data = { "status": { "print_stats": { @@ -324,6 +343,7 @@ async def test_current_layer_in_info(): async def test_current_layer_calculated(): + """test.""" data = { "status": { "print_stats": { @@ -339,6 +359,7 @@ async def test_current_layer_calculated(): async def test_current_layer_calculated_layer_height_0(): + """test.""" data = { "status": { "print_stats": { @@ -354,6 +375,7 @@ async def test_current_layer_calculated_layer_height_0(): async def test_current_layer_calculate_missing_layer_height(): + """test.""" data = { "status": { "print_stats": { @@ -368,6 +390,7 @@ async def test_current_layer_calculate_missing_layer_height(): async def test_current_layer_calculated_partial_info(): + """test.""" data = { "status": { "print_stats": { diff --git a/tests/test_switch.py b/tests/test_switch.py index 8755ba0..f676dfe 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -1,13 +1,10 @@ -""" Switch Tests""" +"""Switch Tests.""" from unittest.mock import patch -from homeassistant.components.switch import ( - DOMAIN as SWITCH_DOMAIN, - SERVICE_TURN_OFF, - SERVICE_TURN_ON, -) -from homeassistant.const import ATTR_ENTITY_ID import pytest +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN +from homeassistant.components.switch import SERVICE_TURN_OFF, SERVICE_TURN_ON +from homeassistant.const import ATTR_ENTITY_ID from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.moonraker import async_setup_entry @@ -33,6 +30,7 @@ def bypass_connect_client_fixture(): ], ) async def test_switch_turn_on(hass, switch, switch_type, get_default_api_response): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry) @@ -74,6 +72,7 @@ async def test_switch_turn_on(hass, switch, switch_type, get_default_api_respons ], ) async def test_switch_turn_off(hass, switch, switch_type, get_default_api_response): + """test.""" config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test") config_entry.add_to_hass(hass) assert await async_setup_entry(hass, config_entry)