diff --git a/test/__init__.py b/test/__init__.py index 77631dcc..10b36a85 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# # This file is formatted with ruff format # # Run with pytest: @@ -20,16 +18,14 @@ # - WacomDevice, WacomDatabase, ...: pythonic wrappers around the # underlying C object. -from ctypes import c_char_p, c_char, c_int, c_uint32, c_void_p -from typing import Optional, Tuple, Type, List -from dataclasses import dataclass -from pathlib import Path - import ctypes import enum import itertools import logging - +from ctypes import c_char, c_char_p, c_int, c_uint32, c_void_p +from dataclasses import dataclass +from pathlib import Path +from typing import ClassVar logger = logging.getLogger(__name__) @@ -40,8 +36,8 @@ @dataclass class _Api: name: str - args: Tuple[Type[ctypes._SimpleCData], ...] - return_type: Optional[Type[ctypes._SimpleCData]] + args: tuple[type[ctypes._SimpleCData], ...] + return_type: type[ctypes._SimpleCData] | None @property def basename(self) -> str: @@ -61,7 +57,7 @@ def basename(self) -> str: class GlibC: _lib = None - _api_prototypes: List[_Api] = [ + _api_prototypes: ClassVar[list[_Api]] = [ _Api(name="free", args=(c_void_p,), return_type=None), ] @@ -127,7 +123,7 @@ def instance(cls): cls._load() return cls - _api_prototypes: List[_Api] = [ + _api_prototypes: ClassVar[list[_Api]] = [ _Api(name="libwacom_error_new", args=(c_void_p,), return_type=c_void_p), _Api(name="libwacom_error_free", args=(c_void_p,), return_type=None), _Api(name="libwacom_error_get_code", args=(c_void_p,), return_type=c_int), @@ -329,7 +325,7 @@ def instance(cls): ), ] - _enums: List[_Enum] = [ + _enums: ClassVar[list[_Enum]] = [ _Enum(name="WERROR_NONE", value=0), _Enum(name="WERROR_BAD_ALLOC", value=1), _Enum(name="WERROR_INVALID_PATH", value=2), @@ -439,12 +435,12 @@ def wrapper(func): setattr(self, api.basename.removeprefix("match_"), wrapper(func)) @property - def name(self) -> Optional[str]: + def name(self) -> str | None: name = self.get_name() return name.decode("utf-8") if name else None @property - def uniq(self) -> Optional[str]: + def uniq(self) -> str | None: uniq = self.get_uniq() return uniq.decode("utf-8") if uniq else None @@ -482,23 +478,23 @@ def wrapper(func): setattr(self, api.basename.removeprefix("builder_"), wrapper(func)) @property - def device_name(self) -> Optional[str]: + def device_name(self) -> str | None: return self._device_name @property - def match_name(self) -> Optional[str]: + def match_name(self) -> str | None: return self._match_name @property - def uniq(self) -> Optional[str]: + def uniq(self) -> str | None: return self._uniq @property - def bustype(self) -> Optional[WacomBustype]: + def bustype(self) -> WacomBustype | None: return self._bustype @property - def usbid(self) -> Optional[Tuple[int, int]]: + def usbid(self) -> tuple[int, int] | None: return self._usbid @bustype.setter @@ -507,7 +503,7 @@ def bustype(self, bus: WacomBustype): self.set_bustype(bus.value) @usbid.setter - def usbid(self, usbid: Tuple[int, int]): + def usbid(self, usbid: tuple[int, int]): self._usbid = usbid self.set_usbid(usbid[0], usbid[1]) @@ -529,11 +525,11 @@ def uniq(self, uniq: str): @classmethod def create( cls, - device_name: Optional[str] = None, - match_name: Optional[str] = None, - uniq: Optional[str] = None, - usbid: Optional[Tuple[int, int]] = None, - bus: Optional[WacomBustype] = None, + device_name: str | None = None, + match_name: str | None = None, + uniq: str | None = None, + usbid: tuple[int, int] | None = None, + bus: WacomBustype | None = None, ) -> "WacomBuilder": lib = LibWacom.instance() builder = WacomBuilder(lib.builder_new()) @@ -660,7 +656,7 @@ def stylus_type(self) -> WacomStylusType: def eraser_type(self) -> WacomEraserType: return WacomEraserType(self.get_eraser_type()) - def get_paired_styli(self) -> List["WacomStylus"]: + def get_paired_styli(self) -> list["WacomStylus"]: lib = LibWacom.instance() paired = lib.stylus_get_paired_styli(self.stylus, None) styli = [ @@ -705,7 +701,7 @@ class ButtonFlags(enum.IntEnum): DIAL2_MODESWITCH = 1 << 11 @staticmethod - def modeswitch_flags() -> List["WacomDevice.ButtonFlags"]: + def modeswitch_flags() -> list["WacomDevice.ButtonFlags"]: return [ WacomDevice.ButtonFlags.RING_MODESWITCH, WacomDevice.ButtonFlags.RING2_MODESWITCH, @@ -745,12 +741,12 @@ def wrapper(func): val = getattr(lib, e.basename) setattr(self, e.basename, val) - def get_paired_device(self) -> Optional[WacomMatch]: + def get_paired_device(self) -> WacomMatch | None: lib = LibWacom.instance() match = lib.get_paired_device(self.device) return WacomMatch(match) if match else None - def get_matches(self) -> List[WacomMatch]: + def get_matches(self) -> list[WacomMatch]: lib = LibWacom.instance() matches = lib.get_matches(self.device) @@ -759,7 +755,7 @@ def get_matches(self) -> List[WacomMatch]: for m in itertools.takewhile(lambda ptr: ptr is not None, matches) ] - def get_styli(self) -> List[WacomStylus]: + def get_styli(self) -> list[WacomStylus]: lib = LibWacom.instance() styli = lib.get_styli(self.device, None) @@ -774,7 +770,7 @@ def __del__(self): lib.destroy(self.device) @property - def paired_device(self) -> Optional[WacomMatch]: + def paired_device(self) -> WacomMatch | None: return self.get_paired_device() @property @@ -864,11 +860,11 @@ def matches(self): return self.get_matches() @property - def integration_flags(self) -> List[IntegrationFlags]: + def integration_flags(self) -> list[IntegrationFlags]: flags = self.get_integration_flags() return [f for f in WacomDevice.IntegrationFlags if f & flags != 0] - def button_flags(self, button: str) -> List[ButtonFlags]: + def button_flags(self, button: str) -> list[ButtonFlags]: flags = self.get_button_flag(button.encode("utf-8")) return [f for f in WacomDevice.ButtonFlags if f & flags != 0] @@ -879,11 +875,11 @@ def button_modeswitch_mode(self, button: str) -> ModeSwitch: mode = self.get_button_modeswitch_mode(button.encode("utf-8")) return WacomDevice.ModeSwitch(mode) - def button_led_group(self, button: str) -> List[ButtonFlags]: + def button_led_group(self, button: str) -> list[ButtonFlags]: return self.get_button_led_group(button.encode("utf-8")) @property - def status_leds(self) -> List["WacomStatusLed"]: + def status_leds(self) -> list["WacomStatusLed"]: nleds = c_int() leds = self.get_status_leds(ctypes.byref(nleds)) @@ -899,7 +895,7 @@ class Fallback(enum.IntEnum): NONE = 0x0 GENERIC = 0x1 - def __init__(self, path: Optional[Path] = None): + def __init__(self, path: Path | None = None): lib = LibWacom.instance() if path is None: self.db = lib.database_new() # type: ignore @@ -926,27 +922,27 @@ def __del__(self): lib = LibWacom.instance() lib.database_destroy(db) - def new_from_name(self, name: str) -> Optional[WacomDevice]: + def new_from_name(self, name: str) -> WacomDevice | None: device = self.libwacom_new_from_name(name.encode("utf-8"), 0) return WacomDevice(device) if device else None def new_from_path( self, path: str, fallback: Fallback = Fallback.NONE - ) -> Optional[WacomDevice]: + ) -> WacomDevice | None: device = self.libwacom_new_from_path(path.encode("utf-8"), fallback, 0) return WacomDevice(device) if device else None - def new_from_usbid(self, vid: int, pid: int) -> Optional[WacomDevice]: + def new_from_usbid(self, vid: int, pid: int) -> WacomDevice | None: device = self.libwacom_new_from_usbid(vid, pid, 0) return WacomDevice(device) if device else None def new_from_builder( self, builder: WacomBuilder, fallback: Fallback = Fallback.NONE - ) -> Optional[WacomDevice]: + ) -> WacomDevice | None: device = self.libwacom_new_from_builder(builder.builder, fallback.value, 0) return WacomDevice(device) if device else None - def list_devices(self) -> List[WacomDevice]: + def list_devices(self) -> list[WacomDevice]: devices = self.libwacom_list_devices_from_database(self.db, 0) devs = [ WacomDevice(d, destroy=False) @@ -955,7 +951,7 @@ def list_devices(self) -> List[WacomDevice]: GlibC.instance().free(devices) return devs - def list_styli(self) -> List[WacomStylus]: + def list_styli(self) -> list[WacomStylus]: styli = self.libwacom_list_styli_from_database(self.db, 0) result = [ WacomStylus(s) diff --git a/test/conftest.py b/test/conftest.py index da48b7a7..6eb69109 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,11 +1,9 @@ -#!/usr/bin/env python3 -# # This file is formatted with ruff format -# -from pathlib import Path import logging import os +from pathlib import Path + import pytest from . import WacomDatabase diff --git a/test/test_data_files.py b/test/test_data_files.py index 4cff174f..30d426f3 100755 --- a/test/test_data_files.py +++ b/test/test_data_files.py @@ -7,7 +7,6 @@ import re from pathlib import Path - WACOM_RECEIVER_USBIDS = [ (0x56A, 0x84), ] diff --git a/test/test_libwacom.py b/test/test_libwacom.py index 37171c81..fd49ec1c 100644 --- a/test/test_libwacom.py +++ b/test/test_libwacom.py @@ -1,14 +1,12 @@ -#!/usr/bin/env python3 -# # This file is formatted with ruff format +import ctypes +import logging +import string from configparser import ConfigParser from dataclasses import dataclass, field -import ctypes -import logging import pytest -import string from . import ( WacomAxisType, @@ -97,8 +95,9 @@ def write_to_dir(self, dir, filename="libwacom.stylus"): if logger.getEffectiveLevel() == logging.DEBUG: logger.debug(f"{dir}/{filename}:") - for line in open(dir / filename).readlines(): - logger.debug(f" {line.rstrip()}") + with open(dir / filename) as f: + for line in f: + logger.debug(f" {line.rstrip()}") @dataclass @@ -148,8 +147,9 @@ def write_to(self, filename): if logger.getEffectiveLevel() == logging.DEBUG: logger.debug(f"{filename}:") - for line in open(filename).readlines(): - logger.debug(f" {line.rstrip()}") + with open(filename) as f: + for line in f: + logger.debug(f" {line.rstrip()}") @pytest.fixture() diff --git a/test/test_svg.py b/test/test_svg.py index 9e1670e9..7fbae49b 100644 --- a/test/test_svg.py +++ b/test/test_svg.py @@ -1,15 +1,12 @@ -#!/usr/bin/env python3 -# # Run with pytest -from typing import Optional, List -from pathlib import Path -from dataclasses import dataclass -import xml.etree - -import os import logging +import os import string +import xml.etree +from dataclasses import dataclass +from pathlib import Path + import pytest from . import WacomDatabase, WacomDevice @@ -45,7 +42,7 @@ class SvgDevice: svg: xml.etree.ElementTree is_autogenerated: bool - def has_item(self, id: str, classes: Optional[List[str]] = None): + def has_item(self, id: str, classes: list[str] | None = None): root = self.svg.getroot() nodes = root.findall(f".//*[@id='{id}']") assert nodes, f"Failed to find required element with id {id}" @@ -87,7 +84,7 @@ def pytest_generate_tests(metafunc): ] devices = sorted(devices, key=lambda d: d.name) - def filenames(devices: List[SvgDevice]) -> List[str]: + def filenames(devices: list[SvgDevice]) -> list[str]: return [Path(d.device.layout_filename).name for d in devices] if "svgdevice" in metafunc.fixturenames: @@ -130,7 +127,7 @@ def test_svg_maybe_not_needed(svgdevice): ) -def has_item(root, id: str, classes: Optional[List[str]] = None): +def has_item(root, id: str, classes: list[str] | None = None): nodes = root.findall(f".//*[@id='{id}']") assert nodes, f"Failed to find required element with id {id}" assert len(nodes) == 1, f"Expected on element with id {id}, have {len(nodes)}" @@ -193,7 +190,7 @@ def test_svg_strips(stripdevice): except AssertionError as e: if stripdevice.is_autogenerated: pytest.skip(f"Autogenerated device has errors in SVG: {e}") - raise e + raise def test_svg_dials(dialdevice): @@ -224,7 +221,7 @@ def test_svg_dials(dialdevice): except AssertionError as e: if dialdevice.is_autogenerated: pytest.skip(f"Autogenerated device has errors in SVG: {e}") - raise e + raise def test_svg_button(buttondevice): diff --git a/test/test_udev_rules.py b/test/test_udev_rules.py index b0d023ab..9239e597 100644 --- a/test/test_udev_rules.py +++ b/test/test_udev_rules.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# # This test will only work where /dev/uinput is available. # This test will reload the hwdb and udev rules # @@ -9,13 +7,16 @@ # - check if that device has the udev properties set we expect import configparser +import logging import os +import shutil +import subprocess +import sys from pathlib import Path + import pytest -import logging -import sys -import subprocess -import shutil + +logger = logging.getLogger(__name__) @pytest.fixture(scope="session", autouse=True) @@ -42,13 +43,13 @@ def systemd_reload(): subprocess.run(["systemd-hwdb", "update"], check=True) - except (IOError, FileNotFoundError, subprocess.CalledProcessError) as e: + except (OSError, FileNotFoundError, subprocess.CalledProcessError) as e: # If any of the commands above are not found (most likely the system # simply does not use systemd), just skip. - logging.critical(f"{e}") + logger.critical(f"{e}") pytest.skip(f"Skipping test: {e}") - except Exception as e: - logging.critical(f"{e}") + except Exception as e: # noqa: BLE001 + logger.critical(f"{e}") pytest.fail(f"Aborting test: {e}") @@ -67,7 +68,7 @@ def pytest_generate_tests(metafunc): want_pad = config["Device"].get("Buttons", 0) want_finger = config["Features"].get("Touch") == "true" integrated_in = config["Device"].get("IntegratedIn", "").split(";") - is_touchscreen = set(integrated_in) & set(["Display", "System"]) + is_touchscreen = set(integrated_in) & {"Display", "System"} for match in config["Device"]["DeviceMatch"].split(";"): if not match or match == "generic": @@ -80,16 +81,16 @@ def pytest_generate_tests(metafunc): try: vid = int(vid, 16) pid = int(pid, 16) - except ValueError as e: + except ValueError: print(f"Invalid vid/pid in {match} in {f}", file=sys.stderr) - raise e + raise if bus == "usb": bus = 0x3 elif bus == "bluetooth": bus = 0x5 - class Tablet(object): + class Tablet: def __init__(self, name, bus, vid, pid, is_touchscreen=False): self.name = name self.bus = bus @@ -119,14 +120,14 @@ def test_hwdb_files(tablet): # Note: the actual name doesn't really matter, all our hwdb files use either "*" # or "* Finger", etc. It does matter for that "Finger" suffix though. query = f"libwacom:name:{tablet.name}:input:b{tablet.bus:04X}v{tablet.vid:04X}p{tablet.pid:04X}" - logging.debug(query) + logger.debug(query) r = subprocess.run( ["systemd-hwdb", "query", query], check=True, capture_output=True ) stdout = r.stdout.decode("utf-8").strip() assert stdout, f"No output recorded for query {query}" - logging.debug(stdout) + logger.debug(stdout) props = {} for line in filter(lambda line: len(line) > 1, stdout.split("\n")): print(line) @@ -149,6 +150,7 @@ def test_hwdb_files(tablet): assert "ID_INPUT_TOUCHPAD" in props # For the Wacom Bamboo Pad we check for "Pad Pad" in the device name - if "Pad" in tablet.name: - if "Wacom Bamboo Pad" not in tablet.name or "Pad Pad" in tablet.name: - assert "ID_INPUT_TABLET_PAD" in props + if "Pad" in tablet.name and ( + "Wacom Bamboo Pad" not in tablet.name or "Pad Pad" in tablet.name + ): + assert "ID_INPUT_TABLET_PAD" in props diff --git a/tools/clean_svg.py b/tools/clean_svg.py index 1163b3d2..24be0519 100755 --- a/tools/clean_svg.py +++ b/tools/clean_svg.py @@ -20,8 +20,8 @@ import logging import sys -from pathlib import Path from argparse import ArgumentParser +from pathlib import Path from xml.etree import ElementTree as ET logging.basicConfig(level=logging.INFO) @@ -84,8 +84,7 @@ def round_if_number(value): def remove_non_svg_nodes_and_strip_namespace(root): - if root.tag.startswith(BRACKETS_NAMESPACE): - root.tag = root.tag[len(BRACKETS_NAMESPACE) :] + root.tag = root.tag.removeprefix(BRACKETS_NAMESPACE) for elem in list(root): if ( not elem.tag.startswith(BRACKETS_NAMESPACE) @@ -109,7 +108,7 @@ def remove_transform_if_exists(node): values = transform[len(TRANSLATE) + 1 : -1].split(",") try: x, y = float(values[0]), float(values[1]) - except Exception: + except Exception: # noqa: BLE001 return apply_translation(node, 1, 0, 0, 1, x, y) @@ -117,7 +116,7 @@ def remove_transform_if_exists(node): values = transform[len(MATRIX) + 1 : -1].split(",") try: a, b, c, d, e, f = [float(value.strip()) for value in values] - except Exception: + except Exception: # noqa: BLE001 return apply_translation(node, a, b, c, d, e, f) apply_scaling(node, a, d) @@ -137,8 +136,8 @@ def apply_translation(node, a, b, c, d, e, f): new_y = x * b + y * d + 1 * f node.attrib[x_attr] = str(new_x) node.attrib[y_attr] = str(new_y) - except Exception: - pass + except Exception: # noqa:BLE001 + print(f"Failed to apply translation: {e}", file=sys.stderr) def apply_translation_to_path(node, x, y): @@ -183,8 +182,8 @@ def apply_scaling(node, x, y): h = float(node.attrib[h_attr]) node.attrib[w_attr] = str(w * x) node.attrib[h_attr] = str(h * y) - except Exception: - pass + except Exception as e: # noqa: BLE001 + print(f"Failed to apply scaling: {e}", file=sys.stderr) def to_string_rec(node, level=0): @@ -202,7 +201,7 @@ def to_string_rec(node, level=0): for attr in get_node_attrs_sorted(node): attr_value = node.attrib.get(attr) if attr_value is not None: - attribs.append(indent + ' %s="%s"' % (attr, attr_value)) + attribs.append(indent + f' {attr}="{attr_value}"') string = indent + "<" + tag_name + "".join(attribs) if len(node) or node.text: @@ -215,7 +214,7 @@ def to_string_rec(node, level=0): for child in get_node_children_sorted(node): string += to_string_rec(child, level + 1) string += indent - string += "" % tag_name + string += f"" else: string += " />" return string @@ -269,20 +268,20 @@ def apply_id_and_class_from_group(group_node): if child.tag == "rect" or child.tag == "circle": if button_assigned: continue - child.attrib["id"] = "Button%s" % _id - child.attrib["class"] = "%s Button" % _id + child.attrib["id"] = f"Button{_id}" + child.attrib["class"] = f"{_id} Button" button_assigned = True elif child.tag == "path": if path_assigned: continue - child.attrib["id"] = "Leader%s" % _id - child.attrib["class"] = "%s Leader" % _id + child.attrib["id"] = f"Leader{_id}" + child.attrib["class"] = f"{_id} Leader" path_assigned = True elif child.tag == "text": if label_assigned: continue - child.attrib["id"] = "Label%s" % _id - child.attrib["class"] = "%s Label" % _id + child.attrib["id"] = f"Label{_id}" + child.attrib["class"] = f"{_id} Label" child.text = _id label_assigned = True @@ -359,7 +358,7 @@ def clean_svg(root, tabletname): ET.register_namespace("", NAMESPACE) try: tree = ET.parse(svgfile) - except Exception as e: + except Exception as e: # noqa: BLE001 sys.stderr.write(str(e) + "\n") sys.exit(1) root = tree.getroot() diff --git a/tools/libwacom-update-db.py b/tools/libwacom-update-db.py index 85a8ec38..d8ed3f00 100755 --- a/tools/libwacom-update-db.py +++ b/tools/libwacom-update-db.py @@ -24,8 +24,8 @@ import argparse import configparser import os -import sys import subprocess +import sys import tempfile from pathlib import Path @@ -34,7 +34,7 @@ def xdg_dir(): return Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) / "libwacom" -class Tablet(object): +class Tablet: def __init__(self, name, bus, vid, pid): self.name = name self.bus = bus @@ -46,7 +46,7 @@ def __init__(self, name, bus, vid, pid): # We have everything in strings so let's use that for sorting later # This will sort bluetooth before usb but meh - self.cmpstr = ":".join((bus, vid, pid, name)) + self.cmpstr = f"{bus}:{vid}:{pid}:{name}" def __lt__(self, other): return self.cmpstr < other.cmpstr @@ -137,9 +137,9 @@ def _load(self, path): # for tablets with re-used usbids and that doesn't matter try: bus, vid, pid, *_ = match.split("|") - except ValueError as e: + except ValueError: print(f"Failed to process match {match} in {file}", file=sys.stderr) - raise e + raise name = config["Device"]["Name"] t = Tablet(name, bus, vid, pid) diff --git a/tools/show-stylus.py b/tools/show-stylus.py index 061e45e5..5645d171 100755 --- a/tools/show-stylus.py +++ b/tools/show-stylus.py @@ -30,7 +30,7 @@ import libevdev import pyudev except ModuleNotFoundError as e: - print("Error: {}".format(str(e)), file=sys.stderr) + print(f"Error: {e!s}", file=sys.stderr) print( "One or more python modules are missing. Please install those " "modules and re-run this tool." @@ -92,9 +92,7 @@ def record_events(ns): if not d.absinfo[libevdev.EV_ABS.ABS_MISC]: die("Device only supports generic styli") - tool_bits = set( - c for c in libevdev.EV_KEY.codes if c.name.startswith("BTN_TOOL_") - ) + tool_bits = {c for c in libevdev.EV_KEY.codes if c.name.startswith("BTN_TOOL_")} styli = {} # dict of (type, serial) = proximity_state current_type, current_serial = 0, 0 in_prox = False @@ -132,7 +130,7 @@ def record_events(ns): except KeyboardInterrupt: print("Terminating") - return [s[0] for s in styli.keys()] + return [s[0] for s in styli] def load_data_files(): @@ -164,7 +162,7 @@ def load_data_files(): for stylus_id in config.sections(): ids = stylus_id.split(":") if len(ids) > 1: - _, sid = map(lambda x: int(x, 16), ids) + _, sid = (int(x, 16) for x in ids) else: _ = 0x56A # vid sid = int(ids[0], 16)