mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 11:13:34 +00:00
Add flake8-pytest-style (PT) for ruff (#1105)
This will catch common issues with pytest code. * Use `match` when using `pytest.raises()` for base exception types like `TypeError` or `ValueError` * Use tuples for `parametrize()` * Enforces `pytest.raises()` to contain simple statements, using `noqa` to skip this on some cases for now. * Fixes incorrect exception type (valueerror instead of typeerror) for iotdimmer. * Adds check valid types for `iotbulb.set_hsv` and `color` smart module. * Consolidate exception messages for common interface modules.
This commit is contained in:
parent
3e43781bb2
commit
6a86ffbbba
@ -388,10 +388,14 @@ class IotBulb(IotDevice):
|
||||
if not self._is_color:
|
||||
raise KasaException("Bulb does not support color.")
|
||||
|
||||
if not isinstance(hue, int) or not (0 <= hue <= 360):
|
||||
if not isinstance(hue, int):
|
||||
raise TypeError("Hue must be an integer.")
|
||||
if not (0 <= hue <= 360):
|
||||
raise ValueError(f"Invalid hue value: {hue} (valid range: 0-360)")
|
||||
|
||||
if not isinstance(saturation, int) or not (0 <= saturation <= 100):
|
||||
if not isinstance(saturation, int):
|
||||
raise TypeError("Saturation must be an integer.")
|
||||
if not (0 <= saturation <= 100):
|
||||
raise ValueError(
|
||||
f"Invalid saturation value: {saturation} (valid range: 0-100%)"
|
||||
)
|
||||
@ -445,7 +449,9 @@ class IotBulb(IotDevice):
|
||||
return await self._set_light_state(light_state, transition=transition)
|
||||
|
||||
def _raise_for_invalid_brightness(self, value):
|
||||
if not isinstance(value, int) or not (0 <= value <= 100):
|
||||
if not isinstance(value, int):
|
||||
raise TypeError("Brightness must be an integer")
|
||||
if not (0 <= value <= 100):
|
||||
raise ValueError(f"Invalid brightness value: {value} (valid range: 0-100%)")
|
||||
|
||||
@property # type: ignore
|
||||
|
@ -118,7 +118,9 @@ class IotDimmer(IotPlug):
|
||||
)
|
||||
|
||||
if not 0 <= brightness <= 100:
|
||||
raise ValueError("Brightness value %s is not valid." % brightness)
|
||||
raise ValueError(
|
||||
f"Invalid brightness value: {brightness} (valid range: 0-100%)"
|
||||
)
|
||||
|
||||
# Dimmers do not support a brightness of 0, but bulbs do.
|
||||
# Coerce 0 to 1 to maintain the same interface between dimmers and bulbs.
|
||||
@ -161,20 +163,18 @@ class IotDimmer(IotPlug):
|
||||
A brightness value of 0 will turn off the dimmer.
|
||||
"""
|
||||
if not isinstance(brightness, int):
|
||||
raise ValueError(
|
||||
"Brightness must be integer, " "not of %s.", type(brightness)
|
||||
)
|
||||
raise TypeError(f"Brightness must be an integer, not {type(brightness)}.")
|
||||
|
||||
if not 0 <= brightness <= 100:
|
||||
raise ValueError("Brightness value %s is not valid." % brightness)
|
||||
raise ValueError(
|
||||
f"Invalid brightness value: {brightness} (valid range: 0-100%)"
|
||||
)
|
||||
|
||||
# If zero set to 1 millisecond
|
||||
if transition == 0:
|
||||
transition = 1
|
||||
if not isinstance(transition, int):
|
||||
raise ValueError(
|
||||
"Transition must be integer, " "not of %s.", type(transition)
|
||||
)
|
||||
raise TypeError(f"Transition must be integer, not of {type(transition)}.")
|
||||
if transition <= 0:
|
||||
raise ValueError("Transition value %s is not valid." % transition)
|
||||
|
||||
|
@ -51,10 +51,12 @@ class Color(SmartModule):
|
||||
|
||||
return HSV(hue=h, saturation=s, value=v)
|
||||
|
||||
def _raise_for_invalid_brightness(self, value: int):
|
||||
def _raise_for_invalid_brightness(self, value):
|
||||
"""Raise error on invalid brightness value."""
|
||||
if not isinstance(value, int) or not (1 <= value <= 100):
|
||||
raise ValueError(f"Invalid brightness value: {value} (valid range: 1-100%)")
|
||||
if not isinstance(value, int):
|
||||
raise TypeError("Brightness must be an integer")
|
||||
if not (0 <= value <= 100):
|
||||
raise ValueError(f"Invalid brightness value: {value} (valid range: 0-100%)")
|
||||
|
||||
async def set_hsv(
|
||||
self,
|
||||
@ -73,10 +75,14 @@ class Color(SmartModule):
|
||||
:param int value: value in percentage [0, 100]
|
||||
:param int transition: transition in milliseconds.
|
||||
"""
|
||||
if not isinstance(hue, int) or not (0 <= hue <= 360):
|
||||
if not isinstance(hue, int):
|
||||
raise TypeError("Hue must be an integer")
|
||||
if not (0 <= hue <= 360):
|
||||
raise ValueError(f"Invalid hue value: {hue} (valid range: 0-360)")
|
||||
|
||||
if not isinstance(saturation, int) or not (0 <= saturation <= 100):
|
||||
if not isinstance(saturation, int):
|
||||
raise TypeError("Saturation must be an integer")
|
||||
if not (0 <= saturation <= 100):
|
||||
raise ValueError(
|
||||
f"Invalid saturation value: {saturation} (valid range: 0-100%)"
|
||||
)
|
||||
|
@ -90,7 +90,7 @@ class LightEffect(SmartModule, SmartLightEffect):
|
||||
"""
|
||||
if effect != self.LIGHT_EFFECTS_OFF and effect not in self._scenes_names_to_id:
|
||||
raise ValueError(
|
||||
f"Cannot set light effect to {effect}, possible values "
|
||||
f"The effect {effect} is not a built in effect. Possible values "
|
||||
f"are: {self.LIGHT_EFFECTS_OFF} "
|
||||
f"{' '.join(self._scenes_names_to_id.keys())}"
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ async def handle_turn_on(dev, turn_on):
|
||||
await dev.turn_off()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
def dummy_protocol():
|
||||
"""Return a smart protocol instance with a mocking-ready dummy transport."""
|
||||
|
||||
|
@ -75,7 +75,7 @@ new_discovery = parametrize_discovery(
|
||||
async def discovery_mock(request, mocker):
|
||||
"""Mock discovery and patch protocol queries to use Fake protocols."""
|
||||
fixture_info: FixtureInfo = request.param
|
||||
yield patch_discovery({DISCOVERY_MOCK_IP: fixture_info}, mocker)
|
||||
return patch_discovery({DISCOVERY_MOCK_IP: fixture_info}, mocker)
|
||||
|
||||
|
||||
def create_discovery_mock(ip: str, fixture_data: dict):
|
||||
@ -253,4 +253,4 @@ def unsupported_device_info(request, mocker):
|
||||
|
||||
mocker.patch("kasa.discover._DiscoverProtocol.do_discover", mock_discover)
|
||||
|
||||
yield discovery_data
|
||||
return discovery_data
|
||||
|
@ -18,17 +18,18 @@ async def test_brightness_component(dev: SmartDevice):
|
||||
# Test getting the value
|
||||
feature = brightness._device.features["brightness"]
|
||||
assert isinstance(feature.value, int)
|
||||
assert feature.value > 1 and feature.value <= 100
|
||||
assert feature.value > 1
|
||||
assert feature.value <= 100
|
||||
|
||||
# Test setting the value
|
||||
await feature.set_value(10)
|
||||
await dev.update()
|
||||
assert feature.value == 10
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="out of range"):
|
||||
await feature.set_value(feature.minimum_value - 10)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="out of range"):
|
||||
await feature.set_value(feature.maximum_value + 10)
|
||||
|
||||
|
||||
@ -41,15 +42,16 @@ async def test_brightness_dimmable(dev: IotDevice):
|
||||
# Test getting the value
|
||||
feature = dev.features["brightness"]
|
||||
assert isinstance(feature.value, int)
|
||||
assert feature.value > 0 and feature.value <= 100
|
||||
assert feature.value > 0
|
||||
assert feature.value <= 100
|
||||
|
||||
# Test setting the value
|
||||
await feature.set_value(10)
|
||||
await dev.update()
|
||||
assert feature.value == 10
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="out of range"):
|
||||
await feature.set_value(feature.minimum_value - 10)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="out of range"):
|
||||
await feature.set_value(feature.maximum_value + 10)
|
||||
|
@ -23,8 +23,8 @@ async def test_colortemp_component(dev: SmartDevice):
|
||||
await dev.update()
|
||||
assert feature.value == new_value
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="out of range"):
|
||||
await feature.set_value(feature.minimum_value - 10)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="out of range"):
|
||||
await feature.set_value(feature.maximum_value + 10)
|
||||
|
@ -18,7 +18,7 @@ autooff = parametrize(
|
||||
|
||||
@autooff
|
||||
@pytest.mark.parametrize(
|
||||
"feature, prop_name, type",
|
||||
("feature", "prop_name", "type"),
|
||||
[
|
||||
("auto_off_enabled", "enabled", bool),
|
||||
("auto_off_minutes", "delay", int),
|
||||
|
@ -10,7 +10,7 @@ contact = parametrize(
|
||||
|
||||
@contact
|
||||
@pytest.mark.parametrize(
|
||||
"feature, type",
|
||||
("feature", "type"),
|
||||
[
|
||||
("is_open", bool),
|
||||
],
|
||||
|
@ -76,8 +76,8 @@ async def test_fan_module(dev: SmartDevice, mocker: MockerFixture):
|
||||
await dev.update()
|
||||
assert not device.is_on
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Invalid level"):
|
||||
await fan.set_fan_speed_level(-1)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Invalid level"):
|
||||
await fan.set_fan_speed_level(5)
|
||||
|
@ -19,11 +19,10 @@ firmware = parametrize(
|
||||
|
||||
@firmware
|
||||
@pytest.mark.parametrize(
|
||||
"feature, prop_name, type, required_version",
|
||||
("feature", "prop_name", "type", "required_version"),
|
||||
[
|
||||
("auto_update_enabled", "auto_update_enabled", bool, 2),
|
||||
("update_available", "update_available", bool, 1),
|
||||
("update_available", "update_available", bool, 1),
|
||||
("current_firmware_version", "current_firmware", str, 1),
|
||||
("available_firmware_version", "latest_firmware", str, 1),
|
||||
],
|
||||
|
@ -10,7 +10,7 @@ humidity = parametrize(
|
||||
|
||||
@humidity
|
||||
@pytest.mark.parametrize(
|
||||
"feature, type",
|
||||
("feature", "type"),
|
||||
[
|
||||
("humidity", int),
|
||||
("humidity_warning", bool),
|
||||
|
@ -37,7 +37,7 @@ async def test_light_effect(dev: Device, mocker: MockerFixture):
|
||||
assert light_effect.effect == effect
|
||||
assert feature.value == effect
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="The effect foobar is not a built in effect"):
|
||||
await light_effect.set_effect("foobar")
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ async def test_light_strip_effect(dev: Device, mocker: MockerFixture):
|
||||
assert light_effect.effect == effect
|
||||
assert feature.value == effect
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="The effect foobar is not a built in effect"):
|
||||
await light_effect.set_effect("foobar")
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ motion = parametrize(
|
||||
|
||||
@motion
|
||||
@pytest.mark.parametrize(
|
||||
"feature, type",
|
||||
("feature", "type"),
|
||||
[
|
||||
("motion_detected", bool),
|
||||
],
|
||||
|
@ -16,7 +16,7 @@ temperature_warning = parametrize(
|
||||
|
||||
@temperature
|
||||
@pytest.mark.parametrize(
|
||||
"feature, type",
|
||||
("feature", "type"),
|
||||
[
|
||||
("temperature", float),
|
||||
("temperature_unit", str),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
@ -15,7 +16,7 @@ temperature = parametrize(
|
||||
|
||||
@thermostats_smart
|
||||
@pytest.mark.parametrize(
|
||||
"feature, type",
|
||||
("feature", "type"),
|
||||
[
|
||||
("target_temperature", float),
|
||||
("temperature_offset", int),
|
||||
@ -59,10 +60,14 @@ async def test_set_temperature_invalid_values(dev):
|
||||
"""Test that out-of-bounds temperature values raise errors."""
|
||||
temp_module: TemperatureControl = dev.modules["TemperatureControl"]
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError, match="Invalid target temperature -1, must be in range"
|
||||
):
|
||||
await temp_module.set_target_temperature(-1)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError, match="Invalid target temperature 100, must be in range"
|
||||
):
|
||||
await temp_module.set_target_temperature(100)
|
||||
|
||||
|
||||
@ -70,10 +75,14 @@ async def test_set_temperature_invalid_values(dev):
|
||||
async def test_temperature_offset(dev):
|
||||
"""Test the temperature offset API."""
|
||||
temp_module: TemperatureControl = dev.modules["TemperatureControl"]
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError, match=re.escape("Temperature offset must be [-10, 10]")
|
||||
):
|
||||
await temp_module.set_temperature_offset(100)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError, match=re.escape("Temperature offset must be [-10, 10]")
|
||||
):
|
||||
await temp_module.set_temperature_offset(-100)
|
||||
|
||||
await temp_module.set_temperature_offset(5)
|
||||
@ -83,7 +92,7 @@ async def test_temperature_offset(dev):
|
||||
|
||||
@thermostats_smart
|
||||
@pytest.mark.parametrize(
|
||||
"mode, states, frost_protection",
|
||||
("mode", "states", "frost_protection"),
|
||||
[
|
||||
pytest.param(ThermostatState.Idle, [], False, id="idle has empty"),
|
||||
pytest.param(
|
||||
@ -114,7 +123,7 @@ async def test_thermostat_mode(dev, mode, states, frost_protection):
|
||||
|
||||
@thermostats_smart
|
||||
@pytest.mark.parametrize(
|
||||
"mode, states, msg",
|
||||
("mode", "states", "msg"),
|
||||
[
|
||||
pytest.param(
|
||||
ThermostatState.Heating,
|
||||
|
@ -12,7 +12,7 @@ waterleak = parametrize(
|
||||
|
||||
@waterleak
|
||||
@pytest.mark.parametrize(
|
||||
"feature, prop_name, type",
|
||||
("feature", "prop_name", "type"),
|
||||
[
|
||||
("water_alert", "alert", int),
|
||||
("water_leak", "status", Enum),
|
||||
|
@ -100,7 +100,7 @@ async def test_login(mocker, status_code, error_code, inner_error_code, expectat
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"inner_error_codes, expectation, call_count",
|
||||
("inner_error_codes", "expectation", "call_count"),
|
||||
[
|
||||
([SmartErrorCode.LOGIN_ERROR, 0, 0, 0], does_not_raise(), 4),
|
||||
(
|
||||
@ -298,7 +298,7 @@ async def test_unknown_errors(mocker, error_code):
|
||||
"requestID": 1,
|
||||
"terminal_uuid": "foobar",
|
||||
}
|
||||
with pytest.raises(KasaException):
|
||||
with pytest.raises(KasaException): # noqa: PT012
|
||||
res = await transport.send(json_dumps(request))
|
||||
assert res is SmartErrorCode.INTERNAL_UNKNOWN_ERROR
|
||||
|
||||
@ -315,7 +315,7 @@ async def test_port_override():
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device_delay_required, should_error, should_succeed",
|
||||
("device_delay_required", "should_error", "should_succeed"),
|
||||
[
|
||||
pytest.param(0, False, True, id="No error"),
|
||||
pytest.param(0.125, True, True, id="Error then succeed"),
|
||||
|
@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
import pytest
|
||||
from voluptuous import (
|
||||
All,
|
||||
@ -51,10 +53,8 @@ async def test_state_attributes(dev: Device):
|
||||
|
||||
@bulb_iot
|
||||
async def test_light_state_without_update(dev: IotBulb, monkeypatch):
|
||||
monkeypatch.setitem(dev._last_update["system"]["get_sysinfo"], "light_state", None)
|
||||
with pytest.raises(KasaException):
|
||||
monkeypatch.setitem(
|
||||
dev._last_update["system"]["get_sysinfo"], "light_state", None
|
||||
)
|
||||
print(dev.light_state)
|
||||
|
||||
|
||||
@ -114,23 +114,72 @@ async def test_light_set_state(dev: IotBulb, mocker):
|
||||
|
||||
@color_bulb
|
||||
@turn_on
|
||||
async def test_invalid_hsv(dev: Device, turn_on):
|
||||
@pytest.mark.parametrize(
|
||||
("hue", "sat", "brightness", "exception_cls", "error"),
|
||||
[
|
||||
pytest.param(-1, 0, 0, ValueError, "Invalid hue", id="hue out of range"),
|
||||
pytest.param(361, 0, 0, ValueError, "Invalid hue", id="hue out of range"),
|
||||
pytest.param(
|
||||
0.5, 0, 0, TypeError, "Hue must be an integer", id="hue invalid type"
|
||||
),
|
||||
pytest.param(
|
||||
"foo", 0, 0, TypeError, "Hue must be an integer", id="hue invalid type"
|
||||
),
|
||||
pytest.param(
|
||||
0, -1, 0, ValueError, "Invalid saturation", id="saturation out of range"
|
||||
),
|
||||
pytest.param(
|
||||
0, 101, 0, ValueError, "Invalid saturation", id="saturation out of range"
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
0.5,
|
||||
0,
|
||||
TypeError,
|
||||
"Saturation must be an integer",
|
||||
id="saturation invalid type",
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
"foo",
|
||||
0,
|
||||
TypeError,
|
||||
"Saturation must be an integer",
|
||||
id="saturation invalid type",
|
||||
),
|
||||
pytest.param(
|
||||
0, 0, -1, ValueError, "Invalid brightness", id="brightness out of range"
|
||||
),
|
||||
pytest.param(
|
||||
0, 0, 101, ValueError, "Invalid brightness", id="brightness out of range"
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
0,
|
||||
0.5,
|
||||
TypeError,
|
||||
"Brightness must be an integer",
|
||||
id="brightness invalid type",
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
0,
|
||||
"foo",
|
||||
TypeError,
|
||||
"Brightness must be an integer",
|
||||
id="brightness invalid type",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_invalid_hsv(
|
||||
dev: Device, turn_on, hue, sat, brightness, exception_cls, error
|
||||
):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await handle_turn_on(dev, turn_on)
|
||||
assert light.is_color
|
||||
|
||||
for invalid_hue in [-1, 361, 0.5]:
|
||||
with pytest.raises(ValueError):
|
||||
await light.set_hsv(invalid_hue, 0, 0) # type: ignore[arg-type]
|
||||
|
||||
for invalid_saturation in [-1, 101, 0.5]:
|
||||
with pytest.raises(ValueError):
|
||||
await light.set_hsv(0, invalid_saturation, 0) # type: ignore[arg-type]
|
||||
|
||||
for invalid_brightness in [-1, 101, 0.5]:
|
||||
with pytest.raises(ValueError):
|
||||
await light.set_hsv(0, 0, invalid_brightness) # type: ignore[arg-type]
|
||||
with pytest.raises(exception_cls, match=error):
|
||||
await light.set_hsv(hue, sat, brightness)
|
||||
|
||||
|
||||
@color_bulb
|
||||
@ -201,9 +250,13 @@ async def test_smart_temp_range(dev: Device):
|
||||
async def test_out_of_range_temperature(dev: Device):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError, match="Temperature should be between \d+ and \d+, was 1000"
|
||||
):
|
||||
await light.set_color_temp(1000)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError, match="Temperature should be between \d+ and \d+, was 10000"
|
||||
):
|
||||
await light.set_color_temp(10000)
|
||||
|
||||
|
||||
@ -236,7 +289,7 @@ async def test_dimmable_brightness(dev: IotBulb, turn_on):
|
||||
await dev.update()
|
||||
assert dev.brightness == 10
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(TypeError, match="Brightness must be an integer"):
|
||||
await dev.set_brightness("foo") # type: ignore[arg-type]
|
||||
|
||||
|
||||
@ -264,10 +317,16 @@ async def test_dimmable_brightness_transition(dev: IotBulb, mocker):
|
||||
async def test_invalid_brightness(dev: IotBulb):
|
||||
assert dev._is_dimmable
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match=re.escape("Invalid brightness value: 110 (valid range: 0-100%)"),
|
||||
):
|
||||
await dev.set_brightness(110)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match=re.escape("Invalid brightness value: -100 (valid range: 0-100%)"),
|
||||
):
|
||||
await dev.set_brightness(-100)
|
||||
|
||||
|
||||
|
@ -128,9 +128,9 @@ async def test_light_effect_module(dev: Device, mocker: MockerFixture):
|
||||
assert feat.value == second_effect
|
||||
call.reset_mock()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="The effect foobar is not a built in effect."):
|
||||
await light_effect_module.set_effect("foobar")
|
||||
call.assert_not_called()
|
||||
call.assert_not_called()
|
||||
|
||||
|
||||
@light_effect
|
||||
@ -174,10 +174,10 @@ async def test_light_brightness(dev: Device):
|
||||
await dev.update()
|
||||
assert light.brightness == 10
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Invalid brightness value: "):
|
||||
await light.set_brightness(feature.minimum_value - 10)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Invalid brightness value: "):
|
||||
await light.set_brightness(feature.maximum_value + 10)
|
||||
|
||||
|
||||
@ -213,10 +213,10 @@ async def test_light_color_temp(dev: Device):
|
||||
assert light.color_temp == feature.minimum_value + 20
|
||||
assert light.brightness == 60
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Temperature should be between \d+ and \d+"):
|
||||
await light.set_color_temp(feature.minimum_value - 10)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Temperature should be between \d+ and \d+"):
|
||||
await light.set_color_temp(feature.maximum_value + 10)
|
||||
|
||||
|
||||
@ -293,9 +293,9 @@ async def test_light_preset_module(dev: Device, mocker: MockerFixture):
|
||||
assert preset_mod.preset == second_preset
|
||||
assert feat.value == second_preset
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="foobar is not a valid preset"):
|
||||
await preset_mod.set_preset("foobar")
|
||||
assert call.call_count == 3
|
||||
assert call.call_count == 3
|
||||
|
||||
|
||||
@light_preset
|
||||
@ -315,9 +315,7 @@ async def test_light_preset_save(dev: Device, mocker: MockerFixture):
|
||||
await preset_mod.save_preset(second_preset, new_preset)
|
||||
await dev.update()
|
||||
new_preset_state = preset_mod.preset_states_list[0]
|
||||
assert (
|
||||
new_preset_state.brightness == new_preset.brightness
|
||||
and new_preset_state.hue == new_preset.hue
|
||||
and new_preset_state.saturation == new_preset.saturation
|
||||
and new_preset_state.color_temp == new_preset.color_temp
|
||||
)
|
||||
assert new_preset_state.brightness == new_preset.brightness
|
||||
assert new_preset_state.hue == new_preset.hue
|
||||
assert new_preset_state.saturation == new_preset.saturation
|
||||
assert new_preset_state.color_temp == new_preset.color_temp
|
||||
|
@ -103,7 +103,7 @@ async def test_create_thin_wrapper():
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device_class, use_class", kasa.deprecated_smart_devices.items()
|
||||
("device_class", "use_class"), kasa.deprecated_smart_devices.items()
|
||||
)
|
||||
def test_deprecated_devices(device_class, use_class):
|
||||
package_name = ".".join(use_class.__module__.split(".")[:-1])
|
||||
@ -117,7 +117,9 @@ def test_deprecated_devices(device_class, use_class):
|
||||
getattr(module, use_class.__name__)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("deprecated_class, use_class", kasa.deprecated_classes.items())
|
||||
@pytest.mark.parametrize(
|
||||
("deprecated_class", "use_class"), kasa.deprecated_classes.items()
|
||||
)
|
||||
def test_deprecated_classes(deprecated_class, use_class):
|
||||
msg = f"{deprecated_class} is deprecated, use {use_class.__name__} instead"
|
||||
with pytest.deprecated_call(match=msg):
|
||||
|
@ -46,13 +46,23 @@ async def test_set_brightness_transition(dev, turn_on, mocker):
|
||||
|
||||
@dimmer_iot
|
||||
async def test_set_brightness_invalid(dev):
|
||||
for invalid_brightness in [-1, 101, 0.5]:
|
||||
with pytest.raises(ValueError):
|
||||
for invalid_brightness in [-1, 101]:
|
||||
with pytest.raises(ValueError, match="Invalid brightness"):
|
||||
await dev.set_brightness(invalid_brightness)
|
||||
|
||||
for invalid_transition in [-1, 0.5]:
|
||||
with pytest.raises(ValueError):
|
||||
for invalid_type in [0.5, "foo"]:
|
||||
with pytest.raises(TypeError, match="Brightness must be an integer"):
|
||||
await dev.set_brightness(invalid_type)
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
async def test_set_brightness_invalid_transition(dev):
|
||||
for invalid_transition in [-1]:
|
||||
with pytest.raises(ValueError, match="Transition value .+? is not valid."):
|
||||
await dev.set_brightness(1, transition=invalid_transition)
|
||||
for invalid_type in [0.5, "foo"]:
|
||||
with pytest.raises(TypeError, match="Transition must be integer"):
|
||||
await dev.set_brightness(1, transition=invalid_type)
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
@ -128,14 +138,24 @@ async def test_set_dimmer_transition_to_off(dev, turn_on, mocker):
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
async def test_set_dimmer_transition_invalid(dev):
|
||||
for invalid_brightness in [-1, 101, 0.5]:
|
||||
with pytest.raises(ValueError):
|
||||
async def test_set_dimmer_transition_invalid_brightness(dev):
|
||||
for invalid_brightness in [-1, 101]:
|
||||
with pytest.raises(ValueError, match="Invalid brightness value: "):
|
||||
await dev.set_dimmer_transition(invalid_brightness, 1000)
|
||||
|
||||
for invalid_transition in [-1, 0.5]:
|
||||
with pytest.raises(ValueError):
|
||||
await dev.set_dimmer_transition(1, invalid_transition)
|
||||
for invalid_type in [0.5, "foo"]:
|
||||
with pytest.raises(TypeError, match="Transition must be integer"):
|
||||
await dev.set_dimmer_transition(1, invalid_type)
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
async def test_set_dimmer_transition_invalid_transition(dev):
|
||||
for invalid_transition in [-1]:
|
||||
with pytest.raises(ValueError, match="Transition value .+? is not valid."):
|
||||
await dev.set_dimmer_transition(1, transition=invalid_transition)
|
||||
for invalid_type in [0.5, "foo"]:
|
||||
with pytest.raises(TypeError, match="Transition must be integer"):
|
||||
await dev.set_dimmer_transition(1, transition=invalid_type)
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
|
@ -252,7 +252,7 @@ INVALIDS = [
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("msg, data", INVALIDS)
|
||||
@pytest.mark.parametrize(("msg", "data"), INVALIDS)
|
||||
async def test_discover_invalid_info(msg, data, mocker):
|
||||
"""Make sure that invalid discovery information raises an exception."""
|
||||
host = "127.0.0.1"
|
||||
@ -304,7 +304,7 @@ async def test_discover_datagram_received(mocker, discovery_data):
|
||||
assert dev.host == addr
|
||||
|
||||
|
||||
@pytest.mark.parametrize("msg, data", INVALIDS)
|
||||
@pytest.mark.parametrize(("msg", "data"), INVALIDS)
|
||||
async def test_discover_invalid_responses(msg, data, mocker):
|
||||
"""Verify that we don't crash whole discovery if some devices in the network are sending unexpected data."""
|
||||
proto = _DiscoverProtocol()
|
||||
@ -349,7 +349,7 @@ async def test_discover_single_authentication(discovery_mock, mocker):
|
||||
side_effect=AuthenticationError("Failed to authenticate"),
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
with pytest.raises( # noqa: PT012
|
||||
AuthenticationError,
|
||||
match="Failed to authenticate",
|
||||
):
|
||||
@ -495,7 +495,7 @@ async def test_do_discover_drop_packets(mocker, port, do_not_reply_count):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"port, will_timeout",
|
||||
("port", "will_timeout"),
|
||||
[(FakeDatagramTransport.GHOST_PORT, True), (20002, False)],
|
||||
ids=["unknownport", "unsupporteddevice"],
|
||||
)
|
||||
|
@ -61,7 +61,7 @@ async def test_get_emeter_realtime(dev):
|
||||
|
||||
|
||||
@has_emeter_iot
|
||||
@pytest.mark.requires_dummy
|
||||
@pytest.mark.requires_dummy()
|
||||
async def test_get_emeter_daily(dev):
|
||||
assert dev.has_emeter
|
||||
|
||||
@ -81,7 +81,7 @@ async def test_get_emeter_daily(dev):
|
||||
|
||||
|
||||
@has_emeter_iot
|
||||
@pytest.mark.requires_dummy
|
||||
@pytest.mark.requires_dummy()
|
||||
async def test_get_emeter_monthly(dev):
|
||||
assert dev.has_emeter
|
||||
|
||||
|
@ -14,7 +14,7 @@ class DummyDevice:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
def dummy_feature() -> Feature:
|
||||
# create_autospec for device slows tests way too much, so we use a dummy here
|
||||
|
||||
@ -49,7 +49,7 @@ def test_feature_api(dummy_feature: Feature):
|
||||
)
|
||||
def test_feature_setter_on_sensor(read_only_type):
|
||||
"""Test that creating a sensor feature with a setter causes an error."""
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Invalid type for configurable feature"):
|
||||
Feature(
|
||||
device=DummyDevice(), # type: ignore[arg-type]
|
||||
id="dummy_error",
|
||||
@ -103,7 +103,7 @@ async def test_feature_setter(dev, mocker, dummy_feature: Feature):
|
||||
async def test_feature_setter_read_only(dummy_feature):
|
||||
"""Verify that read-only feature raises an exception when trying to change it."""
|
||||
dummy_feature.attribute_setter = None
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Tried to set read-only feature"):
|
||||
await dummy_feature.set_value("value for read only feature")
|
||||
|
||||
|
||||
@ -134,7 +134,7 @@ async def test_feature_choice_list(dummy_feature, caplog, mocker: MockerFixture)
|
||||
mock_setter.assert_called_with("first")
|
||||
mock_setter.reset_mock()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError, match="Unexpected value for dummy_feature: invalid"): # noqa: PT012
|
||||
await dummy_feature.set_value("invalid")
|
||||
assert "Unexpected value" in caplog.text
|
||||
|
||||
|
@ -14,7 +14,7 @@ from ..httpclient import HttpClient
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"error, error_raises, error_message",
|
||||
("error", "error_raises", "error_message"),
|
||||
[
|
||||
(
|
||||
aiohttp.ServerDisconnectedError(),
|
||||
@ -52,7 +52,7 @@ from ..httpclient import HttpClient
|
||||
"ServerFingerprintMismatch",
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("mock_read", (False, True), ids=("post", "read"))
|
||||
@pytest.mark.parametrize("mock_read", [False, True], ids=("post", "read"))
|
||||
async def test_httpclient_errors(mocker, error, error_raises, error_message, mock_read):
|
||||
class _mock_response:
|
||||
def __init__(self, status, error):
|
||||
|
@ -89,7 +89,7 @@ async def test_state_info(dev):
|
||||
assert isinstance(dev.state_information, dict)
|
||||
|
||||
|
||||
@pytest.mark.requires_dummy
|
||||
@pytest.mark.requires_dummy()
|
||||
@device_iot
|
||||
async def test_invalid_connection(mocker, dev):
|
||||
with (
|
||||
|
@ -49,7 +49,7 @@ class _mock_response:
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"error, retry_expectation",
|
||||
("error", "retry_expectation"),
|
||||
[
|
||||
(Exception("dummy exception"), False),
|
||||
(aiohttp.ServerTimeoutError("dummy exception"), True),
|
||||
@ -79,7 +79,7 @@ async def test_protocol_retries_via_client_session(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"error, retry_expectation",
|
||||
("error", "retry_expectation"),
|
||||
[
|
||||
(KasaException("dummy exception"), False),
|
||||
(_RetryableError("dummy exception"), True),
|
||||
@ -305,7 +305,7 @@ async def test_transport_decrypt_error(mocker, caplog):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device_credentials, expectation",
|
||||
("device_credentials", "expectation"),
|
||||
[
|
||||
(Credentials("foo", "bar"), does_not_raise()),
|
||||
(Credentials(), does_not_raise()),
|
||||
@ -321,7 +321,7 @@ async def test_transport_decrypt_error(mocker, caplog):
|
||||
ids=("client", "blank", "kasa_setup", "shouldfail"),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"transport_class, seed_auth_hash_calc",
|
||||
("transport_class", "seed_auth_hash_calc"),
|
||||
[
|
||||
pytest.param(KlapTransport, lambda c, s, a: c + a, id="KLAP"),
|
||||
pytest.param(KlapTransportV2, lambda c, s, a: c + s + a, id="KLAPV2"),
|
||||
@ -365,7 +365,7 @@ async def test_handshake1(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"transport_class, seed_auth_hash_calc1, seed_auth_hash_calc2",
|
||||
("transport_class", "seed_auth_hash_calc1", "seed_auth_hash_calc2"),
|
||||
[
|
||||
pytest.param(
|
||||
KlapTransport, lambda c, s, a: c + a, lambda c, s, a: s + a, id="KLAP"
|
||||
@ -466,7 +466,7 @@ async def test_query(mocker):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"response_status, credentials_match, expectation",
|
||||
("response_status", "credentials_match", "expectation"),
|
||||
[
|
||||
pytest.param(
|
||||
(403, 403, 403),
|
||||
|
@ -22,7 +22,9 @@ async def test_lightstrip_effect(dev: IotLightStrip):
|
||||
|
||||
@lightstrip_iot
|
||||
async def test_effects_lightstrip_set_effect(dev: IotLightStrip):
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(
|
||||
ValueError, match="The effect Not real is not a built in effect"
|
||||
):
|
||||
await dev.set_effect("Not real")
|
||||
|
||||
await dev.set_effect("Candy Cane")
|
||||
|
@ -33,7 +33,7 @@ from .fakeprotocol_iot import FakeIotTransport
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
("protocol_class", "transport_class"),
|
||||
[
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
@ -63,7 +63,7 @@ async def test_protocol_retries(mocker, retry_count, protocol_class, transport_c
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
("protocol_class", "transport_class"),
|
||||
[
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
@ -87,7 +87,7 @@ async def test_protocol_no_retry_on_unreachable(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
("protocol_class", "transport_class"),
|
||||
[
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
@ -111,7 +111,7 @@ async def test_protocol_no_retry_connection_refused(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
("protocol_class", "transport_class"),
|
||||
[
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
@ -135,7 +135,7 @@ async def test_protocol_retry_recoverable_error(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
("protocol_class", "transport_class", "encryption_class"),
|
||||
[
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
@ -185,7 +185,7 @@ async def test_protocol_reconnect(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
("protocol_class", "transport_class", "encryption_class"),
|
||||
[
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
@ -239,7 +239,7 @@ async def test_protocol_handles_cancellation_during_write(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
("protocol_class", "transport_class", "encryption_class"),
|
||||
[
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
@ -291,7 +291,7 @@ async def test_protocol_handles_cancellation_during_connection(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
("protocol_class", "transport_class", "encryption_class"),
|
||||
[
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
@ -338,7 +338,7 @@ async def test_protocol_logging(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
("protocol_class", "transport_class", "encryption_class"),
|
||||
[
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
@ -494,14 +494,10 @@ def test_protocol_init_signature(class_name_obj):
|
||||
params = list(inspect.signature(class_name_obj[1].__init__).parameters.values())
|
||||
|
||||
assert len(params) == 2
|
||||
assert (
|
||||
params[0].name == "self"
|
||||
and params[0].kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
|
||||
)
|
||||
assert (
|
||||
params[1].name == "transport"
|
||||
and params[1].kind == inspect.Parameter.KEYWORD_ONLY
|
||||
)
|
||||
assert params[0].name == "self"
|
||||
assert params[0].kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
|
||||
assert params[1].name == "transport"
|
||||
assert params[1].kind == inspect.Parameter.KEYWORD_ONLY
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -511,13 +507,10 @@ def test_transport_init_signature(class_name_obj):
|
||||
params = list(inspect.signature(class_name_obj[1].__init__).parameters.values())
|
||||
|
||||
assert len(params) == 2
|
||||
assert (
|
||||
params[0].name == "self"
|
||||
and params[0].kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
|
||||
)
|
||||
assert (
|
||||
params[1].name == "config" and params[1].kind == inspect.Parameter.KEYWORD_ONLY
|
||||
)
|
||||
assert params[0].name == "self"
|
||||
assert params[0].kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
|
||||
assert params[1].name == "config"
|
||||
assert params[1].kind == inspect.Parameter.KEYWORD_ONLY
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -582,7 +575,7 @@ async def test_transport_credentials_hash(
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"transport_class",
|
||||
[AesTransport, KlapTransport, KlapTransportV2, XorTransport, XorTransport],
|
||||
[AesTransport, KlapTransport, KlapTransportV2, XorTransport],
|
||||
)
|
||||
async def test_transport_credentials_hash_from_config(mocker, transport_class):
|
||||
"""Test that credentials_hash provided via config sets correctly."""
|
||||
@ -599,7 +592,7 @@ async def test_transport_credentials_hash_from_config(mocker, transport_class):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"error, retry_expectation",
|
||||
("error", "retry_expectation"),
|
||||
[
|
||||
(ConnectionRefusedError("dummy exception"), False),
|
||||
(OSError(errno.EHOSTDOWN, os.strerror(errno.EHOSTDOWN)), False),
|
||||
@ -609,7 +602,7 @@ async def test_transport_credentials_hash_from_config(mocker, transport_class):
|
||||
ids=("ConnectionRefusedError", "OSErrorNoRetry", "OSErrorRetry", "Exception"),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
("protocol_class", "transport_class"),
|
||||
[
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
@ -631,7 +624,7 @@ async def test_protocol_will_retry_on_connect(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"error, retry_expectation",
|
||||
("error", "retry_expectation"),
|
||||
[
|
||||
(ConnectionRefusedError("dummy exception"), True),
|
||||
(OSError(errno.EHOSTDOWN, os.strerror(errno.EHOSTDOWN)), True),
|
||||
@ -641,7 +634,7 @@ async def test_protocol_will_retry_on_connect(
|
||||
ids=("ConnectionRefusedError", "OSErrorNoRetry", "OSErrorRetry", "Exception"),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
("protocol_class", "transport_class"),
|
||||
[
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
|
@ -145,7 +145,7 @@ def test_tutorial_examples(readmes_mock):
|
||||
assert not res["failed"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
async def readmes_mock(mocker):
|
||||
fixture_infos = {
|
||||
"127.0.0.1": get_fixture_info("KP303(UK)_1.0_1.0.3.json", "IOT"), # Strip
|
||||
@ -154,4 +154,4 @@ async def readmes_mock(mocker):
|
||||
"127.0.0.4": get_fixture_info("KL430(US)_1.0_1.0.10.json", "IOT"), # Lightstrip
|
||||
"127.0.0.5": get_fixture_info("HS220(US)_1.0_1.5.7.json", "IOT"), # Dimmer
|
||||
}
|
||||
yield patch_discovery(fixture_infos, mocker)
|
||||
return patch_discovery(fixture_infos, mocker)
|
||||
|
@ -65,7 +65,7 @@ async def test_smart_device_unknown_errors(
|
||||
dummy_protocol._transport, "send", return_value=mock_response
|
||||
)
|
||||
|
||||
with pytest.raises(KasaException):
|
||||
with pytest.raises(KasaException): # noqa: PT012
|
||||
res = await dummy_protocol.query(DUMMY_QUERY)
|
||||
assert res is SmartErrorCode.INTERNAL_UNKNOWN_ERROR
|
||||
|
||||
|
@ -20,7 +20,8 @@ def test_usage_convert_stat_data():
|
||||
k, v = d.popitem()
|
||||
assert isinstance(k, int)
|
||||
assert isinstance(v, int)
|
||||
assert k == 4 and v == 30
|
||||
assert k == 4
|
||||
assert v == 30
|
||||
|
||||
|
||||
def test_usage_today():
|
||||
|
@ -117,6 +117,7 @@ select = [
|
||||
"FA", # flake8-future-annotations
|
||||
"I", # isort
|
||||
"S", # bandit
|
||||
"PT", # flake8-pytest-style
|
||||
"LOG", # flake8-logging
|
||||
"G", # flake8-logging-format
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user