Skip to content

Commit

Permalink
Fix property getters not returning D-Bus registered errors to D-Bus
Browse files Browse the repository at this point in the history
  • Loading branch information
igo95862 committed Jun 12, 2022
1 parent 382ed39 commit ecc1efe
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 7 deletions.
27 changes: 27 additions & 0 deletions src/sdbus/sd_bus_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@
utf_8_bytes; \
})

#define SD_BUS_PY_UNICODE_AS_BYTES_GOTO_FAIL(py_unicode) \
({ \
PyObject* utf_8_bytes = PyUnicode_AsUTF8String(py_unicode); \
if (utf_8_bytes == NULL) { \
goto fail; \
} \
utf_8_bytes; \
})

#define SD_BUS_PY_BYTES_AS_CHAR_PTR(py_bytes) \
({ \
const char* new_char_ptr = PyBytes_AsString(py_bytes); \
Expand All @@ -105,6 +114,15 @@
new_char_ptr; \
})

#define SD_BUS_PY_BYTES_AS_CHAR_PTR_GOTO_FAIL(py_bytes) \
({ \
const char* new_char_ptr = PyBytes_AsString(py_bytes); \
if (new_char_ptr == NULL) { \
goto fail; \
} \
new_char_ptr; \
})

#ifndef Py_LIMITED_API
#define SD_BUS_PY_UNICODE_AS_CHAR_PTR(py_object) \
({ \
Expand All @@ -115,6 +133,15 @@
new_char_ptr; \
})

#define SD_BUS_PY_UNICODE_AS_CHAR_PTR_GOTO_FAIL(py_object) \
({ \
const char* new_char_ptr = PyUnicode_AsUTF8(py_object); \
if (new_char_ptr == NULL) { \
goto fail; \
} \
new_char_ptr; \
})

#define SD_BUS_PY_UNICODE_AS_CHAR_PTR_OPTIONAL(py_object) \
({ \
const char* new_char_ptr_or_null = NULL; \
Expand Down
23 changes: 21 additions & 2 deletions src/sdbus/sd_bus_internals_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,26 @@ PyType_Spec SdBusInterfaceType = {
},
};

static int set_dbus_error_from_python_exception(sd_bus_error* ret_error) {
PyObject* current_exception = PyErr_Occurred();
if (NULL == current_exception) {
return 0;
}
#ifdef Py_LIMITED_API
PyObject* dbus_error_bytes CLEANUP_PY_OBJECT = NULL;
#endif
PyObject* dbus_error_str = CALL_PYTHON_GOTO_FAIL(PyDict_GetItem(exception_to_dbus_error_dict, current_exception));
#ifndef Py_LIMITED_API
const char* dbus_error_char_ptr = SD_BUS_PY_UNICODE_AS_CHAR_PTR_GOTO_FAIL(dbus_error_str);
#else
dbus_error_bytes = SD_BUS_PY_UNICODE_AS_BYTES_GOTO_FAIL(dbus_error_str);
const char* dbus_error_char_ptr = SD_BUS_PY_BYTES_AS_CHAR_PTR_GOTO_FAIL(dbus_error_bytes);
#endif
return sd_bus_error_set(ret_error, dbus_error_char_ptr, "");
fail:
return sd_bus_error_set(ret_error, SD_BUS_ERROR_FAILED, "");
}

static int _SdBusInterface_callback(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) {
// TODO: Better error handling
SdBusInterfaceObject* self = userdata;
Expand Down Expand Up @@ -429,8 +449,7 @@ static int _SdBusInterface_property_get_callback(sd_bus* Py_UNUSED(bus),
Py_XDECREF(CALL_PYTHON_GOTO_FAIL(PyObject_CallFunctionObjArgs(get_call, new_message, NULL)));
return 0;
fail:
sd_bus_error_set(ret_error, SD_BUS_ERROR_FAILED, "");
return -1;
return set_dbus_error_from_python_exception(ret_error);
}

static int _SdBusInterface_property_set_callback(sd_bus* Py_UNUSED(bus),
Expand Down
31 changes: 26 additions & 5 deletions test/test_low_level_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,25 @@
HELLO_WORLD = 'Hello, world!'


class DbusDerivePropertydError(DbusFailedError):
dbus_error_name = 'org.example.PropertyError'


class IndependentError(Exception):
...


class InterfaceWithErrors(
DbusInterfaceCommonAsync,
interface_name='org.example.test',
):
@dbus_property_async('s')
def test_str(self) -> str:
raise RuntimeError
def indep_err_getter(self) -> str:
raise IndependentError

@dbus_property_async('s')
def derrive_err_getter(self) -> str:
raise DbusDerivePropertydError

@dbus_method_async(result_signature='s')
async def hello_error(self) -> str:
Expand Down Expand Up @@ -70,12 +82,21 @@ def silence_exceptions(*args: Any, **kwrags: Any) -> None:

loop.set_exception_handler(silence_exceptions)

async def test_property_getter_error(self) -> None:
async def test_property_getter_independent_error(self) -> None:
with self.assertRaises(DbusFailedError) as cm:
await wait_for(self.test_object_connection.test_str.get_async(),
timeout=1)
await wait_for(
self.test_object_connection.indep_err_getter.get_async(),
timeout=1,
)

should_be_dbus_failed = cm.exception
self.assertIs(should_be_dbus_failed.__class__, DbusFailedError)

await self.test_object_connection.hello_world()

async def test_property_getter_derived_error(self) -> None:
with self.assertRaises(DbusDerivePropertydError):
await wait_for(
self.test_object_connection.derrive_err_getter.get_async(),
timeout=1,
)

0 comments on commit ecc1efe

Please sign in to comment.