Skip to content

Commit

Permalink
Drop python 3.6 support (#1263)
Browse files Browse the repository at this point in the history
* Drop python 3.6 support

* Replace 3.6 with 3.10 for CI
* Add pyupgrade with --py37-plus to pre-commit hooks, reformat files

* Require pytest >=6.2.5

Required for running on python 3.10 (pytest-dev/pytest#8540)

* Update lockfile

* Update pre-commit hooks
  • Loading branch information
rytilahti committed Dec 17, 2021
1 parent 8c41748 commit 68fc467
Show file tree
Hide file tree
Showing 24 changed files with 132 additions and 202 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- uses: "actions/checkout@v2"
Expand Down Expand Up @@ -55,7 +55,7 @@ jobs:

strategy:
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "pypy3"]
python-version: ["3.7", "3.8", "3.9", "3.10", "pypy3"]
os: [ubuntu-latest, macos-latest, windows-latest]
# test pypy3 only on ubuntu as cryptography requires rust compilation
# which slows the pipeline and was not currently working on macos
Expand Down
10 changes: 8 additions & 2 deletions .pre-commit-config.yaml
Expand Up @@ -12,7 +12,7 @@ repos:
- id: check-ast

- repo: https://github.com/psf/black
rev: 21.11b1
rev: 21.12b0
hooks:
- id: black
language_version: python3
Expand Down Expand Up @@ -48,7 +48,13 @@ repos:


- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.910-1
rev: v0.920
hooks:
- id: mypy
additional_dependencies: [types-attrs, types-PyYAML, types-requests, types-pytz, types-croniter]

- repo: https://github.com/asottile/pyupgrade
rev: v2.29.1
hooks:
- id: pyupgrade
args: ['--py37-plus']
1 change: 0 additions & 1 deletion docs/conf.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# python-miio documentation build configuration file, created by
# sphinx-quickstart on Wed Oct 18 03:50:00 2017.
Expand Down
8 changes: 2 additions & 6 deletions miio/airhumidifier_jsq.py
Expand Up @@ -208,9 +208,7 @@ def set_mode(self, mode: OperationMode):
"""Set mode."""
value = mode.value
if value not in (om.value for om in OperationMode):
raise AirHumidifierException(
"{} is not a valid OperationMode value".format(value)
)
raise AirHumidifierException(f"{value} is not a valid OperationMode value")

return self.send("set_mode", [value])

Expand All @@ -222,9 +220,7 @@ def set_led_brightness(self, brightness: LedBrightness):
"""Set led brightness."""
value = brightness.value
if value not in (lb.value for lb in LedBrightness):
raise AirHumidifierException(
"{} is not a valid LedBrightness value".format(value)
)
raise AirHumidifierException(f"{value} is not a valid LedBrightness value")

return self.send("set_brightness", [value])

Expand Down
4 changes: 1 addition & 3 deletions miio/airpurifier_airdog.py
Expand Up @@ -147,9 +147,7 @@ def off(self):
def set_mode_and_speed(self, mode: OperationMode, speed: int = 1):
"""Set mode and speed."""
if mode.value not in (om.value for om in OperationMode):
raise AirDogException(
"{} is not a valid OperationMode value".format(mode.value)
)
raise AirDogException(f"{mode.value} is not a valid OperationMode value")

if mode in [OperationMode.Auto, OperationMode.Idle]:
speed = 1
Expand Down
14 changes: 3 additions & 11 deletions miio/click_common.py
Expand Up @@ -7,7 +7,6 @@
import json
import logging
import re
import sys
from functools import partial, wraps
from typing import Callable, Set, Type, Union

Expand All @@ -17,13 +16,6 @@

from .exceptions import DeviceError

if sys.version_info < (3, 5):
click.echo(
"To use this script you need python 3.5 or newer, got %s" % (sys.version_info,)
)
sys.exit(1)


_LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -205,7 +197,7 @@ def wrap(self, ctx, func):
elif self.default_output:
output = self.default_output
else:
output = format_output("Running command {0}".format(self.command_name))
output = format_output(f"Running command {self.command_name}")

# Remove skip_autodetect before constructing the click.command
self.kwargs.pop("skip_autodetect", None)
Expand Down Expand Up @@ -235,7 +227,7 @@ def __init__(
chain=False,
result_callback=None,
result_callback_pass_device=True,
**attrs
**attrs,
):

self.commands = getattr(device_class, "_device_group_commands", None)
Expand All @@ -260,7 +252,7 @@ def __init__(
subcommand_metavar,
chain,
result_callback,
**attrs
**attrs,
)

def group_callback(self, ctx, *args, **kwargs):
Expand Down
14 changes: 7 additions & 7 deletions miio/cooker.py
Expand Up @@ -136,7 +136,7 @@ def temperatures(self) -> List[int]:

@property
def raw(self) -> str:
return "".join(["{:02x}".format(value) for value in self.data])
return "".join([f"{value:02x}" for value in self.data])

def __str__(self) -> str:
return str(self.data)
Expand Down Expand Up @@ -194,7 +194,7 @@ def favorite_cooking(self) -> time:
return time(hour=self.custom[10], minute=self.custom[11])

def __str__(self) -> str:
return "".join(["{:02x}".format(value) for value in self.custom])
return "".join([f"{value:02x}" for value in self.custom])


class CookingStage(DeviceStatus):
Expand Down Expand Up @@ -299,7 +299,7 @@ def lid_open_warning(self, timeout: int):
self.timeouts[2] = timeout

def __str__(self) -> str:
return "".join(["{:02x}".format(value) for value in self.timeouts])
return "".join([f"{value:02x}" for value in self.timeouts])


class CookerSettings(DeviceStatus):
Expand Down Expand Up @@ -429,7 +429,7 @@ def favorite_auto_keep_warm(self, auto_keep_warm: bool):
self.settings[1] &= 247

def __str__(self) -> str:
return "".join(["{:02x}".format(value) for value in self.settings])
return "".join([f"{value:02x}" for value in self.settings])


class CookerStatus(DeviceStatus):
Expand Down Expand Up @@ -678,9 +678,9 @@ def set_interaction(self, settings: CookerSettings, timeouts: InteractionTimeout
"set_interaction",
[
str(settings),
"{:x}".format(timeouts.led_off),
"{:x}".format(timeouts.lid_open),
"{:x}".format(timeouts.lid_open_warning),
f"{timeouts.led_off:x}",
f"{timeouts.lid_open:x}",
f"{timeouts.lid_open_warning:x}",
],
)

Expand Down
2 changes: 1 addition & 1 deletion miio/device.py
Expand Up @@ -264,7 +264,7 @@ def fail(x):

click.echo(f"Testing properties {properties} for {model}")
valid_properties = {}
max_property_len = max([len(p) for p in properties])
max_property_len = max(len(p) for p in properties)
for property in properties:
try:
click.echo(f"Testing {property:{max_property_len+2}} ", nl=False)
Expand Down
2 changes: 1 addition & 1 deletion miio/deviceinfo.py
Expand Up @@ -31,7 +31,7 @@ def __init__(self, data):
self.data = data

def __repr__(self):
return "%s v%s (%s) @ %s - token: %s" % (
return "{} v{} ({}) @ {} - token: {}".format(
self.model,
self.firmware_version,
self.mac_address,
Expand Down
2 changes: 1 addition & 1 deletion miio/discovery.py
Expand Up @@ -228,7 +228,7 @@ def get_addr_from_info(info):

def other_package_info(info, desc):
"""Return information about another package supporting the device."""
return "Found %s at %s, check %s" % (info.name, get_addr_from_info(info), desc)
return f"Found {info.name} at {get_addr_from_info(info)}, check {desc}"


def create_device(name: str, addr: str, device_cls: partial) -> Device:
Expand Down
4 changes: 2 additions & 2 deletions miio/extract_tokens.py
Expand Up @@ -182,7 +182,7 @@ def read_miio_database(tar):
try:
db = tar.extractfile(DBFILE)
except KeyError as ex:
click.echo("Unable to find miio database file %s: %s" % (DBFILE, ex))
click.echo(f"Unable to find miio database file {DBFILE}: {ex}")
return []
if write_to_disk:
file = write_to_disk
Expand All @@ -200,7 +200,7 @@ def read_yeelight_database(tar):
try:
db = tar.extractfile(DBFILE)
except KeyError as ex:
click.echo("Unable to find yeelight database file %s: %s" % (DBFILE, ex))
click.echo(f"Unable to find yeelight database file {DBFILE}: {ex}")
return []

return list(read_android_yeelight(db))
Expand Down
4 changes: 2 additions & 2 deletions miio/fan_miot.py
Expand Up @@ -329,7 +329,7 @@ def set_angle(self, angle: int):
if angle not in SUPPORTED_ANGLES[self.model]:
raise FanException(
"Unsupported angle. Supported values: "
+ ", ".join("{0}".format(i) for i in SUPPORTED_ANGLES[self.model])
+ ", ".join(f"{i}" for i in SUPPORTED_ANGLES[self.model])
)

return self.set_property("swing_mode_angle", angle)
Expand Down Expand Up @@ -754,7 +754,7 @@ def set_angle(self, angle: int):
if angle not in SUPPORTED_ANGLES[self.model]:
raise FanException(
"Unsupported angle. Supported values: "
+ ", ".join("{0}".format(i) for i in SUPPORTED_ANGLES[self.model])
+ ", ".join(f"{i}" for i in SUPPORTED_ANGLES[self.model])
)

return self.set_property("swing_mode_angle", angle)
Expand Down
4 changes: 2 additions & 2 deletions miio/gateway/devices/subdevice.py
Expand Up @@ -61,7 +61,7 @@ def __init__(
self.setter = model_info.get("setter")

def __repr__(self):
return "<Subdevice %s: %s, model: %s, zigbee: %s, fw: %s, bat: %s, vol: %s, props: %s>" % (
return "<Subdevice {}: {}, model: {}, zigbee: {}, fw: {}, bat: {}, vol: {}, props: {}>".format(
self.device_type,
self.sid,
self.model,
Expand Down Expand Up @@ -165,7 +165,7 @@ def get_property(self, property):

if not response:
raise GatewayException(
"Empty response while fetching property '%s': %s" % (property, response)
f"Empty response while fetching property '{property}': {response}"
)

return response
Expand Down
2 changes: 1 addition & 1 deletion miio/integrations/vacuum/roborock/vacuum.py
Expand Up @@ -857,7 +857,7 @@ def callback(ctx, *args, id_file, **kwargs):

start_id = manual_seq = 0
with contextlib.suppress(FileNotFoundError, TypeError, ValueError), open(
id_file, "r"
id_file
) as f:
x = json.load(f)
start_id = x.get("seq", 0)
Expand Down
36 changes: 15 additions & 21 deletions miio/integrations/vacuum/roborock/vacuum_cli.py
Expand Up @@ -36,9 +36,7 @@
def _read_config(file):
"""Return sequence id information."""
config = {"seq": 0, "manual_seq": 0}
with contextlib.suppress(FileNotFoundError, TypeError, ValueError), open(
file, "r"
) as f:
with contextlib.suppress(FileNotFoundError, TypeError, ValueError), open(file) as f:
config = json.load(f)

return config
Expand Down Expand Up @@ -144,10 +142,10 @@ def status(vac: RoborockVacuum):
def consumables(vac: RoborockVacuum):
"""Return consumables status."""
res = vac.consumable_status()
click.echo("Main brush: %s (left %s)" % (res.main_brush, res.main_brush_left))
click.echo("Side brush: %s (left %s)" % (res.side_brush, res.side_brush_left))
click.echo("Filter: %s (left %s)" % (res.filter, res.filter_left))
click.echo("Sensor dirty: %s (left %s)" % (res.sensor_dirty, res.sensor_dirty_left))
click.echo(f"Main brush: {res.main_brush} (left {res.main_brush_left})")
click.echo(f"Side brush: {res.side_brush} (left {res.side_brush_left})")
click.echo(f"Filter: {res.filter} (left {res.filter_left})")
click.echo(f"Sensor dirty: {res.sensor_dirty} (left {res.sensor_dirty_left})")


@cli.command()
Expand All @@ -170,9 +168,7 @@ def reset_consumable(vac: RoborockVacuum, name):
click.echo("Unexpected state name: %s" % name)
return

click.echo(
"Resetting consumable '%s': %s" % (name, vac.consumable_reset(consumable))
)
click.echo(f"Resetting consumable '{name}': {vac.consumable_reset(consumable)}")


@cli.command()
Expand Down Expand Up @@ -331,15 +327,13 @@ def dnd(
click.echo("Disabling DND..")
click.echo(vac.disable_dnd())
elif cmd == "on":
click.echo(
"Enabling DND %s:%s to %s:%s" % (start_hr, start_min, end_hr, end_min)
)
click.echo(f"Enabling DND {start_hr}:{start_min} to {end_hr}:{end_min}")
click.echo(vac.set_dnd(start_hr, start_min, end_hr, end_min))
else:
x = vac.dnd_status()
click.echo(
click.style(
"Between %s and %s (enabled: %s)" % (x.start, x.end, x.enabled),
f"Between {x.start} and {x.end} (enabled: {x.enabled})",
bold=x.enabled,
)
)
Expand Down Expand Up @@ -370,14 +364,14 @@ def timer(ctx, vac: RoborockVacuum):
color = "green" if timer.enabled else "yellow"
click.echo(
click.style(
"Timer #%s, id %s (ts: %s)" % (idx, timer.id, timer.ts),
f"Timer #{idx}, id {timer.id} (ts: {timer.ts})",
bold=True,
fg=color,
)
)
click.echo(" %s" % timer.cron)
min, hr, x, y, days = timer.cron.split(" ")
cron = "%s %s %s %s %s" % (min, hr, x, y, days)
cron = f"{min} {hr} {x} {y} {days}"
click.echo(" %s" % cron)


Expand Down Expand Up @@ -451,7 +445,7 @@ def cleaning_history(vac: RoborockVacuum):
"""Query the cleaning history."""
res = vac.clean_history()
click.echo("Total clean count: %s" % res.count)
click.echo("Cleaned for: %s (area: %s m²)" % (res.total_duration, res.total_area))
click.echo(f"Cleaned for: {res.total_duration} (area: {res.total_area} m²)")
if res.dust_collection_count is not None:
click.echo("Emptied dust collection bin: %s times" % res.dust_collection_count)
click.echo()
Expand Down Expand Up @@ -504,7 +498,7 @@ def install_sound(vac: RoborockVacuum, url: str, md5sum: str, sid: int, ip: str)
`--ip` can be used to override automatically detected IP address for
the device to contact for the update.
"""
click.echo("Installing from %s (md5: %s) for id %s" % (url, md5sum, sid))
click.echo(f"Installing from {url} (md5: {md5sum}) for id {sid}")

local_url = None
server = None
Expand All @@ -527,7 +521,7 @@ def install_sound(vac: RoborockVacuum, url: str, md5sum: str, sid: int, ip: str)
progress = vac.sound_install_progress()
while progress.is_installing:
progress = vac.sound_install_progress()
click.echo("%s (%s %%)" % (progress.state.name, progress.progress))
click.echo(f"{progress.state.name} ({progress.progress} %)")
time.sleep(1)

progress = vac.sound_install_progress()
Expand Down Expand Up @@ -641,7 +635,7 @@ def update_firmware(vac: RoborockVacuum, url: str, md5: str, ip: str):
click.echo("You need to pass md5 when using URL for updating.")
return

click.echo("Using %s (md5: %s)" % (url, md5))
click.echo(f"Using {url} (md5: {md5})")
else:
server = OneShotServer(url)
url = server.url(ip)
Expand Down Expand Up @@ -685,7 +679,7 @@ def raw_command(vac: RoborockVacuum, cmd, parameters):
params = [] # type: Any
if parameters:
params = ast.literal_eval(parameters)
click.echo("Sending cmd %s with params %s" % (cmd, params))
click.echo(f"Sending cmd {cmd} with params {params}")
click.echo(vac.raw_command(cmd, params))


Expand Down

0 comments on commit 68fc467

Please sign in to comment.