Skip to content

Commit

Permalink
Add SdBusMessage.parse_to_tuple() method
Browse files Browse the repository at this point in the history
Parses the message data to a tuple. The main difference is handling
of no data and a single complete type messages. When message has
no data returns a zero size tuple. When message has a single complete
type return a tuple of one element.

This makes it simpler to implement the D-Bus -> Python calls
because now all it takes is calling Python function with unpacked
tuple. Unpacking zero size tuple is equivalent to calling function
with no arguments.

This also fixes methods that take a single struct. Before there was
ambiguity if a method was called with a struct or multiple complete
types. Now a single struct would be a part of one element tuple.
  • Loading branch information
igo95862 committed Feb 18, 2024
1 parent 3535e7d commit f58ace9
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 8 deletions.
8 changes: 1 addition & 7 deletions src/sdbus/dbus_proxy_async_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,13 @@ async def _dbus_reply_call_method(
request_message: SdBusMessage,
local_object: DbusInterfaceBaseAsync,
) -> Any:
request_data = request_message.get_contents()

local_method = self.dbus_method.original_method.__get__(
local_object, None)

CURRENT_MESSAGE.set(request_message)

if isinstance(request_data, tuple):
return await local_method(*request_data)
elif request_data is None:
return await local_method()
else:
return await local_method(request_data)
return await local_method(*request_message.parse_to_tuple())

async def _dbus_reply_call(
self,
Expand Down
3 changes: 3 additions & 0 deletions src/sdbus/sd_bus_internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ def create_error_reply(
def send(self) -> None:
raise NotImplementedError(__STUB_ERROR)

def parse_to_tuple(self) -> Tuple[Any, ...]:
raise NotImplementedError(__STUB_ERROR)

expect_reply: bool = False
destination: Optional[str] = None
path: Optional[str] = None
Expand Down
24 changes: 24 additions & 0 deletions src/sdbus/sd_bus_internals_message.c
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,29 @@ static PyObject* SdBusMessage_get_contents2(SdBusMessageObject* self, PyObject*
return iter_tuple_or_single(&read_parser);
}

static PyObject* SdBusMessage_parse_to_tuple(SdBusMessageObject* self, PyObject* Py_UNUSED(args)) {
const char* message_signature = sd_bus_message_get_signature(self->message_ref, 0);

if (message_signature == NULL) {
PyErr_SetString(PyExc_ValueError, "Failed to get message signature.");
return NULL;
}
if (message_signature[0] == '\0') {
// Empty message. Return zero size tuple.
return PyTuple_New(0);
}

CALL_SD_BUS_AND_CHECK(sd_bus_message_rewind(self->message_ref, 0));
_Parse_state read_parser = {
.message = self->message_ref,
.container_char_ptr = message_signature,
.index = 0,
.max_index = strlen(message_signature),
};

return _iter_struct(&read_parser);
}

#ifndef Py_LIMITED_API
static SdBusMessageObject* SdBusMessage_create_error_reply(SdBusMessageObject* self, PyObject* const* args, Py_ssize_t nargs) {
SD_BUS_PY_CHECK_ARGS_NUMBER(2);
Expand Down Expand Up @@ -1012,6 +1035,7 @@ static PyMethodDef SdBusMessage_methods[] = {
{"dump", (PyCFunction)SdBusMessage_dump, METH_NOARGS, PyDoc_STR("Dump message to stdout.")},
{"seal", (PyCFunction)SdBusMessage_seal, METH_NOARGS, PyDoc_STR("Seal message contents.")},
{"get_contents", (PyCFunction)SdBusMessage_get_contents2, METH_NOARGS, PyDoc_STR("Iterate over message contents.")},
{"parse_to_tuple", (PyCFunction)SdBusMessage_parse_to_tuple, METH_NOARGS, PyDoc_STR("Parse message data to a tuple.")},
{"create_reply", (PyCFunction)SdBusMessage_create_reply, METH_NOARGS, PyDoc_STR("Create reply message.")},
{"create_error_reply", (SD_BUS_PY_FUNC_TYPE)SdBusMessage_create_error_reply, SD_BUS_PY_METH,
PyDoc_STR("Create error reply with error name and error message.")},
Expand Down
22 changes: 21 additions & 1 deletion test/test_sdbus_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,17 @@ def empty_signal(self) -> None:
async def returns_none_method(self) -> None:
return

@dbus_method_async(
input_signature="(iiii)",
result_signature="i",
)
async def takes_struct_method(
self,
int_struct: Tuple[int, int, int, int],
) -> int:
a, b, c, d = int_struct
return a*b*c*d


class DbusErrorTest(DbusFailedError):
dbus_error_name = 'org.example.Error'
Expand Down Expand Up @@ -316,7 +327,6 @@ async def test_method(self) -> None:

with self.subTest("Test method that returns None"):
self.assertIsNone(

await test_object
.returns_none_method() # type: ignore[func-returns-value]
)
Expand All @@ -325,6 +335,16 @@ async def test_method(self) -> None:
.returns_none_method() # type: ignore[func-returns-value]
)

with self.subTest("Test method that takes a single struct"):
self.assertEqual(
await test_object.takes_struct_method((2, 3, 4, 5)),
120,
)
self.assertEqual(
await test_object_connection.takes_struct_method((9, 8, 7, 6)),
3024,
)

async def test_subclass(self) -> None:
test_object, test_object_connection = initialize_object()

Expand Down

0 comments on commit f58ace9

Please sign in to comment.