From 6c7b5cafe232dd925d70b62c9a3ca575bf89d3cc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Oct 2022 22:09:38 -1000 Subject: [PATCH] feat: add cython extension for signature --- build.py | 1 + src/dbus_fast/_private/marshaller.pxd | 12 ++++++++--- src/dbus_fast/_private/marshaller.py | 4 ++-- src/dbus_fast/_private/unmarshaller.pxd | 17 +++++++++------ src/dbus_fast/_private/unmarshaller.py | 28 ++++++++++++------------- src/dbus_fast/_private/util.py | 8 +++---- src/dbus_fast/introspection.py | 6 +++--- src/dbus_fast/message.py | 11 ++-------- src/dbus_fast/service.py | 14 ++++++------- src/dbus_fast/signature.pxd | 16 ++++++++++++++ src/dbus_fast/signature.py | 12 ++++++----- 11 files changed, 76 insertions(+), 53 deletions(-) create mode 100644 src/dbus_fast/signature.pxd diff --git a/build.py b/build.py index 61c4b2bd..2bf8b26f 100644 --- a/build.py +++ b/build.py @@ -22,6 +22,7 @@ def build(setup_kwargs): dict( ext_modules=cythonize( [ + "src/dbus_fast/signature.py", "src/dbus_fast/unpack.py", "src/dbus_fast/_private/marshaller.py", "src/dbus_fast/_private/unmarshaller.py", diff --git a/src/dbus_fast/_private/marshaller.pxd b/src/dbus_fast/_private/marshaller.pxd index 0f089018..f7164a0e 100644 --- a/src/dbus_fast/_private/marshaller.pxd +++ b/src/dbus_fast/_private/marshaller.pxd @@ -9,14 +9,13 @@ cdef class Marshaller: cdef bytearray _buf cdef object body - @cython.locals( offset=cython.ulong, ) cpdef int align(self, unsigned long n) - @cython.locals( + value_len=cython.uint, signature_len=cython.uint, written=cython.uint, ) @@ -26,11 +25,13 @@ cdef class Marshaller: array_len=cython.uint, written=cython.uint, array_len_packed=cython.bytes, + i=cython.uint, ) cpdef write_array(self, object array, object type) @cython.locals( written=cython.uint, + i=cython.uint, ) cpdef write_struct(self, object array, object type) @@ -39,9 +40,14 @@ cdef class Marshaller: ) cpdef write_single(self, object type_, object body) + @cython.locals( + written=cython.uint, + ) + cpdef write_dict_entry(self, object type_, object body) + cpdef marshall(self) @cython.locals( offset=cython.ulong, ) - cpdef _construct_buffer(self) + cdef _construct_buffer(self) diff --git a/src/dbus_fast/_private/marshaller.py b/src/dbus_fast/_private/marshaller.py index 30ffd1e0..eaf99dda 100644 --- a/src/dbus_fast/_private/marshaller.py +++ b/src/dbus_fast/_private/marshaller.py @@ -1,7 +1,7 @@ from struct import Struct, error from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple -from ..signature import SignatureTree, SignatureType, Variant +from ..signature import SignatureType, Variant, get_signature_tree PACK_UINT32 = Struct(" None: """Marshaller constructor.""" - self.signature_tree = SignatureTree._get(signature) + self.signature_tree = get_signature_tree(signature) self._buf = bytearray() self.body = body diff --git a/src/dbus_fast/_private/unmarshaller.pxd b/src/dbus_fast/_private/unmarshaller.pxd index 4402ea06..c95f85ef 100644 --- a/src/dbus_fast/_private/unmarshaller.pxd +++ b/src/dbus_fast/_private/unmarshaller.pxd @@ -13,6 +13,9 @@ cdef unsigned int BIG_ENDIAN cdef str UINT32_CAST cdef object UINT32_SIGNATURE +cdef class MarshallerStreamEndError(Exception): + pass + cdef class Unmarshaller: cdef object _unix_fds @@ -33,18 +36,20 @@ cdef class Unmarshaller: cpdef reset(self) + cdef read_sock(self, unsigned long length) + @cython.locals( start_len=cython.ulong, missing_bytes=cython.ulong ) - cpdef read_to_pos(self, unsigned long pos) + cdef read_to_pos(self, unsigned long pos) cpdef read_uint32_cast(self, object type_) @cython.locals( buf_bytes=cython.bytearray, ) - cpdef read_string_cast(self, type_ = *) + cpdef read_string_cast(self, object type_) @cython.locals( beginning_pos=cython.ulong, @@ -56,16 +61,16 @@ cdef class Unmarshaller: o=cython.ulong, signature_len=cython.uint, ) - cpdef read_signature(self, type_ = *) + cpdef read_signature(self, object type_) @cython.locals( endian=cython.uint, protocol_version=cython.uint, can_cast=cython.bint ) - cpdef _read_header(self) + cdef _read_header(self) - cpdef _read_body(self) + cdef _read_body(self) cpdef unmarshall(self) @@ -74,4 +79,4 @@ cdef class Unmarshaller: o=cython.ulong, signature_len=cython.uint, ) - cpdef header_fields(self, unsigned int header_length) + cdef header_fields(self, unsigned int header_length) diff --git a/src/dbus_fast/_private/unmarshaller.py b/src/dbus_fast/_private/unmarshaller.py index 3ed53d21..d7737efe 100644 --- a/src/dbus_fast/_private/unmarshaller.py +++ b/src/dbus_fast/_private/unmarshaller.py @@ -3,12 +3,12 @@ import socket import sys from struct import Struct -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any, Callable, Dict, List, Tuple -from ..constants import MESSAGE_FLAG_MAP, MESSAGE_TYPE_MAP, MessageFlag, MessageType +from ..constants import MESSAGE_FLAG_MAP, MESSAGE_TYPE_MAP from ..errors import InvalidMessageError from ..message import Message -from ..signature import SignatureTree, SignatureType, Variant +from ..signature import SignatureType, Variant, get_signature_tree from .constants import ( BIG_ENDIAN, HEADER_NAME_MAP, @@ -28,7 +28,7 @@ UINT32_CAST = "I" UINT32_SIZE = 4 UINT32_DBUS_TYPE = "u" -UINT32_SIGNATURE = SignatureTree._get(UINT32_DBUS_TYPE).types[0] +UINT32_SIGNATURE = get_signature_tree(UINT32_DBUS_TYPE).types[0] DBUS_TO_CTYPE = { "y": ("B", 1), # byte @@ -236,14 +236,14 @@ def read_to_pos(self, pos) -> None: if len(data) + start_len != pos: raise MarshallerStreamEndError() - def read_uint32_cast(self, signature: SignatureType) -> Any: + def read_uint32_cast(self, type_: SignatureType) -> Any: self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align return self._view[self._pos - UINT32_SIZE : self._pos].cast(UINT32_CAST)[0] - def read_boolean(self, type_=None) -> bool: + def read_boolean(self, type_: SignatureType) -> bool: return bool(self._readers[UINT32_SIGNATURE.token](self, UINT32_SIGNATURE)) - def read_string_cast(self, type_=None) -> str: + def read_string_cast(self, type_: SignatureType) -> str: """Read a string using cast.""" self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align str_start = self._pos @@ -252,7 +252,7 @@ def read_string_cast(self, type_=None) -> str: self._pos += self._view[start_pos : self._pos].cast(UINT32_CAST)[0] + 1 return self._buf[str_start : self._pos - 1].decode() - def read_string_unpack(self, type_=None) -> str: + def read_string_unpack(self, type_: SignatureType) -> str: """Read a string using unpack.""" self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align str_start = self._pos @@ -260,21 +260,21 @@ def read_string_unpack(self, type_=None) -> str: self._pos += self._uint32_unpack(self._view, str_start - UINT32_SIZE)[0] + 1 return self._buf[str_start : self._pos - 1].decode() - def read_signature(self, type_=None) -> str: + def read_signature(self, type_: SignatureType) -> str: signature_len = self._view[self._pos] # byte o = self._pos + 1 # read terminating '\0' byte as well (str_length + 1) self._pos = o + signature_len + 1 return self._buf[o : o + signature_len].decode() - def read_variant(self, type_=None) -> Variant: - tree = SignatureTree._get(self.read_signature()) + def read_variant(self, type_: SignatureType) -> Variant: + tree = get_signature_tree(self.read_signature(type_)) # verify in Variant is only useful on construction not unmarshalling return Variant( tree, self._readers[tree.types[0].token](self, tree.types[0]), verify=False ) - def read_struct(self, type_=None) -> List[Any]: + def read_struct(self, type_: SignatureType) -> List[Any]: self._pos += -self._pos & 7 # align 8 readers = self._readers return [ @@ -344,7 +344,7 @@ def header_fields(self, header_length) -> Dict[str, Any]: signature_len = self._view[self._pos] # byte o = self._pos + 1 self._pos += signature_len + 2 # one for the byte, one for the '\0' - tree = SignatureTree._get(self._buf[o : o + signature_len].decode()) + tree = get_signature_tree(self._buf[o : o + signature_len].decode()) headers[HEADER_NAME_MAP[field_0]] = self._readers[tree.types[0].token]( self, tree.types[0] ) @@ -391,7 +391,7 @@ def _read_body(self): self._pos = HEADER_ARRAY_OF_STRUCT_SIGNATURE_POSITION header_fields = self.header_fields(self._header_len) self._pos += -self._pos & 7 # align 8 - tree = SignatureTree._get(header_fields.get(HeaderField.SIGNATURE.name, "")) + tree = get_signature_tree(header_fields.get(HeaderField.SIGNATURE.name, "")) self._message = Message( destination=header_fields.get(HEADER_DESTINATION), path=header_fields.get(HEADER_PATH), diff --git a/src/dbus_fast/_private/util.py b/src/dbus_fast/_private/util.py index 18f94af1..38c6a53a 100644 --- a/src/dbus_fast/_private/util.py +++ b/src/dbus_fast/_private/util.py @@ -2,7 +2,7 @@ import inspect from typing import Any, List, Union -from ..signature import SignatureTree, Variant +from ..signature import SignatureTree, Variant, get_signature_tree def signature_contains_type( @@ -11,7 +11,7 @@ def signature_contains_type( """For a given signature and body, check to see if it contains any members with the given token""" if type(signature) is str: - signature = SignatureTree._get(signature) + signature = get_signature_tree(signature) queue = [] contains_variants = False @@ -56,7 +56,7 @@ def replace_fds_with_idx( an index and return the corresponding list of unix fds that can be set on the Message""" if type(signature) is str: - signature = SignatureTree._get(signature) + signature = get_signature_tree(signature) if not signature_contains_type(signature, body, "h"): return body, [] @@ -82,7 +82,7 @@ def replace_idx_with_fds( Type 'h' refers to an index in the unix_fds array. Replace those with the actual file descriptor or `None` if one does not exist.""" if type(signature) is str: - signature = SignatureTree._get(signature) + signature = get_signature_tree(signature) if not signature_contains_type(signature, body, "h"): return body diff --git a/src/dbus_fast/introspection.py b/src/dbus_fast/introspection.py index a1558bcd..ed851580 100644 --- a/src/dbus_fast/introspection.py +++ b/src/dbus_fast/introspection.py @@ -3,7 +3,7 @@ from .constants import ArgDirection, PropertyAccess from .errors import InvalidIntrospectionError -from .signature import SignatureTree, SignatureType +from .signature import SignatureType, get_signature_tree from .validators import assert_interface_name_valid, assert_member_name_valid # https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format @@ -42,7 +42,7 @@ def __init__( type_ = signature signature = signature.signature else: - tree = SignatureTree._get(signature) + tree = get_signature_tree(signature) if len(tree.types) != 1: raise InvalidIntrospectionError( f"an argument must have a single complete type. (has {len(tree.types)} types)" @@ -248,7 +248,7 @@ def __init__( ): assert_member_name_valid(name) - tree = SignatureTree._get(signature) + tree = get_signature_tree(signature) if len(tree.types) != 1: raise InvalidIntrospectionError( f"properties must have a single complete type. (has {len(tree.types)} types)" diff --git a/src/dbus_fast/message.py b/src/dbus_fast/message.py index 270453b9..d976dc56 100644 --- a/src/dbus_fast/message.py +++ b/src/dbus_fast/message.py @@ -4,7 +4,7 @@ from ._private.marshaller import Marshaller from .constants import ErrorType, MessageFlag, MessageType from .errors import InvalidMessageError -from .signature import SignatureTree, Variant +from .signature import SignatureTree, Variant, get_signature_tree from .validators import ( assert_bus_name_valid, assert_interface_name_valid, @@ -129,7 +129,7 @@ def __init__( self.signature_tree = signature else: self.signature = signature - self.signature_tree = SignatureTree._get(signature) + self.signature_tree = get_signature_tree(signature) self.body = body self.serial = serial @@ -255,13 +255,6 @@ def new_signal( unix_fds=unix_fds, ) - def _matches(self, **kwargs): - for attr, val in kwargs.items(): - if getattr(self, attr) != val: - return False - - return True - def _marshall(self, negotiate_unix_fd=False): # TODO maximum message size is 134217728 (128 MiB) body_block = Marshaller(self.signature, self.body) diff --git a/src/dbus_fast/service.py b/src/dbus_fast/service.py index b24bb544..20ca015f 100644 --- a/src/dbus_fast/service.py +++ b/src/dbus_fast/service.py @@ -13,7 +13,7 @@ ) from .constants import PropertyAccess from .errors import SignalDisabledError -from .signature import SignatureBodyMismatchError, SignatureTree, Variant +from .signature import SignatureBodyMismatchError, Variant, get_signature_tree class _Method: @@ -39,7 +39,7 @@ def __init__(self, fn, name, disabled=False): out_args = [] out_signature = parse_annotation(inspection.return_annotation) if out_signature: - for type_ in SignatureTree._get(out_signature).types: + for type_ in get_signature_tree(out_signature).types: out_args.append(intr.Arg(type_, intr.ArgDirection.OUT)) self.name = name @@ -48,8 +48,8 @@ def __init__(self, fn, name, disabled=False): self.introspection = intr.Method(name, in_args, out_args) self.in_signature = in_signature self.out_signature = out_signature - self.in_signature_tree = SignatureTree._get(in_signature) - self.out_signature_tree = SignatureTree._get(out_signature) + self.in_signature_tree = get_signature_tree(in_signature) + self.out_signature_tree = get_signature_tree(out_signature) def method(name: str = None, disabled: bool = False): @@ -116,12 +116,12 @@ def __init__(self, fn, name, disabled=False): if return_annotation: signature = return_annotation - signature_tree = SignatureTree._get(signature) + signature_tree = get_signature_tree(signature) for type_ in signature_tree.types: args.append(intr.Arg(type_, intr.ArgDirection.OUT)) else: signature = "" - signature_tree = SignatureTree._get("") + signature_tree = get_signature_tree("") self.signature = signature self.signature_tree = signature_tree @@ -226,7 +226,7 @@ def __init__(self, fn, *args, **kwargs): ) self.signature = return_annotation - tree = SignatureTree._get(return_annotation) + tree = get_signature_tree(return_annotation) if len(tree.types) != 1: raise ValueError("the property signature must be a single complete type") diff --git a/src/dbus_fast/signature.pxd b/src/dbus_fast/signature.pxd new file mode 100644 index 00000000..14b3fad0 --- /dev/null +++ b/src/dbus_fast/signature.pxd @@ -0,0 +1,16 @@ +"""cdefs for signature.py""" + +import cython + + +cdef class SignatureType: + + cdef public str token + cdef public list children + cdef str _signature + + +cdef class SignatureTree: + + cdef public str signature + cdef public list types diff --git a/src/dbus_fast/signature.py b/src/dbus_fast/signature.py index a9081b6f..d7da371d 100644 --- a/src/dbus_fast/signature.py +++ b/src/dbus_fast/signature.py @@ -330,10 +330,7 @@ class SignatureTree: :class:`InvalidSignatureError` if the given signature is not valid. """ - @staticmethod - @lru_cache(maxsize=None) - def _get(signature: str = "") -> "SignatureTree": - return SignatureTree(signature) + __slots__ = ("signature", "types") def __init__(self, signature: str = ""): self.signature = signature @@ -414,7 +411,7 @@ def __init__( signature_str = signature.signature signature_tree = None elif type(signature) is str: - signature_tree = SignatureTree._get(signature) + signature_tree = get_signature_tree(signature) else: raise TypeError( "signature must be a SignatureTree, SignatureType, or a string" @@ -445,3 +442,8 @@ def __repr__(self): return "".format( self.type.signature, self.value ) + + +@lru_cache(maxsize=None) +def get_signature_tree(signature: str = "") -> SignatureTree: + return SignatureTree(signature)