test: drop the use of attr

All our uses can be done with dataclasses so we don't need an external
package.

Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/319>
This commit is contained in:
Peter Hutterer 2024-12-09 09:28:44 +10:00
parent f2811418dd
commit 504afdea4a
6 changed files with 76 additions and 74 deletions

View file

@ -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

View file

@ -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

View file

@ -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="<overridden by subclass>")
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="<overridden by subclass>")
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):

View file

@ -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']

View file

@ -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:

View file

@ -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()