feat: add cython extension for signature (#72)

This commit is contained in:
J. Nick Koston 2022-10-04 07:32:21 -10:00 committed by GitHub
parent 254f9b3f95
commit 0ad8801215
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 76 additions and 53 deletions

View File

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

View File

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

View File

@ -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("<I").pack
@ -13,7 +13,7 @@ class Marshaller:
def __init__(self, signature: str, body: List[Any]) -> None:
"""Marshaller constructor."""
self.signature_tree = SignatureTree._get(signature)
self.signature_tree = get_signature_tree(signature)
self._buf = bytearray()
self.body = body

View File

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

View File

@ -3,12 +3,12 @@ import io
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 @@ UNPACK_LENGTHS = {BIG_ENDIAN: Struct(">III"), LITTLE_ENDIAN: Struct("<III")}
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 @@ class Unmarshaller:
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 @@ class Unmarshaller:
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 @@ class Unmarshaller:
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 @@ class Unmarshaller:
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 @@ class Unmarshaller:
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),

View File

@ -2,7 +2,7 @@ import ast
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

View File

@ -3,7 +3,7 @@ from typing import List, Union
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 @@ class Arg:
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 @@ class Property:
):
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)"

View File

@ -4,7 +4,7 @@ from ._private.constants import LITTLE_ENDIAN, PROTOCOL_VERSION, HeaderField
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 @@ class Message:
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 @@ class Message:
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)

View File

@ -13,7 +13,7 @@ from ._private.util import (
)
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 @@ class _Method:
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 @@ class _Method:
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 @@ class _Signal:
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 @@ class _Property(property):
)
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")

View File

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

View File

@ -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 @@ class Variant:
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 @@ class Variant:
return "<dbus_fast.signature.Variant ('{}', {})>".format(
self.type.signature, self.value
)
@lru_cache(maxsize=None)
def get_signature_tree(signature: str = "") -> SignatureTree:
return SignatureTree(signature)