feat: add cython extension for signature (#72)
This commit is contained in:
parent
254f9b3f95
commit
0ad8801215
1
build.py
1
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",
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)"
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")
|
||||
|
||||
16
src/dbus_fast/signature.pxd
Normal file
16
src/dbus_fast/signature.pxd
Normal 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
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user