Add Babeltrace 2 Python bindings
[babeltrace.git] / bindings / python / bt2 / component.py
CommitLineData
81447b5b
PP
1# The MIT License (MIT)
2#
3# Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in
13# all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21# THE SOFTWARE.
22
23from bt2 import native_bt, object, utils
24import bt2.notification_iterator
25import collections.abc
26import bt2
27
28
29# This class wraps a component class pointer. This component class could
30# have been created by Python code, but since we only have the pointer,
31# we can only wrap it in a generic way and lose the original Python
32# class.
33class _GenericComponentClass(object._Object):
34 @property
35 def name(self):
36 return native_bt.component_class_get_name(self._ptr)
37
38 @property
39 def description(self):
40 return native_bt.component_class_get_description(self._ptr)
41
42 def __call__(self, params=None, name=None):
43 params = bt2.create_value(params)
44 comp_ptr = native_bt.component_create_with_init_method_data(self._ptr,
45 name,
46 params._ptr,
47 None)
48
49 if comp_ptr is None:
50 raise bt2.CreationError('cannot create component object')
51
52 return _create_generic_component_from_ptr(comp_ptr)
53
54
55class _GenericSourceComponentClass(_GenericComponentClass):
56 pass
57
58
59class _GenericFilterComponentClass(_GenericComponentClass):
60 pass
61
62
63class _GenericSinkComponentClass(_GenericComponentClass):
64 pass
65
66
67# This class holds the methods which are common to both generic
68# component objects and Python user component objects. They use the
69# internal native _ptr, however it was set, to call native API
70# functions.
71class _CommonComponentMethods:
72 @property
73 def name(self):
74 return native_bt.component_get_name(self._ptr)
75
76 @property
77 def component_class(self):
78 cc_ptr = native_bt.component_get_class(self._ptr)
79 utils._handle_ptr(cc_ptr, "cannot get component object's class object")
80 return _create_generic_component_class_from_ptr(cc_ptr)
81
82 def _handle_status(self, status, gen_error_msg):
83 if status == native_bt.COMPONENT_STATUS_END:
84 raise bt2.Stop
85 elif status == native_bt.COMPONENT_STATUS_AGAIN:
86 raise bt2.TryAgain
87 elif status == native_bt.COMPONENT_STATUS_UNSUPPORTED:
88 raise bt2.UnsupportedFeature
89 elif status < 0:
90 raise bt2.Error(gen_error_msg)
91
92
93class _CommonSourceComponentMethods(_CommonComponentMethods):
94 def create_notification_iterator(self):
95 iter_ptr = native_bt.component_source_create_notification_iterator_with_init_method_data(self._ptr, None)
96
97 if iter_ptr is None:
98 raise bt2.CreationError('cannot create notification iterator object')
99
100 return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(iter_ptr)
101
102
103class _CommonFilterComponentMethods(_CommonComponentMethods):
104 def create_notification_iterator(self):
105 iter_ptr = native_bt.component_filter_create_notification_iterator_with_init_method_data(self._ptr, None)
106
107 if iter_ptr is None:
108 raise bt2.CreationError('cannot create notification iterator object')
109
110 return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(iter_ptr)
111
112 def add_notification_iterator(self, notif_iter):
113 utils._check_type(notif_iter, bt2.notification_iterator._GenericNotificationIteratorMethods)
114 status = native_bt.component_filter_add_iterator(self._ptr, notif_iter._ptr)
115 self._handle_status(status, 'unexpected error: cannot add notification iterator to filter component object')
116
117
118class _CommonSinkComponentMethods(_CommonComponentMethods):
119 def add_notification_iterator(self, notif_iter):
120 utils._check_type(notif_iter, bt2.notification_iterator._GenericNotificationIteratorMethods)
121 status = native_bt.component_sink_add_iterator(self._ptr, notif_iter._ptr)
122 self._handle_status(status, 'unexpected error: cannot add notification iterator to sink component object')
123
124 def consume(self):
125 status = native_bt.component_sink_consume(self._ptr)
126 self._handle_status(status, 'unexpected error: cannot consume sink component object')
127
128
129# This is analogous to _GenericSourceComponentClass, but for source
130# component objects.
131class _GenericSourceComponent(object._Object, _CommonSourceComponentMethods):
132 pass
133
134
135# This is analogous to _GenericFilterComponentClass, but for filter
136# component objects.
137class _GenericFilterComponent(object._Object, _CommonFilterComponentMethods):
138 pass
139
140
141# This is analogous to _GenericSinkComponentClass, but for sink
142# component objects.
143class _GenericSinkComponent(object._Object, _CommonSinkComponentMethods):
144 pass
145
146
147_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS = {
148 native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponent,
149 native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponent,
150 native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponent,
151}
152
153
154_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_CLS = {
155 native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponentClass,
156 native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponentClass,
157 native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponentClass,
158}
159
160
161def _create_generic_component_from_ptr(ptr):
162 comp_cls_type = native_bt.component_get_class_type(ptr)
163 return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS[comp_cls_type]._create_from_ptr(ptr)
164
165
166def _create_generic_component_class_from_ptr(ptr):
167 comp_cls_type = native_bt.component_class_get_type(ptr)
168 return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_CLS[comp_cls_type]._create_from_ptr(ptr)
169
170
171# Metaclass for component classes defined by Python code.
172#
173# The Python user can create a standard Python class which inherits one
174# of the three base classes (UserSourceComponent, UserFilterComponent,
175# or UserSinkComponent). Those base classes set this class
176# (_UserComponentType) as their metaclass.
177#
178# Once the body of a user-defined component class is executed, this
179# metaclass is used to create and initialize the class. The metaclass
180# creates a native BT component class of the corresponding type and
181# associates it with this user-defined class. The metaclass also defines
182# class methods like the `name` and `description` properties to match
183# the _GenericComponentClass interface.
184#
185# The component class name which is used is either:
186#
187# * The `name` parameter of the class:
188#
189# class MySink(bt2.SinkComponent, name='my-custom-sink'):
190# ...
191#
192# * If the `name` class parameter is not used: the name of the class
193# itself (`MySink` in the example above).
194#
195# The component class description which is used is the user-defined
196# class's docstring:
197#
198# class MySink(bt2.SinkComponent):
199# 'Description goes here'
200# ...
201#
202# A user-defined Python component class can have an __init__() method
203# which must at least accept the `params` and `name` arguments:
204#
205# def __init__(self, params, name, something_else):
206# ...
207#
208# The user-defined component class can also have a _destroy() method
209# (do NOT use __del__()) to be notified when the component object is
210# (really) destroyed.
211#
212# User-defined source and filter component classes must use the
213# `notification_iterator_class` class parameter to specify the
214# notification iterator class to use for this component class:
215#
216# class MyNotificationIterator(bt2.UserNotificationIterator):
217# ...
218#
219# class MySource(bt2.UserSourceComponent,
220# notification_iterator_class=MyNotificationIterator):
221# ...
222#
223# This notification iterator class must inherit
224# bt2.UserNotificationIterator, and it must define the _get() and
225# _next() methods. The notification iterator class can also define an
226# __init__() method: this method has access to the original Python
227# component object which was used to create it as the `component`
228# property. The notification iterator class can also define a _destroy()
229# method (again, do NOT use __del__()): this is called when the
230# notification iterator is (really) destroyed.
231#
232# When the user-defined class is destroyed, this metaclass's __del__()
233# method is called: the native BT component class pointer is put (not
234# needed anymore, at least not by any Python code since all references
235# are dropped for __del__() to be called).
236class _UserComponentType(type):
237 # __new__() is used to catch custom class parameters
238 def __new__(meta_cls, class_name, bases, attrs, **kwargs):
239 return super().__new__(meta_cls, class_name, bases, attrs)
240
241 def __init__(cls, class_name, bases, namespace, **kwargs):
242 super().__init__(class_name, bases, namespace)
243
244 # skip our own bases; they are never directly instantiated by the user
245 if class_name in ('_UserComponent', 'UserSourceComponent', 'UserFilterComponent', 'UserSinkComponent'):
246 return
247
248 comp_cls_name = kwargs.get('name', class_name)
249 comp_cls_descr = getattr(cls, '__doc__', None)
250 iter_cls = kwargs.get('notification_iterator_class')
251
252 if UserSourceComponent in bases:
253 _UserComponentType._set_iterator_class(cls, iter_cls)
254 has_seek_time = _UserComponentType._has_seek_to_time_method(cls._iter_cls)
255 cc_ptr = native_bt.py3_component_class_source_create(cls,
256 comp_cls_name,
257 comp_cls_descr,
258 has_seek_time)
259 elif UserFilterComponent in bases:
260 _UserComponentType._set_iterator_class(cls, iter_cls)
261 has_seek_time = _UserComponentType._has_seek_to_time_method(cls._iter_cls)
262 cc_ptr = native_bt.py3_component_class_filter_create(cls,
263 comp_cls_name,
264 comp_cls_descr,
265 has_seek_time)
266 elif UserSinkComponent in bases:
267 if not hasattr(cls, '_consume'):
268 raise bt2.IncompleteUserClassError("cannot create component class '{}': missing a _consume() method".format(class_name))
269
270 cc_ptr = native_bt.py3_component_class_sink_create(cls,
271 comp_cls_name,
272 comp_cls_descr)
273 else:
274 raise bt2.IncompleteUserClassError("cannot find a known component class base in the bases of '{}'".format(class_name))
275
276 if cc_ptr is None:
277 raise bt2.CreationError("cannot create component class '{}'".format(class_name))
278
279 cls._cc_ptr = cc_ptr
280
281 def __call__(cls, *args, **kwargs):
282 # create instance
283 self = cls.__new__(cls)
284
285 # assign native component pointer received from caller
286 self._ptr = kwargs.get('__comp_ptr')
287 name = kwargs.get('name')
288
289 if self._ptr is None:
290 # called from Python code
291 self._belongs_to_native_component = False
292
293 # py3_component_create() will call self.__init__() with the
294 # desired arguments and keyword arguments. This is needed
295 # because functions such as
296 # bt_component_sink_set_minimum_input_count() can only be
297 # called _during_ the bt_component_create() call (in
298 # Python words: during self.__init__()).
299 #
300 # The arguments and keyword arguments to use for
301 # self.__init__() are put in the object itself to find them
302 # from the bt_component_create() function.
303 self._init_args = args
304 self._init_kwargs = kwargs
305 native_bt.py3_component_create(cls._cc_ptr, self, name)
306
307 # At this point, self._ptr should be set to non-None. If
308 # it's not, an error occured during the
309 # native_bt.py3_component_create() call. We consider this a
310 # creation error.
311 if self._ptr is None:
312 raise bt2.CreationError("cannot create component object from component class '{}'".format(cls.__name__))
313 else:
314 # Called from non-Python code (within
315 # bt_component_create()): call __init__() here, after
316 # removing the __comp_ptr keyword argument which is just for
317 # this __call__() method.
318 self._belongs_to_native_component = True
319 del kwargs['__comp_ptr']
320
321 # inject `name` into the keyword arguments
322 kwargs['name'] = self.name
323 self.__init__(*args, **kwargs)
324
325 return self
326
327 @staticmethod
328 def _has_seek_to_time_method(iter_cls):
329 return hasattr(iter_cls, '_seek_to_time')
330
331 @staticmethod
332 def _set_iterator_class(cls, iter_cls):
333 if iter_cls is None:
334 raise bt2.IncompleteUserClassError("cannot create component class '{}': missing notification iterator class".format(cls.__name__))
335
336 if not issubclass(iter_cls, bt2.notification_iterator.UserNotificationIterator):
337 raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class does not inherit bt2.UserNotificationIterator".format(cls.__name__))
338
339 if not hasattr(iter_cls, '_get'):
340 raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class is missing a _get() method".format(cls.__name__))
341
342 if not hasattr(iter_cls, '_next'):
343 raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class is missing a _next() method".format(cls.__name__))
344
345 cls._iter_cls = iter_cls
346
347 @property
348 def name(cls):
349 return native_bt.component_class_get_name(cls._cc_ptr)
350
351 @property
352 def description(cls):
353 return native_bt.component_class_get_description(cls._cc_ptr)
354
355 @property
356 def addr(cls):
357 return int(cls._cc_ptr)
358
359 def __del__(cls):
360 if hasattr(cls, '_cc_ptr'):
361 native_bt.put(cls._cc_ptr)
362
363
364class _ComponentInputNotificationIterators(collections.abc.Sequence):
365 def __init__(self, comp, count_func, get_func):
366 self._comp = comp
367 self._count_func = count_func
368 self._get_func = get_func
369
370 def __len__(self):
371 status, count = self._count_func(self._comp._ptr)
372 utils._handle_ret(status, "cannot get component object's input notification iterator count")
373 return count
374
375 def __getitem__(self, index):
376 utils._check_uint64(index)
377
378 if index >= len(self):
379 raise IndexError
380
381 notif_iter_ptr = self._get_func(self._comp._ptr, index)
382 utils._handle_ptr(notif_iter_ptr, "cannot get component object's input notification iterator")
383 return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr)
384
385
386class _UserComponent(metaclass=_UserComponentType):
387 @property
388 def addr(self):
389 return int(self._ptr)
390
391 def __init__(self, *args, **kwargs):
392 pass
393
394 def _destroy(self):
395 pass
396
397 def __del__(self):
398 if not self._belongs_to_native_component:
399 self._belongs_to_native_component = True
400 native_bt.py3_component_on_del(self)
401 native_bt.put(self._ptr)
402
403
404class UserSourceComponent(_UserComponent, _CommonSourceComponentMethods):
405 pass
406
407
408class UserFilterComponent(_UserComponent, _CommonFilterComponentMethods):
409 def _set_minimum_input_notification_iterator_count(self, count):
410 utils._check_uint64(count)
411 status = native_bt.component_filter_set_minimum_input_count(self._ptr, count)
412 self._handle_status(status, "unexpected error: cannot set filter component object's minimum input notification iterator count")
413
414 _minimum_input_notification_iterator_count = property(fset=_set_minimum_input_notification_iterator_count)
415
416 def _set_maximum_input_notification_iterator_count(self, count):
417 utils._check_uint64(count)
418 status = native_bt.component_filter_set_maximum_input_count(self._ptr, count)
419 self._handle_status(status, "unexpected error: cannot set filter component object's maximum input notification iterator count")
420
421 _maximum_input_notification_iterator_count = property(fset=_set_maximum_input_notification_iterator_count)
422
423 @property
424 def _input_notification_iterators(self):
425 return _ComponentInputNotificationIterators(self,
426 native_bt.component_filter_get_input_count,
427 native_bt.component_filter_get_input_iterator_private)
428
429 def _add_iterator_from_bt(self, notif_iter_ptr):
430 notif_iter = bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr)
431 self._add_notification_iterator(notif_iter)
432
433 def _add_notification_iterator(self, notif_iter):
434 pass
435
436
437class UserSinkComponent(_UserComponent, _CommonSinkComponentMethods):
438 def _set_minimum_input_notification_iterator_count(self, count):
439 utils._check_uint64(count)
440 status = native_bt.component_sink_set_minimum_input_count(self._ptr, count)
441 self._handle_status(status, "unexpected error: cannot set sink component object's minimum input notification iterator count")
442
443 _minimum_input_notification_iterator_count = property(fset=_set_minimum_input_notification_iterator_count)
444
445 def _set_maximum_input_notification_iterator_count(self, count):
446 utils._check_uint64(count)
447 status = native_bt.component_sink_set_maximum_input_count(self._ptr, count)
448 self._handle_status(status, "unexpected error: cannot set sink component object's maximum input notification iterator count")
449
450 _maximum_input_notification_iterator_count = property(fset=_set_maximum_input_notification_iterator_count)
451
452 @property
453 def _input_notification_iterators(self):
454 return _ComponentInputNotificationIterators(self,
455 native_bt.component_sink_get_input_count,
456 native_bt.component_sink_get_input_iterator_private)
457
458 def _add_iterator_from_bt(self, notif_iter_ptr):
459 notif_iter = bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr)
460 self._add_notification_iterator(notif_iter)
461
462 def _add_notification_iterator(self, notif_iter):
463 pass
This page took 0.040388 seconds and 4 git commands to generate.