mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-05-05 14:48:02 +02:00
scanner: switch to using dataclasses
This drops one dependency that we're not fully using anyway. Except for the per-attribute validators that can be done in __post_init() we're not using attrs for anything that dataclasses cannot do.
This commit is contained in:
parent
a5826424f1
commit
dbc06510a1
5 changed files with 90 additions and 90 deletions
|
|
@ -40,9 +40,9 @@ variables:
|
|||
# See the documentation here: #
|
||||
# https://wayland.freedesktop.org/libinput/doc/latest/building_libinput.html #
|
||||
###############################################################################
|
||||
FEDORA_PACKAGES: 'git diffutils gcc gcc-c++ pkgconf-pkg-config systemd-devel libxkbcommon-devel libxml2 doxygen python3-attrs python3-pytest python3-dbusmock python3-jinja2 python3-pip python3-pyyaml golang libabigail '
|
||||
FEDORA_PACKAGES: 'git diffutils gcc gcc-c++ pkgconf-pkg-config systemd-devel libxkbcommon-devel libxml2 doxygen python3-pytest python3-dbusmock python3-jinja2 python3-pip python3-pyyaml golang libabigail '
|
||||
FEDORA_PIP_PACKAGES: 'meson ninja structlog strenum '
|
||||
DEBIAN_PACKAGES: 'git gcc g++ pkg-config libsystemd-dev libxkbcommon-dev libxml2 doxygen python3-attr python3-pytest python3-dbusmock python3-jinja2 python3-pip python3-yaml '
|
||||
DEBIAN_PACKAGES: 'git gcc g++ pkg-config libsystemd-dev libxkbcommon-dev libxml2 doxygen python3-pytest python3-dbusmock python3-jinja2 python3-pip python3-yaml '
|
||||
DEBIAN_PIP_PACKAGES: 'meson ninja structlog strenum '
|
||||
############################ end of package lists #############################
|
||||
|
||||
|
|
@ -50,8 +50,8 @@ variables:
|
|||
# changing these will force rebuilding the associated image
|
||||
# Note: these tags have no meaning and are not tied to a particular
|
||||
# libinput version
|
||||
FEDORA_TAG: '2024-07-24.3'
|
||||
DEBIAN_TAG: '2024-07-24.3'
|
||||
FEDORA_TAG: '2024-09-11.0'
|
||||
DEBIAN_TAG: '2024-09-11.0'
|
||||
|
||||
FDO_UPSTREAM_REPO: libinput/libei
|
||||
|
||||
|
|
@ -285,6 +285,7 @@ abicheck@fedora:40:
|
|||
meson compile -C _build
|
||||
meson install -C _build
|
||||
popd
|
||||
- pip install attrs
|
||||
script:
|
||||
- git remote add upstream$CI_JOB_ID https://gitlab.freedesktop.org/$FDO_UPSTREAM_REPO
|
||||
- git fetch --tags upstream$CI_JOB_ID
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ abicheck@{{distro.name}}:{{version}}:
|
|||
meson compile -C _build
|
||||
meson install -C _build
|
||||
popd
|
||||
- pip install attrs
|
||||
script:
|
||||
- git remote add upstream$CI_JOB_ID https://gitlab.freedesktop.org/$FDO_UPSTREAM_REPO
|
||||
- git fetch --tags upstream$CI_JOB_ID
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
|
||||
# We're happy to rebuild all containers when one changes.
|
||||
.default_tag: &default_tag '2024-07-24.3'
|
||||
.default_tag: &default_tag '2024-09-11.0'
|
||||
|
||||
last_abi_break: abe85e051e7029bfd2e7913ab980a9e0042b6d0d
|
||||
minimum_meson_version: 0.57.0
|
||||
|
|
@ -24,7 +24,6 @@ distributions:
|
|||
- libxkbcommon-devel
|
||||
- libxml2
|
||||
- doxygen
|
||||
- python3-attrs
|
||||
- python3-pytest
|
||||
- python3-dbusmock
|
||||
- python3-jinja2
|
||||
|
|
@ -51,7 +50,6 @@ distributions:
|
|||
- libxkbcommon-dev
|
||||
- libxml2
|
||||
- doxygen
|
||||
- python3-attr
|
||||
- python3-pytest
|
||||
- python3-dbusmock
|
||||
- python3-jinja2
|
||||
|
|
|
|||
164
proto/ei-scanner
164
proto/ei-scanner
|
|
@ -20,9 +20,9 @@ appear in the XML file.
|
|||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
import argparse
|
||||
import attr
|
||||
import jinja2
|
||||
import jinja2.environment
|
||||
import os
|
||||
|
|
@ -55,48 +55,54 @@ def snake2camel(s: str) -> str:
|
|||
return s.replace("_", " ").title().replace(" ", "")
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Description:
|
||||
summary: str = attr.ib(default="")
|
||||
text: str = attr.ib(default="")
|
||||
summary: str = ""
|
||||
text: str = ""
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Argument:
|
||||
"""
|
||||
Argument to a request or a reply
|
||||
"""
|
||||
|
||||
name: str = attr.ib()
|
||||
protocol_type: str = attr.ib()
|
||||
summary: str = attr.ib()
|
||||
enum: Optional["Enum"] = attr.ib()
|
||||
interface: Optional["Interface"] = attr.ib()
|
||||
interface_arg: Optional["Argument"] = attr.ib(default=None)
|
||||
name: str
|
||||
protocol_type: str
|
||||
summary: str
|
||||
enum: Optional["Enum"]
|
||||
interface: Optional["Interface"]
|
||||
interface_arg: Optional["Argument"] = None
|
||||
"""
|
||||
For an argument with "interface_arg", this field points to the argument that
|
||||
contains the interface name.
|
||||
"""
|
||||
interface_arg_for: Optional["Argument"] = attr.ib(default=None)
|
||||
interface_arg_for: Optional["Argument"] = None
|
||||
"""
|
||||
For an argument referenced by another argument through "interface_name", this field
|
||||
points to the other argument that references this argument.
|
||||
"""
|
||||
version_arg: Optional["Argument"] = attr.ib(default=None)
|
||||
version_arg: Optional["Argument"] = None
|
||||
"""
|
||||
For an argument with type "new_id", this field points to the argument that
|
||||
contains the version for this new object.
|
||||
"""
|
||||
version_arg_for: Optional["Argument"] = attr.ib(default=None)
|
||||
version_arg_for: Optional["Argument"] = None
|
||||
"""
|
||||
For an argument referenced by another argument of type "new_id", this field
|
||||
points to the other argument that references this argument.
|
||||
"""
|
||||
allow_null: bool = attr.ib(default=False)
|
||||
allow_null: bool = False
|
||||
"""
|
||||
For an argument of type string, specify if the argument may be NULL.
|
||||
"""
|
||||
|
||||
def __post_init(self):
|
||||
if self.protocol_type is None or self.protocol_type not in PROTOCOL_TYPES:
|
||||
raise ValueError(f"Failed to parse protocol_type {self.protocol_type}")
|
||||
if self.interface is not None and self.signature not in ["n", "o"]:
|
||||
raise ValueError("Interface may only be set for object types")
|
||||
|
||||
@property
|
||||
def signature(self) -> str:
|
||||
"""
|
||||
|
|
@ -104,11 +110,6 @@ class Argument:
|
|||
"""
|
||||
return PROTOCOL_TYPES[self.protocol_type]
|
||||
|
||||
@interface.validator # type: ignore
|
||||
def _validate_interface(self, attribute, value):
|
||||
if value is not None and self.signature not in ["n", "o"]:
|
||||
raise ValueError("Interface may only be set for object types")
|
||||
|
||||
@property
|
||||
def as_c_arg(self) -> str:
|
||||
return f"{self.c_type} {self.name}"
|
||||
|
|
@ -127,12 +128,6 @@ class Argument:
|
|||
"new_id": "new_id_t",
|
||||
}[self.protocol_type]
|
||||
|
||||
@protocol_type.validator # type: ignore
|
||||
def _validate_protocol_type(self, attribute, value):
|
||||
assert (
|
||||
value is not None and value in PROTOCOL_TYPES
|
||||
), f"Failed to parse protocol_type {value}"
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
|
|
@ -153,26 +148,25 @@ class Argument:
|
|||
)
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Message:
|
||||
"""
|
||||
Parent class for a wire message (Request or Event).
|
||||
"""
|
||||
|
||||
name: str = attr.ib()
|
||||
since: int = attr.ib()
|
||||
opcode: int = attr.ib()
|
||||
interface: "Interface" = attr.ib()
|
||||
description: Optional[Description] = attr.ib(default=None)
|
||||
is_destructor: bool = attr.ib(default=False)
|
||||
context_type: Optional[str] = attr.ib(default=None)
|
||||
name: str
|
||||
since: int
|
||||
opcode: int
|
||||
interface: "Interface"
|
||||
description: Optional[Description] = None
|
||||
is_destructor: bool = False
|
||||
context_type: Optional[str] = None
|
||||
|
||||
arguments: List[Argument] = attr.ib(init=False, factory=list)
|
||||
arguments: List[Argument] = field(init=False, default_factory=list)
|
||||
|
||||
@context_type.validator # type: ignore
|
||||
def _context_type_validate(self, attr, value):
|
||||
if value not in [None, "sender", "receiver"]:
|
||||
raise ValueError(f"Invalid context type {value}")
|
||||
def __post_init(self):
|
||||
if self.context_type not in [None, "sender", "receiver"]:
|
||||
raise ValueError(f"Invalid context type {self.context_type}")
|
||||
|
||||
def add_argument(self, arg: Argument) -> None:
|
||||
if arg.name in [a.name for a in self.arguments]:
|
||||
|
|
@ -198,7 +192,7 @@ class Message:
|
|||
return None
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Request(Message):
|
||||
@classmethod
|
||||
def create(
|
||||
|
|
@ -225,7 +219,7 @@ class Request(Message):
|
|||
return f"{self.interface.name}_request_{self.name}"
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Event(Message):
|
||||
@classmethod
|
||||
def create(
|
||||
|
|
@ -252,17 +246,17 @@ class Event(Message):
|
|||
return f"{self.interface.name}_event_{self.name}"
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Entry:
|
||||
"""
|
||||
An enum entry
|
||||
"""
|
||||
|
||||
name: str = attr.ib()
|
||||
value: int = attr.ib()
|
||||
enum: "Enum" = attr.ib()
|
||||
summary: str = attr.ib()
|
||||
since: int = attr.ib()
|
||||
name: str
|
||||
value: int
|
||||
enum: "Enum"
|
||||
summary: str
|
||||
since: int
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
|
|
@ -278,15 +272,15 @@ class Entry:
|
|||
return f"{self.enum.fqdn}_{self.name}"
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Enum:
|
||||
name: str = attr.ib()
|
||||
since: int = attr.ib()
|
||||
interface: "Interface" = attr.ib()
|
||||
is_bitfield: bool = attr.ib(default=False)
|
||||
description: Optional[Description] = attr.ib(default=None)
|
||||
name: str
|
||||
since: int
|
||||
interface: "Interface"
|
||||
is_bitfield: bool = False
|
||||
description: Optional[Description] = None
|
||||
|
||||
entries: List[Entry] = attr.ib(init=False, factory=list)
|
||||
entries: List[Entry] = field(init=False, default_factory=list)
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
|
|
@ -329,16 +323,20 @@ class Enum:
|
|||
return snake2camel(self.name)
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Interface:
|
||||
protocol_name: str = attr.ib() # name as in the XML, e.g. ei_pointer
|
||||
version: int = attr.ib()
|
||||
requests: List[Request] = attr.ib(init=False, factory=list)
|
||||
events: List[Event] = attr.ib(init=False, factory=list)
|
||||
enums: List[Enum] = attr.ib(init=False, factory=list)
|
||||
protocol_name: str # name as in the XML, e.g. ei_pointer
|
||||
version: int
|
||||
requests: List[Request] = field(init=False, default_factory=list)
|
||||
events: List[Event] = field(init=False, default_factory=list)
|
||||
enums: List[Enum] = field(init=False, default_factory=list)
|
||||
|
||||
mode: str = attr.ib(validator=attr.validators.in_(["ei", "eis", "brei"]))
|
||||
description: Optional[Description] = attr.ib(default=None)
|
||||
mode: str
|
||||
description: Optional[Description] = None
|
||||
|
||||
def __post_init(self):
|
||||
if self.mode not in ["ei", "eis", "brei"]:
|
||||
raise ValueError(f"Invalid mode {self.mode}")
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
|
|
@ -444,11 +442,11 @@ class Interface:
|
|||
return cls(protocol_name=protocol_name, version=version, mode=mode)
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class XmlError(Exception):
|
||||
line: int = attr.ib()
|
||||
column: int = attr.ib()
|
||||
message: str = attr.ib()
|
||||
line: int
|
||||
column: int
|
||||
message: str
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"line {self.line}:{self.column}: {self.message}"
|
||||
|
|
@ -458,32 +456,34 @@ class XmlError(Exception):
|
|||
return cls(line=location[0], column=location[1], message=message)
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Copyright:
|
||||
text: str = attr.ib(default="")
|
||||
is_complete: bool = attr.ib(init=False, default=False)
|
||||
text: str = ""
|
||||
is_complete: bool = field(init=False, default=False)
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class Protocol:
|
||||
copyright: Optional[str] = attr.ib(default=None)
|
||||
interfaces: List[Interface] = attr.ib(factory=list)
|
||||
copyright: Optional[str] = None
|
||||
interfaces: List[Interface] = field(default_factory=list)
|
||||
|
||||
|
||||
@attr.s
|
||||
@dataclass
|
||||
class ProtocolParser(xml.sax.handler.ContentHandler):
|
||||
component: str = attr.ib()
|
||||
interfaces: List[Interface] = attr.ib(factory=list)
|
||||
copyright: Optional[Copyright] = attr.ib(init=False, default=None)
|
||||
component: str
|
||||
interfaces: List[Interface] = field(default_factory=list)
|
||||
copyright: Optional[Copyright] = field(init=False, default=None)
|
||||
|
||||
current_interface: Optional[Interface] = attr.ib(init=False, default=None)
|
||||
current_message: Optional[Union[Message, Enum]] = attr.ib(init=False, default=None)
|
||||
current_description: Optional[Description] = attr.ib(init=False, default=None)
|
||||
current_interface: Optional[Interface] = field(init=False, default=None)
|
||||
current_message: Optional[Union[Message, Enum]] = field(init=False, default=None)
|
||||
current_description: Optional[Description] = field(init=False, default=None)
|
||||
# A dict of arg name to interface_arg name mappings
|
||||
current_interface_arg_names: Dict[str, str] = attr.ib(init=False, default=attr.Factory(dict)) # type: ignore
|
||||
current_new_id_arg: Optional[Argument] = attr.ib(init=False, default=None)
|
||||
current_interface_arg_names: Dict[str, str] = field(
|
||||
init=False, default_factory=dict
|
||||
)
|
||||
current_new_id_arg: Optional[Argument] = field(init=False, default=None)
|
||||
|
||||
_run_counter: int = attr.ib(init=False, default=0, repr=False)
|
||||
_run_counter: int = field(init=False, default=0, repr=False)
|
||||
|
||||
@property
|
||||
def location(self) -> Tuple[int, int]:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ if xmllint.found()
|
|||
endif
|
||||
|
||||
pymod = import('python')
|
||||
required_python_modules = ['attr', 'jinja2']
|
||||
required_python_modules = ['jinja2']
|
||||
python = pymod.find_installation('python3', modules: required_python_modules)
|
||||
if python.language_version().version_compare('<3.9')
|
||||
error('Python 3.9 or later required')
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue