8000 Add flake8-pytest-style (PT) for ruff (#1105) · msz-coder/python-kasa@6a86ffb · GitHub
[go: up one dir, main page]

Skip to content

Commit 6a86ffb

Browse files
authored
Add flake8-pytest-style (PT) for ruff (python-kasa#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.
1 parent 3e43781 commit 6a86ffb

36 files changed

+248
-150
lines changed

kasa/iot/iotbulb.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,10 +388,14 @@ async def _set_hsv(
388388
if not self._is_color:
389389
raise KasaException("Bulb does not support color.")
390390

391-
if not isinstance(hue, int) or not (0 <= hue <= 360):
391+
if not isinstance(hue, int):
392+
raise TypeError("Hue must be an integer.")
393+
if not (0 <= hue <= 360):
392394
raise ValueError(f"Invalid hue value: {hue} (valid range: 0-360)")
393395

394-
if not isinstance(saturation, int) or not (0 <= saturation <= 100):
396+
if not isinstance(saturation, int):
397+
raise TypeError("Saturation must be an integer.")
398+
if not (0 <= saturation <= 100):
395399
raise ValueError(
396400
f"Invalid saturation value: {saturation} (valid range: 0-100%)"
397401
)
@@ -445,7 +449,9 @@ async def _set_color_temp(
445449
return await self._set_light_state(light_state, transition=transition)
446450

447451
def _raise_for_invalid_brightness(self, value):
448-
if not isinstance(value, int) or not (0 <= value <= 100):
452+
if not isinstance(value, int):
453+
raise TypeError("Brightness must be an integer")
454+
if not (0 <= value <= 100):
449455
raise ValueError(f"Invalid brightness value: {value} (valid range: 0-100%)")
450456

451457
@property # type: ignore

kasa/iot/iotdimmer.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ async def _set_brightness(self, brightness: int, *, transition: int | None = Non
118118
)
119119

120120
if not 0 <= brightness <= 100:
121-
raise ValueError("Brightness value %s is not valid." % brightness)
121+
raise ValueError(
122+
f"Invalid brightness value: {brightness} (valid range: 0-100%)"
123+
)
122124

123125
# Dimmers do not support a brightness of 0, but bulbs do.
124126
# Coerce 0 to 1 to maintain the same interface between dimmers and bulbs.
@@ -161,20 +163,18 @@ async def set_dimmer_transition(self, brightness: int, transition: int):
161163
A brightness value of 0 will turn off the dimmer.
162164
"""
163165
if not isinstance(brightness, int):
164-
raise ValueError(
165-
"Brightness must be integer, " "not of %s.", type(brightness)
166-
)
166+
raise TypeError(f"Brightness must be an integer, not {type(brightness)}.")
167167

168168
if not 0 <= brightness <= 100:
169-
raise ValueError("Brightness value %s is not valid." % brightness)
169+
raise ValueError(
170+
f"Invalid brightness value: {brightness} (valid range: 0-100%)"
171+
)
170172

171173
# If zero set to 1 millisecond
172174
if transition == 0:
173175
transition = 1
174176
if not isinstance(transition, int):
175-
raise ValueError(
176-
"Transition must be integer, " "not of %s.", type(transition)
177-
)
177+
raise TypeError(f"Transition must be integer, not of {type(transition)}.")
178178
if transition <= 0:
179179
raise ValueError("Transition value %s is not valid." % transition)
180180

kasa/smart/modules/color.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ def hsv(self) -> HSV:
5151

5252
return HSV(hue=h, saturation=s, value=v)
5353

54-
def _raise_for_invalid_brightness(self, value: int):
54+
def _raise_for_invalid_brightness(self, value):
5555
"""Raise error on invalid brightness value."""
56-
if not isinstance(value, int) or not (1 <= value <= 100):
57-
raise ValueError(f"Invalid brightness value: {value} (valid range: 1-100%)")
56+
if not isinstance(value, int):
57+
raise TypeError("Brightness must be an integer")
58+
if not (0 <= value <= 100):
59+
raise ValueError(f"Invalid brightness value: {value} (valid range: 0-100%)")
5860

5961
async def set_hsv(
6062
self,
@@ -73,10 +75,14 @@ async def set_hsv(
7375
:param int value: value in percentage [0, 100]
7476
:param int transition: transition in milliseconds.
7577
"""
76-
if not isinstance(hue, int) or not (0 <= hue <= 360):
78+
if not isinstance(hue, int):
79+
raise TypeError("Hue must be an integer")
80+
if not (0 <= hue <= 360):
7781
raise ValueError(f"Invalid hue value: {hue} (valid range: 0-360)")
7882

79-
if not isinstance(saturation, int) or not (0 <= saturation <= 100):
83+
if not isinstance(saturation, int):
84+
raise TypeError("Saturation must be an integer")
85+
if not (0 <= saturation <= 100):
8086
raise ValueError(
8187
f"Invalid saturation value: {saturation} (valid range: 0-100%)"
8288
)

kasa/smart/modules/lighteffect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ async def set_effect(
9090
"""
9191
if effect != self.LIGHT_EFFECTS_OFF and effect not in self._scenes_names_to_id:
9292
raise ValueError(
93-
f"Cannot set light effect to {effect}, possible values "
93+
f"The effect {effect} is not a built in effect. Possible values "
9494
f"are: {self.LIGHT_EFFECTS_OFF} "
9595
f"{' '.join(self._scenes_names_to_id.keys())}"
9696
)

kasa/tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ async def handle_turn_on(dev, turn_on):
2525
await dev.turn_off()
2626

2727

28-
@pytest.fixture
28+
@pytest.fixture()
2929
def dummy_protocol():
3030
"""Return a smart protocol instance with a mocking-ready dummy transport."""
3131

kasa/tests/discovery_fixtures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def parametrize_discovery(
7575
async def discovery_mock(request, mocker):
7676
"""Mock discovery and patch protocol queries to use Fake protocols."""
7777
fixture_info: FixtureInfo = request.param
78-
yield patch_discovery({DISCOVERY_MOCK_IP: fixture_info}, mocker)
78+
return patch_discovery({DISCOVERY_MOCK_IP: fixture_info}, mocker)
7979

8080

8181
def create_discovery_mock(ip: str, fixture_data: dict):
@@ -253,4 +253,4 @@ async def mock_discover(self):
253253

254254
mocker.patch("kasa.discover._DiscoverProtocol.do_discover", mock_discover)
255255

256-
yield discovery_data
256+
return discovery_data

kasa/tests/smart/features/test_brightness.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@ async def test_brightness_component(dev: SmartDevice):
1818
# Test getting the value
1919
feature = brightness._device.features["brightness"]
2020
assert isinstance(feature.value, int)
21-
assert feature.value > 1 and feature.value <= 100
21+
assert feature.value > 1
22+
assert feature.value <= 100
2223

2324
# Test setting the value
2425
await feature.set_value(10)
2526
await dev.update()
2627
assert feature.value == 10
2728

28-
with pytest.raises(ValueError):
29+
with pytest.raises(ValueError, match="out of range"):
2930
await feature.set_value(feature.minimum_value - 10)
3031

31-
with pytest.raises(ValueError):
32+
with pytest.raises(ValueError, match="out of range"):
3233
await feature.set_value(feature.maximum_value + 10)
3334

3435

@@ -41,15 +42,16 @@ async def test_brightness_dimmable(dev: IotDevice):
4142
# Test getting the value
4243
feature = dev.features["brightness"]
4344
assert isinstance(feature.value, int)
44-
assert feature.value > 0 and feature.value <= 100
45+
assert feature.value > 0
46+
assert feature.value <= 100
4547

4648
# Test setting the value
4749
await feature.set_value(10)
4850
await dev.update()
4951
assert feature.value == 10
5052

51-
with pytest.raises(ValueError):
53+
with pytest.raises(ValueError, match="out of range"):
5254
await feature.set_value(feature.minimum_value - 10)
5355

54-
with pytest.raises(ValueError):
56+
with pytest.raises(ValueError, match="out of range"):
5557
await feature.set_value(feature.maximum_value + 10)

kasa/tests/smart/features/test_colortemp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ async def test_colortemp_component(dev: SmartDevice):
2323
await dev.update()
2424
assert feature.value == new_value
2525

26-
with pytest.raises(ValueError):
26+
with pytest.raises(ValueError, match="out of range"):
2727
await feature.set_value(feature.minimum_value - 10)
2828

29-
with pytest.raises(ValueError):
29+
with pytest.raises(ValueError, match="out of range"):
3030
await feature.set_value(feature.maximum_value + 10)

kasa/tests/smart/modules/test_autooff.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
@autooff
2020
@pytest.mark.parametrize(
21-
"feature, prop_name, type",
21+
("feature", "prop_name", "type"),
2222
[
2323
("auto_off_enabled", "enabled", bool),
2424
("auto_off_minutes", "delay", int),

kasa/tests/smart/modules/test_contact.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
@contact
1212
@pytest.mark.parametrize(
13-
"feature, type",
13+
("feature", "type"),
1414
[
1515
("is_open", bool),
1616
],

kasa/tests/smart/modules/test_fan.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ async def test_fan_module(dev: SmartDevice, mocker: MockerFixture):
7676
await dev.update()
7777
assert not device.is_on
7878

79-
with pytest.raises(ValueError):
79+
with pytest.raises(ValueError, match="Invalid level"):
8080
await fan.set_fan_speed_level(-1)
8181

82-
with pytest.raises(ValueError):
82+
with pytest.raises(ValueError, match="Invalid level"):
8383
await fan.set_fan_speed_level(5)

kasa/tests/smart/modules/test_firmware.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@
1919

2020
@firmware
2121
@pytest.mark.parametrize(
22-
"feature, prop_name, type, required_version",
22+
("feature", "prop_name", "type", "required_version"),
2323
[
2424
("auto_update_enabled", "auto_update_enabled", bool, 2),
2525
("update_available", "update_available", bool, 1),
26-
("update_available", "update_available", bool, 1),
2726
("current_firmware_version", "current_firmware", str, 1),
2827
("available_firmware_version", "latest_firmware", str, 1),
2928
],

kasa/tests/smart/modules/test_humidity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
@humidity
1212
@pytest.mark.parametrize(
13-
"feature, type",
13+
("feature", "type"),
1414
[
1515
("humidity", int),
1616
("humidity_warning", bool),

kasa/tests/smart/modules/test_light_effect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async def test_light_effect(dev: Device, mocker: MockerFixture):
3737
assert light_effect.effect == effect
3838
assert feature.value == effect
3939

40-
with pytest.raises(ValueError):
40+
with pytest.raises(ValueError, match="The effect foobar is not a built in effect"):
4141
await light_effect.set_effect("foobar")
4242

4343

kasa/tests/smart/modules/test_light_strip_effect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async def test_light_strip_effect(dev: Device, mocker: MockerFixture):
5454
assert light_effect.effect == effect
5555
assert feature.value == effect
5656

57-
with pytest.raises(ValueError):
57+
with pytest.raises(ValueError, match="The effect foobar is not a built in effect"):
5858
await light_effect.set_effect("foobar")
5959

6060

kasa/tests/smart/modules/test_motionsensor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
@motion
1212
@pytest.mark.parametrize(
13-
"feature, type",
13+
("feature", "type"),
1414
[
1515
("motion_detected", bool),
1616
],

kasa/tests/smart/modules/test_temperature.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
@temperature
1818
@pytest.mark.parametrize(
19-
"feature, type",
19+
("feature", "type"),
2020
[
2121
("temperature", float),
2222
("temperature_unit", str),

kasa/tests/smart/modules/test_temperaturecontrol.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import re
23

34
import pytest
45

@@ -15,7 +16,7 @@
1516

1617
@thermostats_smart
1718
@pytest.mark.parametrize(
18-
"feature, type",
19+
("feature", "type"),
1920
[
2021
("target_temperature", float),
2122
("temperature_offset", int),
@@ -59,21 +60,29 @@ async def test_set_temperature_invalid_values(dev):
5960
"""Test that out-of-bounds temperature values raise errors."""
6061
temp_module: TemperatureControl = dev.modules["TemperatureControl"]
6162

62-
with pytest.raises(ValueError):
63+
with pytest.raises(
64+
ValueError, match="Invalid target temperature -1, must be in range"
65+
):
6366
await temp_module.set_target_temperature(-1)
6467

65-
with pytest.raises(ValueError):
68+
with pytest.raises(
69+
ValueError, match="Invalid target temperature 100, must be in range"
70+
):
6671
await temp_module.set_target_temperature(100)
6772

6873

6974
@thermostats_smart
7075
async def test_temperature_offset(dev):
7176
"""Test the temperature offset API."""
7277
temp_module: TemperatureControl = dev.modules["TemperatureControl"]
73-
with pytest.raises(ValueError):
78+
with pytest.raises(
79+
ValueError, match=re.escape("Temperature offset must be [-10, 10]")
80+
):
7481
await temp_module.set_temperature_offset(100)
7582

76-
with pytest.raises(ValueError):
83+
with pytest.raises(
84+
ValueError, match=re.escape("Temperature offset must be [-10, 10]")
85+
):
7786
await temp_module.set_temperature_offset(-100)
7887

7988
await temp_module.set_temperature_offset(5)
@@ -83,7 +92,7 @@ async def test_temperature_offset(dev):
8392

8493
@thermostats_smart
8594
@pytest.mark.parametrize(
86-
"mode, states, frost_protection",
95+
("mode", "states", "frost_protection"),
8796
[
8897
pytest.param(ThermostatState.Idle, [], False, id="idle has empty"),
8998
pytest.param(
@@ -114,7 +123,7 @@ async def test_thermostat_mode(dev, mode, states, frost_protection):
114123

115124
@thermostats_smart
116125
@pytest.mark.parametrize(
117-
"mode, states, msg",
126+
("mode", "states", "msg"),
118127
[
119128
pytest.param(
120129
ThermostatState.Heating,

kasa/tests/smart/modules/test_waterleak.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
@waterleak
1414
@pytest.mark.parametrize(
15-
"feature, prop_name, type",
15+
("feature", "prop_name", "type"),
1616
[
1717
("water_alert", "alert", int),
1818
("water_leak", "status", Enum),

kasa/tests/test_aestransport.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ async def test_login(mocker, status_code, error_code, inner_error_code, expectat
100100

101101

102102
@pytest.mark.parametrize(
103-
"inner_error_codes, expectation, call_count",
103+
("inner_error_codes", "expectation", "call_count"),
104104
[
105105
([SmartErrorCode.LOGIN_ERROR, 0, 0, 0], does_not_raise(), 4),
106106
(
@@ -298,7 +298,7 @@ async def test_unknown_errors(mocker, error_code):
298298
"requestID": 1,
299299
"terminal_uuid": "foobar",
300300
}
301-
with pytest.raises(KasaException):
301+
with pytest.raises(KasaException): # noqa: PT012
302302
res = await transport.send(json_dumps(request))
303303
assert res is SmartErrorCode.INTERNAL_UNKNOWN_ERROR
304304

@@ -315,7 +315,7 @@ async def test_port_override():
315315

316316

317317
@pytest.mark.parametrize(
318-
"device_delay_required, should_error, should_succeed",
318+
("device_delay_required", "should_error", "should_succeed"),
319319
[
320320
pytest.param(0, False, True, id="No error"),
321321
pytest.param(0.125, True, True, id="Error then succeed"),

0 commit comments

Comments
 (0)
0