From 504afdea4ab38a399e7d1eba0d3a672b51c4874d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 9 Dec 2024 09:28:44 +1000 Subject: [PATCH] test: drop the use of attr All our uses can be done with dataclasses so we don't need an external package. Part-of: --- .gitlab-ci.yml | 2 +- .gitlab-ci/ci.template | 2 +- test/eiproto.py.tmpl | 60 ++++++++++++++++++++----------------- test/meson.build | 2 +- test/test_oeffis.py | 16 +++++----- test/test_protocol.py | 68 ++++++++++++++++++++---------------------- 6 files changed, 76 insertions(+), 74 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1b90a5f..09cbcf1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -296,7 +296,7 @@ abicheck@fedora:40: meson compile -C _build meson install -C _build popd - - pip install attrs + - pip install attrs # required by libei 1.0.0 script: - git remote add upstream$CI_JOB_ID https://gitlab.freedesktop.org/$FDO_UPSTREAM_REPO - git fetch --tags upstream$CI_JOB_ID diff --git a/.gitlab-ci/ci.template b/.gitlab-ci/ci.template index a95f9d1..ec118b4 100644 --- a/.gitlab-ci/ci.template +++ b/.gitlab-ci/ci.template @@ -296,7 +296,7 @@ abicheck@{{distro.name}}:{{version}}: meson compile -C _build meson install -C _build popd - - pip install attrs + - pip install attrs # required by libei 1.0.0 script: - git remote add upstream$CI_JOB_ID https://gitlab.freedesktop.org/$FDO_UPSTREAM_REPO - git fetch --tags upstream$CI_JOB_ID diff --git a/test/eiproto.py.tmpl b/test/eiproto.py.tmpl index 33fbbbf..684b8e0 100644 --- a/test/eiproto.py.tmpl +++ b/test/eiproto.py.tmpl @@ -20,12 +20,12 @@ from typing import Any, Callable, Generator, Tuple from enum import IntEnum +from dataclasses import dataclass, field try: from enum import StrEnum except ImportError: from strenum import StrEnum -import attr import binascii import itertools import logging @@ -51,19 +51,24 @@ def hexlify(data): return binascii.hexlify(data, sep=" ", bytes_per_sep=4) -@attr.s +class ObjectId(int): + def __repr__(self) -> str: + return f"{self:#x}" + + +@dataclass class MethodCall: - name: str = attr.ib() - args: dict[str, Any] = attr.ib() - objects: dict[str, "Interface"] = attr.ib(default=attr.Factory(dict)) - timestamp: float = attr.ib(default=attr.Factory(time.time)) + name: str + args: dict[str, Any] + objects: dict[str, "Interface"] = field(default_factory=dict) + timestamp: float = field(default_factory=time.time) -@attr.s +@dataclass class MessageHeader: - object_id: int = attr.ib(repr=lambda id: f"{id:#x}") - msglen: int = attr.ib() - opcode: int = attr.ib() + object_id: ObjectId + msglen: int + opcode: int @classmethod def size(cls) -> int: @@ -72,22 +77,21 @@ class MessageHeader: @classmethod def from_data(cls, data: bytes) -> "MessageHeader": object_id, msglen, opcode = struct.unpack("=QII", data[:cls.size()]) - return cls(object_id, msglen, opcode) + return cls(ObjectId(object_id), msglen, opcode) @property def as_tuple(self) -> Tuple[int, int, int]: return self.object_id, self.msglen, self.opcode -@attr.s +@dataclass class Context: - objects: dict[str, "Interface"] = attr.ib(default=attr.Factory(dict)) - _callbacks: dict[str, dict[int, Callable]] = attr.ib(init=False) - _ids: Generator = attr.ib(init=False, default=attr.Factory(itertools.count)) - - @_callbacks.default # type: ignore - def _default_callbacks(self) -> dict[str, dict[int, Callable]]: - return { "register": {}, "unregister": {}} + objects: dict[str, "Interface"] = field(default_factory=dict) + _callbacks: dict[str, dict[int, Callable]] = field( + init=False, + default_factory=lambda: { "register": {}, "unregister": {}} + ) + _ids: Generator = field(init=False, default_factory=itertools.count) def register(self, object: "Interface") -> None: assert object.object_id not in self.objects @@ -147,15 +151,15 @@ class InterfaceName(StrEnum): {% endfor %} -@attr.s(eq=False) +@dataclass(eq=False) class Interface: - object_id: int = attr.ib(repr=lambda id: f"{id:#x}") - version: int = attr.ib() - callbacks: dict[str, Callable] = attr.ib(init=False, default=attr.Factory(dict), repr=False) - calllog: list[MethodCall] = attr.ib(init=False, default=attr.Factory(list), repr=False) - name: str = attr.ib(default="") - incoming: dict[int, str] = attr.ib(default=attr.Factory(list), repr=False) - outgoing: dict[int, str] = attr.ib(default=attr.Factory(list), repr=False) + object_id: int + version: int + callbacks: dict[str, Callable] = field(init=False, default_factory=dict, repr=False) + calllog: list[MethodCall] = field(init=False, default_factory=list, repr=False) + name: str = field(default="") + incoming: dict[int, str] = field(default_factory=list, repr=False) + outgoing: dict[int, str] = field(default_factory=list, repr=False) def format(self, *args, opcode: int, signature: str) -> bytes: encoding = ["=QII"] @@ -252,7 +256,7 @@ class Interface: {% for interface in interfaces %} -@attr.s +@dataclass class {{interface.camel_name}}(Interface): {% for enum in interface.enums %} class {{component.capitalize()}}{{enum.camel_name}}(IntEnum): diff --git a/test/meson.build b/test/meson.build index 59165b1..294d18e 100644 --- a/test/meson.build +++ b/test/meson.build @@ -144,7 +144,7 @@ endif # Python-based tests pymod = import('python') -required_python_modules = ['pytest', 'attr', 'structlog'] +required_python_modules = ['pytest', 'structlog'] python = pymod.find_installation('python3', required: get_option('tests')) if python.found() and python.language_version().version_compare('< 3.11') required_python_modules += ['strenum'] diff --git a/test/test_oeffis.py b/test/test_oeffis.py index 03d5207..04a00cc 100644 --- a/test/test_oeffis.py +++ b/test/test_oeffis.py @@ -49,8 +49,8 @@ from ctypes import c_char_p, c_int, c_uint32, c_void_p from typing import Iterator, List, Tuple, Type, Optional, TextIO from gi.repository import GLib # type: ignore from dbus.mainloop.glib import DBusGMainLoop +from dataclasses import dataclass -import attr import ctypes import dbus import dbus.proxies @@ -82,21 +82,21 @@ def version_at_least(have, required) -> bool: return True -@attr.s +@dataclass class _Api: - name: str = attr.ib() - args: Tuple[Type[ctypes._SimpleCData], ...] = attr.ib() - return_type: Optional[Type[ctypes._SimpleCData]] = attr.ib() + name: str + args: Tuple[Type[ctypes._SimpleCData], ...] + return_type: Optional[Type[ctypes._SimpleCData]] @property def basename(self) -> str: return self.name[len(PREFIX) :] -@attr.s +@dataclass class _Enum: - name: str = attr.ib() - value: int = attr.ib() + name: str + value: int @property def basename(self) -> str: diff --git a/test/test_protocol.py b/test/test_protocol.py index 1f24d77..71f3598 100644 --- a/test/test_protocol.py +++ b/test/test_protocol.py @@ -23,8 +23,8 @@ from functools import reduce from typing import Generator, Optional from pathlib import Path +from dataclasses import dataclass, field -import attr import itertools import os import pytest @@ -121,17 +121,17 @@ def eis(socketpath, eis_executable) -> Generator["Eis", None, None]: eis.terminate() -@attr.s +@dataclass class Ei: - sock: socket.socket = attr.ib() - context: Context = attr.ib() - connection: Optional[EiConnection] = attr.ib(default=None) - interface_versions: dict[str, int] = attr.ib(init=False, default=attr.Factory(dict)) - seats: list[EiSeat] = attr.ib(init=False, default=attr.Factory(list)) - object_ids: Generator[int, None, None] = attr.ib( - init=False, default=attr.Factory(lambda: itertools.count(3)) + sock: socket.socket + context: Context + connection: Optional[EiConnection] = None + interface_versions: dict[str, int] = field(init=False, default_factory=dict) + seats: list[EiSeat] = field(init=False, default_factory=list) + object_ids: Generator[int, None, None] = field( + init=False, default_factory=lambda: itertools.count(3) ) - _data: bytes = attr.ib(init=False, default=attr.Factory(bytes)) # type: ignore + _data: bytes = field(init=False, default_factory=bytes) @property def data(self) -> bytes: @@ -314,12 +314,12 @@ class Ei: return ei -@attr.s +@dataclass class Eis: - process: Optional[subprocess.Popen] = attr.ib() - ei: Ei = attr.ib() - _stdout: Optional[str] = attr.ib(init=False, default=None) - _stderr: Optional[str] = attr.ib(init=False, default=None) + process: Optional[subprocess.Popen] + ei: Ei + _stdout: Optional[str] = field(init=False, default=None) + _stderr: Optional[str] = field(init=False, default=None) def terminate(self) -> None: if self.process is None: @@ -829,12 +829,12 @@ class TestEiProtocol: if iname != missing_interface: ei.send(setup.InterfaceVersion(iname, VERSION_V(1))) - @attr.s + @dataclass class Status: - connected: bool = attr.ib(default=False) - disconnected: bool = attr.ib(default=False) - seats: bool = attr.ib(default=False) - devices: bool = attr.ib(default=False) + connected: bool = False + disconnected: bool = False + seats: bool = False + devices: bool = False status = Status() @@ -943,11 +943,11 @@ class TestEiProtocol: ei.dispatch() ei.wait_for_connection() - @attr.s + @dataclass class Status: - disconnected: bool = attr.ib(default=False) - reason: int = attr.ib(default=0) - explanation: Optional[str] = attr.ib(default=None) + disconnected: bool = False + reason: int = 0 + explanation: Optional[str] = None status = Status() @@ -999,11 +999,11 @@ class TestEiProtocol: ei.dispatch() ei.wait_for_connection() - @attr.s + @dataclass class Status: - disconnected: bool = attr.ib(default=False) - reason: int = attr.ib(default=0) - explanation: Optional[str] = attr.ib(default=None) + disconnected: bool = False + reason: int = 0 + explanation: Optional[str] = None status = Status() @@ -1039,9 +1039,9 @@ class TestEiProtocol: """ ei = eis.ei - @attr.s + @dataclass class Status: - capability: Optional[Interface] = attr.ib(default=None) # type: ignore + capability: Optional[Interface] = None # type: ignore status = Status() @@ -1101,12 +1101,10 @@ class TestEiProtocol: """ ei = eis.ei - @attr.s + @dataclass class Status: - pointers: dict[InterfaceName, Interface] = attr.ib( - default=attr.Factory(dict) - ) # type: ignore - all_caps: int = attr.ib(default=0) + pointers: dict[InterfaceName, Interface] = field(default_factory=dict) + all_caps: int = 0 status = Status()