bt2: add support for the "query info" API
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Sat, 11 Feb 2017 18:27:06 +0000 (13:27 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sun, 28 May 2017 16:57:38 +0000 (12:57 -0400)
Any user-defined component class can now define a _query_info() static
method which accepts an action name and parameters (value object, or
None) and returns a bt2.create_value()-compatible object:

    class MySink(bt2.UserSinkComponent):
        def _consume(self):
            pass

        @staticmethod
        def _query_info(action, params):
            if action == 'get-stuff':
                return {
                    'stuff': get_stuff(),
                    'id': get_id()
                }
            elif action == 'get-thing':
                return the_thing()

This static method can raise any exception: the caller of
bt_component_class_query_info() gets NULL.

On the Python side, either with a user-defined component class or with
a component class wrapper:

    results = comp_class.query_info('get-stuff', {'name': 'wrerzvr'})

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
bindings/python/bt2/component.py
bindings/python/bt2/native_btcomponentclass.i

index beab3cd9a1c57c9ca472f2291d371942f524b825..ebfe12629a1f7f288ebf709d47528e5b57d36e9a 100644 (file)
@@ -23,6 +23,7 @@
 from bt2 import native_bt, object, utils
 import bt2.notification_iterator
 import collections.abc
+import bt2.values
 import sys
 import bt2
 
@@ -44,6 +45,9 @@ class _GenericComponentClass(object._Object):
     def help(self):
         return native_bt.component_class_get_help(self._ptr)
 
+    def query_info(self, action, params=None):
+        return _query_info(self._ptr, action, params)
+
     def __call__(self, params=None, name=None):
         params = bt2.create_value(params)
         comp_ptr = native_bt.component_create_with_init_method_data(self._ptr,
@@ -198,6 +202,27 @@ def _trim_docstring(docstring):
     return '\n'.join(trimmed)
 
 
+def _query_info(comp_cls_ptr, action, params):
+    utils._check_str(action)
+
+    if params is None:
+        params_ptr = native_bt.value_null
+    else:
+        params = bt2.create_value(params)
+        params_ptr = params._ptr
+
+    results_ptr = native_bt.component_class_query_info(comp_cls_ptr, action,
+                                                       params_ptr)
+
+    if results_ptr is None:
+        raise bt2.Error('cannot query info with action "{}"'.format(action))
+
+    if results_ptr == native_bt.value_null:
+        return
+
+    return bt2.values._create_from_ptr(results_ptr)
+
+
 # Metaclass for component classes defined by Python code.
 #
 # The Python user can create a standard Python class which inherits one
@@ -405,6 +430,29 @@ class _UserComponentType(type):
     def addr(cls):
         return int(cls._cc_ptr)
 
+    def query_info(cls, action, params=None):
+        return _query_info(cls._cc_ptr, action, params)
+
+    def _query_info_from_bt(cls, action, params):
+        # this can raise, in which case the native call to
+        # bt_component_class_query_info() returns NULL
+        results = cls._query_info(action, params)
+        results = bt2.create_value(results)
+
+        if results is None:
+            results_addr = int(native_bt.value_null)
+        else:
+            # steal the underlying native value object for the caller
+            results_addr = int(results._ptr)
+            results._ptr = None
+
+        return results_addr
+
+    @staticmethod
+    def _query_info(action, params):
+        # BT catches this and returns NULL to the user
+        raise NotImplementedError
+
     def __del__(cls):
         if hasattr(cls, '_cc_ptr'):
             native_bt.put(cls._cc_ptr)
index 53652b9acb7510b46e888635589f9eec56899f21..6acfdb6e3d0f4d9cf04c919f014000f9420ddd2d 100644 (file)
@@ -51,6 +51,9 @@ const char *bt_component_class_get_description(
                struct bt_component_class *component_class);
 const char *bt_component_class_get_help(
                struct bt_component_class *component_class);
+struct bt_value *bt_component_class_query_info(
+               struct bt_component_class *component_class,
+               const char *action, struct bt_value *params);
 enum bt_component_class_type bt_component_class_get_type(
                struct bt_component_class *component_class);
 
@@ -560,6 +563,70 @@ static void bt_py3_cc_destroy(struct bt_component *component)
        }
 }
 
+static struct bt_value *bt_py3_cc_query_info(
+               struct bt_component_class *component_class,
+               const char *action, struct bt_value *params)
+{
+       PyObject *py_cls = NULL;
+       PyObject *py_params = NULL;
+       PyObject *py_query_info_func = NULL;
+       PyObject *py_action = NULL;
+       PyObject *py_results_addr = NULL;
+       struct bt_value *results = NULL;
+
+       py_cls = lookup_cc_ptr_to_py_cls(component_class);
+       if (!py_cls) {
+               goto error;
+       }
+
+       py_params = bt_py3_bt_value_from_ptr(params);
+       if (!py_params) {
+               goto error;
+       }
+
+       py_action = SWIG_FromCharPtr(action);
+       if (!py_action) {
+               goto error;
+       }
+
+       py_results_addr = PyObject_CallMethod(py_cls,
+               "_query_info_from_bt", "(OO)", py_action, py_params);
+       if (!py_results_addr) {
+               goto error;
+       }
+
+       /*
+        * The returned object, on success, is an integer object
+        * (PyLong) containing the address of a BT value object
+        * (which is now ours).
+        */
+       results = (struct bt_value *) PyLong_AsUnsignedLongLong(
+               py_results_addr);
+
+       /* Clear potential overflow error; should never happen */
+       if (PyErr_Occurred()) {
+               results = NULL;
+               goto error;
+       }
+
+       if (!results) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_PUT(results);
+       PyErr_Clear();
+
+end:
+       Py_XDECREF(py_params);
+       Py_XDECREF(py_query_info_func);
+       Py_XDECREF(py_action);
+       Py_XDECREF(py_results_addr);
+       return results;
+}
+
 static enum bt_notification_iterator_status bt_py3_exc_to_notif_iter_status(void)
 {
        enum bt_notification_iterator_status status =
@@ -728,13 +795,14 @@ static struct bt_notification *bt_py3_cc_notification_iterator_get(
        /*
         * The returned object, on success, is an integer object
         * (PyLong) containing the address of a BT notification
-        * object (which is not ours).
+        * object (which is now ours).
         */
        notif = (struct bt_notification *) PyLong_AsUnsignedLongLong(
                py_get_method_result);
 
        /* Clear potential overflow error; should never happen */
        if (PyErr_Occurred()) {
+               notif = NULL;
                goto error;
        }
 
@@ -914,6 +982,12 @@ static int bt_py3_cc_set_optional_attrs_methods(struct bt_component_class *cc,
                goto end;
        }
 
+       ret = bt_component_class_set_query_info_method(cc,
+               bt_py3_cc_query_info);
+       if (ret) {
+               goto end;
+       }
+
 end:
        return ret;
 }
This page took 0.02977 seconds and 4 git commands to generate.