Add Babeltrace 2 Python bindings
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Sat, 4 Feb 2017 03:38:48 +0000 (22:38 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sun, 28 May 2017 16:57:37 +0000 (12:57 -0400)
This patch adds new Babeltrace 2 Python bindings to the Babeltrace
project.

Those bindings are compatible with Python 3 (only).

The new bindings still make use of SWIG to simplify the Python-to-native
and native-to-Python calls.

The new bindings, from Python's point of view, are available in the new
`bt2` package. This package imports (__init__.py does) everything that
is public from its modules, so that every public name is available as
bt2.something. This is considered more Pythonic than asking the user to
import specific modules from a given package.

The goal with this, to keep the "old" `babeltrace` package working, is
to make the `babeltrace` package a simple Python-only wrapper of the bt2
package (which offers much more, as you will discover in this commit
message).

Summary of features:

* All the current Babeltrace 2 APIs are wrapped:
  * Clock class
  * Component, component class, notification iterator, and notification
  * CTF writer, CTF writer clock, CTF writer stream
  * Event and event classe
  * Packet, stream, and stream class
  * Fields and field types
  * Plugin
  * Trace
  * Values
* Automatic BT reference count handling for the user (just like the
  `babeltrace` package does).
* Type checking of the arguments of each method/function (before it gets
  to SWIG, where the exception is not as obvious).
* Package exceptions:
  * Error
  * CreationError
  * FrozenError
  * UnsupportedFeature
  * TryAgain
  * Stop
  * IncompleteUserClassError
* Full support of user component classes.
* Package is as Pythonic as possible, with extensive use of collection
  ABCs, number and other protocols, iterators, properties, inheritance
  for the user, and exceptions.
* Easy to extend if we ever add new BT objects or APIs.
* The bindings only use the public Babeltrace 2 C API; they could be
  built outside the Babeltrace repository without altering the code.

Build system
============
Makefile.am does pretty much the same job as previously, although it is
organized so that it's easy to add Python modules and partial SWIG
interfaces. All the rules and commands are built from two simple lists.

SWIG
====
I created one SWIG interface file (.i) for each Babeltrace API. All the
native_bt*.i files are included at the end of native_bt.i. This is the
only input for SWIG.

native_bt.i does more than including partial interface files. It adds
rules to remove the bt_ and BT_ prefixes of all the wrapped functions
and enumeration items.

native_bt.i also adds a few custom typemaps to convert special arguments
back and forth between Python and the C API. For example,
`const char **BTOUTSTR` is a typemap to append a Python string (Unicode
object) to the current SWIG result tuple when the argument is named
as such, as in:

int bt_ctf_field_type_enumeration_get_mapping_signed(
        struct bt_ctf_field_type *enum_field_type, int index,
        const char **BTOUTSTR, int64_t *OUTPUT, int64_t *OUTPUT);

Note that, in example above, OUTPUT is a typemap provided by SWIG for
very simple types.

Another typemap is BTUUID to accept and return Babeltrace UUIDs as
Python `bytes` objects:

    BTUUID bt_ctf_clock_class_get_uuid(struct bt_ctf_clock_class *clock_class);

    int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class,
            BTUUID uuid);

Modules
=======
I'll now go into the details of each module of the bt2 package, in a
relevant order.

Most of the objects described below are comparable. Their compare
function usually start with a simple address comparison, and then falls
back to a rich comparison (sometimes implemented in Python if the
equivalent C function is missing).

utils
-----
Small utility functions: type checking, automatic exception raising,
power of two, etc.

object
------
bt_object API and reference counting.

Base class for a wrapped BT object (with reference counting), not
meant to be instantiated by the user.

Provides:

* self._ptr: SWIG pointer (native BT object), for the functions of
  the bt2 package (private).

* __init__(): called by subclass to wrap a SWIG pointer.

* addr(): public property which returns the address (integer, not the
  SWIG pointer object) of the object, mostly for debug purposes (for the
  user) and for a user to know if two bt2 objects actually wrap the
  same BT native object.

* _create_from_ptr(): class method to wrap a given SWIG pointer as an
  objet of a given class:

      event_class = EventClass._create_from_ptr(ptr)

* __repr__(): Shows the type of the object and its native address.

* __del__(): Puts its BT object reference (calls bt_put()).

object.py also contains _Freezable, a mixin for publicly freezable
objects.

values
------
bt_value API.

The classes in bt2.values are full Python wrappers of the native
bt_value types.

Features:

* BoolValue acts just like Python's bool.
* IntegerValue acts just like Python's int.
* FloatValue acts just like Python's float.
* StringValue acts just like Python's str.
* ArrayValue acts just like Python's list.
* MapValue acts just like Python's dict.
* bt_value_null is the equivalent of None.

In other words:

* All types are comparable and copyable (copy/deep copy).
* All the needed operators are implemented to make the value objects
  act like Python native objects.
* Number classes inherit ABCs in the numbers module.

There's also a bt2.create_value() function which returns a bt2.values
object from any value (bt2.values or native Python object).

I decided to wrap actual bt_value objects instead of always converting
from native Python objects to them and vice versa, because they can
still be shared in this case. Otherwise the conversion would remove this
sharing feature which is implicit with BT reference counting.

A few examples:

    my_int = bt2.IntegerValue(23)
    my_int += 283
    print(my_int)
    some_flt = bt2.FloatValue(45.3)
    print(my_int * some_flt)

    s = bt2.create_value('hello there')
    print(s[3:9])

    my_map = bt2.create_value({'ho': 23, 'meow': (4, 5, False, None)})
    print(my_map)
    print(my_map['meow'][1] >= 5)
    print(my_map.addr)

    for k, v in my_map.items():
        print('{}: {}'.format(k, v))

field_types
-----------
bt_ctf_field_type API.

This looks pretty much like the original field type objects of
the `babeltrace.writer` module, except for the following features:

* `Declaration` suffix is replaced with `FieldType` suffix to match
  the C API convention.

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      int_ft = bt2.IntegerFieldType(size=23, align=16, is_signed=True,
                                    base=8, mapped_clock_class=cc)

* Enumeration field type honors the sequence protocol:

      for mapping in enum_ft:
          print(mapping.name, mapping.lower, mapping.upper)

* Enumeration field type mapping iterator support, e.g.:

      for mapping in enum_ft.mappings_by_name('APPLE'):
          print(mapping.name, mapping.lower, mapping.upper)

  It's easy to add the mappings of another enumeration field type:

      enum_ft += other_enum_ft

* EnumerationFieldType inherits IntegerFieldType so that you can do:

      enum_ft = bt2.EnumerationFieldType(size=23, align=16,
                                         is_signed=True,
                                         base=bt2.Base.HEXADECIMAL,
                                         byte_order=bt2.ByteOrder.BIG_ENDIAN)
      print(enum_ft.size)
      enum_ft.is_signed = False

  instead of getting the underlying integer field type object manually.

* Structure and variant field types honor the mapping protocol:

      for name, ft in struct_ft:
          print(name, ft)

* You can set the `min_alignment` property of a structure field type
  (but you cannot get it), and you can get its `alignment` property
  (but you cannot set it). Those names represent exactly what they
  mean (less ambiguous than the C API equivalent IMO).

* You can instantiate a field object from a field type object by
  "calling" the field type object. This is closer to the concept of
  a class in the Python world:

      my_field = int_ft()

fields
------
bt_ctf_field API.

A bt2._Field is the result of instantiating a field type object. The
type (and all its subclasses) starts with an underscore because you
cannot instatiate them directly (possibly with an initial value):

    int_field = bt2.IntegerFieldType(32)(17)
    str_field = bt2.StringFieldType()('hello there')

Features:

* Copy, deep copy, and comparison support.
* IntegerField and EnumerationField act just like Python's int.
* FloatingPointNumberField acts just like Python's float.
* StringField acts just like Python's str.
* ArrayField and SequenceField honor the mutable sequence protocol.
* StructureField honors the mutable mapping protocol.

Field objects are just like value objects: they act like native Python
objects:

    int_field = bt2.IntegerFieldType(32)(152)
    int_field += 194
    print(int_field % 51)

    str_field = bt2.StringFieldType()('hello there')
    print(len(str_field))
    str_field += ' World!'
    print(str_field)

    print(struct_field['oh']['noes'][23])

    print(variant_field.selected_field)

    for mapping in enum_field.mappings:
        print(mapping.name, mapping.lower, mapping.upper)

clock_class
-----------
bt_ctf_clock_class API.

A straightforward clock class wrapper, pretty much equivalent to the
previous one (CTFWriter.Clock), except that:

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      cc = bt2.ClockClass('my_clock', frequency=18000000,
                          is_absolute=True, precision=500,
                          offset=bt2.ClockClassOffset(seconds=22,
                                                      cycles=187232))

* A clock offset is represented with a ClockClassOffset object.

* You can create a clock value from a clock class object with a given
  number of cycles:

      clock_val = cc.create_clock_value(234)

  This clock value object is copyable, deep-copyable, and comparable.
  You can get its number of cycles (raw value), its clock class,
  and the number of nanoseconds since Epoch:

      print(clock_val.ns_from_epoch())

event_class
-----------
bt_ctf_event_class API.

Features:

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      ec = bt2.EventClass('my_event', id=23, payload_field_type=ft)

* Parent stream class access (returns None if not set):

      print(ec.stream_class.id)

* Attributes property which honor the mutable mapping protocol:

      event_class.attributes['model.emf.uri'] = 'http://diamon.org/'

* Payload and context field type R/W properties.

* Call the class to instantiate an event:

      my_event = my_event_class()

stream_class
------------
bt_ctf_stream_class API.

Features:

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      sc = bt2.StreamClass(name='my_stream_class',
                           event_header_field_type=ev_header_ft,
                           event_classes=(ec1, ec2, ec3))

* Parent trace access (returns None if not set):

      print(sc.trace)

* A stream class object honors the mapping protocol to access its
  event class children by name:

      ec = sc['my_event']

      for ec_name, ec in sc.items():
          print(ec_name, ec.id)

* Packet context, event header, and stream event context field type R/W
  properties.

* Call the class to instantiate a stream:

      my_stream = my_stream_class('optional_name')

trace
-----
bt_ctf_trace API.

Features:

* Copy, deep copy, and comparison support.

* You can pass all the properties of an object at construction time:

      trace = bt2.Trace(name='my_trace',
                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
                        env={'tracer_name': 'BestTracer', 'custom': 23},
                        packet_header_field_type=pkt_head_ft,
                        clock_classes=(cc1, cc2),
                        stream_classes=(sc1, sc2))

* A trace object honors the mapping protocol to access its stream
  class children by ID:

      sc = trace[23]

      for sc_id, sc in trace.items():
          print(sc_id, len(sc))

* Trace environment honors the mutable mapping protocol:

      trace.env['tracer_major'] = 1
      trace.env['tracer_minor'] = 2
      trace.env['uname_r'] = '4.7.2-1-ARCH'

      for k, v in trace.env.items():
          print(k, v)

* Trace clock classes honor the mapping protocol:

      cc = trace.clock_classes['my_clock']

      for cc_name, cc in trace.clock_classes.items():
          print(cc_name, cc.frequency)

* Packet header field type R/W property.

event
-----
bt_ctf_event API.

Features:

* Copy, deep copy, and comparison support.

* Event class, name, ID, and stream read-only properties.

* Event object implements __getitem__() to retrieve a field in different
  scopes:

      # can be found in context, if not in payload, for example
      my_event['cpu_id']

  This is the same behaviour as in the `babeltrace` package. Use the
  specific field properties instead of a field_with_scope() method:

      print(my_event.context_field['specific'])

* Packet property to set and get the event's packet:

      event.packet = my_packet

* Header, stream event context, context, and payload field R/W
  properties.

* You can assign a clock value mapped to a specific clock class and
  get it back:

      event.set_clock_value(some_clock_value)
      print(event.get_clock_value(some_cc).ns_from_epoch)

stream
------
bt_ctf_stream API.

This module defines a base class (_StreamBase) for _Stream (non-writer
stream) and _CtfWriterStream (writer stream).

Features:

* Copy, deep copy, and comparison support.

* You can create a packet from a non-writer stream:

      packet = stream.create_packet()

packet
------
bt_ctf_packet API.

Features:

* Copy, deep copy, and comparison support.

* Stream read-only property (gives back the stream object which
  created it).

* Packet context and packet header field R/W properties.

notification
------------
bt_notification API.

The classes in this module wrap their equivalent in the C API in a
pretty straightfoward way.

notification_iterator
---------------------
bt_notification_iterator API.

A notification iterator object is always created from a source/filter
component object:

    source_component.create_notification_iterator()

A notification iterator object has a next() method to go to the next
notification, and a `notification` property to get the current
notification:

    notif_iter.next()
    print(notif_iter.notification)

The next() method can raise:

* bt2.Stop: End of the iteration (inherits StopIteration).
* bt2.UnsupportedFeature: Unsupported feature.
* bt2.Error: Any other error.

A notification iterator also honors the iterator protocol, that is, you
can use it like any Python iterator:

    for notif in notif_iter:
        print(notif)

Note that the iteration can still raise bt2.UnsupportedFeature or
bt2.Error in this scenario (bt2.Stop stops the iteration: it's not
raised outside the iteration).

You can use the seek_to_time() method to make a notification iterator
seek to a specific time.

The `component` property of a notification iterator returns the original
source/filter component which was used to create it.

You can create your own notification iterator class (to be used by your
own source/filter component class) by inheriting
bt2.UserNotificationIterator. This asks you to write your own _next()
and _get() methods which are eventually called by
bt_notification_iterator_next() and
bt_notification_iterator_get_notification(). You can also define an
__init__() method, a _destroy() method, and a _seek_to_time() method.

Minimal user notification iterator class:

    class MyIterator(bt2.UserNotificationIterator):
        def _get(self):
            # ...

        def _next(self):
            # ...

Your _next() method can raise bt2.Stop to signal the end of the
iteration. If it raises anything else, bt_notification_iterator_next()
returns BT_NOTIFICATION_ITERATOR_STATUS_ERROR to its caller. Since
the C API user only gets a status from this function, any exception
value is lost during this translation. However the C next method could
still log this value and the traceback in verbose mode.

Your _get() method can raise anything so that the caller of
bt_notification_iterator_get_notification() gets an error status.

Complete user notification iterator class:

    class MyIterator(bt2.UserNotificationIterator):
        def __init__(self):
            # Called when the user calls
            # bt_component_source_create_notification_iterator() or
            # bt_component_filter_create_notification_iterator().
            # Anything you raise here makes this function return
            # NULL (creation error).

        def _get(self):
            # ...

        def _next(self):
            # ...

        def _seek_to_time(self, origin, time):
            # You can raise anything or bt2.UnsupportedFeature.
            # `origin` is one of the values of
            # bt2.NotificationIteratorSeekOrigin.

        def _destroy(self):
            # This is called when the actual native BT notification
            # iterator object is destroyed. Anything you raise here
            # is ignored. You cannot use __del__() for this for
            # implementation reasons.

You CANNOT manually instantiate a user notification iterator, e.g.:

    my_iter = MyIterator()

This makes no sense because a notification iterator is always created by
a source/filter component. It probably won't work anyway because
bt2.UserNotificationIterator.__new__() expects a SWIG pointer and your
__init__() probably does not.

component
---------
bt_component_class and bt_component APIs.

This is where the fun begins.

All component objects have the following properties:

* name: Component's name or None.
* component_class: Component class object.

Source components have:

* create_notification_iterator(): Returns a new notification
  iterator object.

Filter components have:

* create_notification_iterator(): Returns a new notification
  iterator object.
* add_notification_iterator(): Adds a notification iterator to the
  filter component.

Sink components have:

* consume(): Consumes notifications from its input notification
  iterators.
* add_notification_iterator(): Adds a notification iterator to the
  filter component.

A component class object has the following properties:

* name: Component class's name or None.
* description: Component class's description or None.

You can also call a component class object to create a component
object, with optional parameters and an optional component name:

    comp = comp_class(name='my_comp', params={'path': '/tmp/lel'})

The `params` argument is passed to bt2.create_value() so you can use
a direct *Value object or anything accepted by this utility function.

What is described above is the _generic_ part of components and
component classes. There's another part for user-defined component
classes. For a user of those classes, both generic and user-defined
classes expose the same interface. The relative complexity of how this
is achieved is justified by the great simplicity from the component
developer's perspective:

    class MySink(bt2.UserSinkComponent):
        def _consume(self):
            notif_iter = self._input_notification_iterators[0]
            notif = next(notif_iter)

            if isinstance(notif, bt2.TraceEventNotification):
                print(notif.event.name)

That's it: some kind of minimal sink component class. Note that
next(notif_iter) can raise bt2.Stop here which is passed to the eventual
caller of bt_component_sink_consume() as the BT_COMPONENT_STATUS_END
status.

Behind the scenes, bt2.UserSinkComponent uses _UserComponentType as its
metaclass. When the class itself is initialized, its metaclass checks if
the subclass has the required interface (depending on its base class,
bt2.UserSinkComponent in this case) and creates a bt_component_class
owned by the Python user class. This bt_component_class is associated to
the Python class thanks to a global GHashTable in the shared object
module (_native_bt.so). Both the key and the value are weak references.

The name of the created bt_component_class is the user class's name by
default (MySink above), but it can also be passed as a class argument.
The description of the created bt_component_class is the docstring of
the user class:

    class MySink(bt2.UserSinkComponent, name='another-name'):
        'this is a custom sink'

        def _consume(self):
            # ...

Source and filter user component classes need to specify a notification
iterator class to use when the user calls
bt_component_*_create_notification_iterator(). This is specified as
a class argument.

    class MyIterator(bt2.UserNotificationIterator):
        def __init__(self):
            # ...

        def _get(self):
            # ...

        def _next(self):
            # ...

        def _seek_to_time(self, origin, time):
            # ...

        def _destroy(self):
            # ...

    class MySource(bt2.UserSinkComponent,
                   notification_iterator_class=MyIterator):
        # no mandatory methods here for a source/filter component class

Note that, within the notification iterator methods, self.component
refers to the actual user Python object which was used to create the
iterator object. This is the way to access custom, component-wide data
when the notification iterator is created (self.component._whatever).

Optional methods for all user-defined component classes are:

    class AnyComponent(...):
        def __init__(self, params, name):
            # `params` is a *Value Python object (bt2.values module),
            # `name` is the optional (can be None) component name,
            # which you can also access as self.name at this point.

        def _destroy(self):
            # This is called when the actual native BT component
            # object is destroyed. Anything you raise here
            # is ignored. You cannot use __del__() for this for
            # implementation reasons.

Optional methods for filter and sink user-defined component classes
are:

    class FilterOrSinkComponent(...):
        def _add_notification_iterator(self, notif_iter):
            # This is called when a notification iterator is added to
            # the component (using bt_component_*_add_iterator()).

Additionally, the __init__() method of filter and sink component classes
can use the self._minimum_input_notification_iterator_count and
self._maximum_input_notification_iterator_count properties to set their
minimum and maximum number of allowed input notification iterators:

    class FilterOrSinkComponent(...):
        def __init__(self, params, name):
            self._maximum_input_notification_iterator_count = 10
            self._minimum_input_notification_iterator_count = 4

They can also use the self._input_notification_iterators property at the
appropriate time to get their connected input notification iterators.
This property honors the sequence protocol. For filter components, this
is most probably going to be used by the iterator class, as such:

    class MyIterator(bt2.UserNotificationIterator):
        def _get(self):
            # ...

        def _next(self):
            notif_iter = self.component._input_notification_iterators[0]
            # ...

The beauty of all this is that both a Python user and the C API side can
instantiate user components:

    Python:

        my_sink = MySink(params={'janine': 'sutto'})

        for _ in my_sink.consume():
            pass

    C API (provided you have access to the bt_component_class object
           created for the Python user class):

        my_sink = bt_component_create(my_sink_comp_class, NULL, params);

        while (true) {
            status = bt_component_sink_consume(my_sink);
            if (status == BT_COMPONENT_STATUS_END) {
                break;
            }
        }

This is possible thanks to the overridden metaclass's __call__() method:

* When a Python user instantiates a user-defined component class, the
  metaclass's __call__() method creates an uninitialized user component
  and calls bt_component_create_with_init_method_data(), giving to this
  function `self` (the uninitialized component).

  When the component initialization method is called with some init
  method data, it sets the bt_component pointer in the received Python
  object and calls its __init__() method so that its intialization
  happens within the bt_component_create_with_init_method_data() call
  (important because some functions cannot be called outside this
  function).

  If the user's __init__() method raises, the error is not cleared on
  the C side, so that the Python user who instantiates the component
  can catch the actual, original Python exception instead of getting
  a generic one.

  In this scenario, the created user component Python object OWNS its
  bt_component. The component is marked as NOT being owned by its
  bt_component:

      self._belongs_to_native_component = False

  The bt_component has the user Python object as its private data
  (borrowed reference).

* When a C user instantiates a user-defined Python component class, he
  calls bt_component_create(). Then the component initialization
  function for this class receives a NULL init method data and knows
  it is called from the C/generic side.

  The initialization method finds the corresponding Python component
  class thanks to the aforementioned global GHashTable. It calls it with
  the `__comp_ptr` keyword argument set to the native bt_component SWIG
  pointer and the `params` keyword argument set to the *Value object
  converted from the `params` parameter. This call (metaclass's
  __call__()), in this scenario, calls the user's __init__() method
  itself. This call returns a new component instance, which is set as
  the private data of the native bt_component.

  In this scenario, the created user component Python object as a
  borrowed reference to the native bt_component. The native bt_component
  OWNS the Python user's component:

      self._belongs_to_native_component = True

The self._belongs_to_native_component property is used for the following
situation:

    my_source = MySource()

    # At this point, my_source is a Python object which owns its
    # bt_component.

    notif_iter = my_source.create_notification_iterator()

    # notif_iter is a generic notification iterator (a dumb
    # bt_notification_iterator wrapper) here, not the actual user
    # Python notification iterator. This method only calls
    # bt_component_source_create_notification_iterator() and wraps the
    # returned pointer.

    del my_source

    # At this point, the Python reference count of the source component
    # object falls to zero. Its __del__() method is called. However
    # we don't want this object to be destroyed here, because it is
    # still needed by the user notification iterator. This __del__()
    # method, if self._belongs_to_native_component is false, inverts
    # the ownership, literally:
    #
    #     if not self._belongs_to_native_component:
    #         self._belongs_to_native_component = True
    #         native_bt.py3_component_on_del(self)
    #         native_bt.put(self._ptr)
    #
    # bt_py3_component_on_del() simply increments the given
    # Python object's reference count. With its reference count back
    # to 1, Python does not actually destroy the object. It is now
    # owned by the bt_component.

    del notif_iter

    # Now, the wrapper puts its bt_notification_iterator object. Its
    # reference count falls to zero. Its bt_component is put: its
    # reference count falls to zero. The user's (C) destroy method for
    # this component class decrements the reference count of its
    # private Python object (the same object referenced by my_source
    # above). __del__() is (possibly) called again, but is a no-op
    # now. Then the user's _destroy() method is called.

plugin
------
bt_plugin API.

You can create plugin objects with bt2.create_plugins_from_file().
This is the equivalent of bt_plugin_create_all_from_file(). You can
also use bt2.create_plugins_from_dir() which is the equivalent of
bt_plugin_create_all_from_dir().

The return value of those functions is a list of _Plugin objects.

Here's an example of printing all the event names of a CTF trace:

    import bt2
    import sys

    def print_all():
        plugins = bt2.create_plugins_from_file(sys.argv[1])
        fs_cc = plugins[0].source_component_class('fs')
        fs_comp = fs_cc(params={'path': sys.argv[2]})
        notif_iter = fs_comp.create_notification_iterator()

        for notif in notif_iter:
            if isinstance(notif, bt2.TraceEventNotification):
                print(notif.event.name)

    print_all()

You would run this script like this:

    python3 script.py /path/to/ctf-plugin.so /path/to/trace

You can access the properties of a plugin:

    print(plugin.path)
    print(plugin.name)
    print(plugin.description)
    print(plugin.author)
    print(plugin.license)
    print(plugin.version)

A plugin object honors the sequence protocol:

    for comp_class in plugin:
        print(comp_class.name, comp_class.description)

ctf_writer
----------
Everything in this module is exclusive to the CTF writer API:

* CtfWriterClock (bt_ctf_clock)
* _CtfWriterStream (bt_ctf_stream, CTF writer interface only)
* CtfWriter (bt_ctf_writer)

I removed the CTFWriter.Writer.create_stream() method because it's the
equivalent of this:

    writer.trace.add_stream_class(sc)
    stream = sc()

This returns a _CtfWriterStream object.

Also removed is CTFWriter.Writer.add_environment_field() which you can
do like this now:

    writer.trace.env['name'] = value

The CTFWriter.Writer.byte_order property is now the `native_byte_order`
property of the CTF writer's trace:

    writer.trace.native_byte_order = bt2.ByteOrder.BIG_ENDIAN

CtfWriter.add_clock() expects a CtfWriterClock object.

__init__
--------
This imports * from each module, thus exposing only the public names of
each one.

It also defines the package's exceptions. bt2.CreationError is raised
when an object cannot be created. bt2.FrozenError is raised when an
operation fails because the object is frozen. This is only raised for
bt2.values objects since this API has a status to indicate the exact
error. bt2.Error is a general error.

The package also does this:

    import bt2.native_bt as _native_bt
    import atexit

    atexit.register(_native_bt.py3_cc_exit_handler)
    _native_bt.py3_cc_init_from_bt2()

bt_py3_cc_init_from_bt2() is used to import some bt2 modules and objects
on the C side and the exit handler, bt_py3_cc_exit_handler(), puts those
objects.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
41 files changed:
.gitignore
bindings/python/Makefile.am
bindings/python/bt2/.gitignore [new file with mode: 0644]
bindings/python/bt2/Makefile.am [new file with mode: 0644]
bindings/python/bt2/__init__.py.in [new file with mode: 0644]
bindings/python/bt2/clock_class.py [new file with mode: 0644]
bindings/python/bt2/component.py [new file with mode: 0644]
bindings/python/bt2/ctf_writer.py [new file with mode: 0644]
bindings/python/bt2/event.py [new file with mode: 0644]
bindings/python/bt2/event_class.py [new file with mode: 0644]
bindings/python/bt2/field_types.py [new file with mode: 0644]
bindings/python/bt2/fields.py [new file with mode: 0644]
bindings/python/bt2/native_bt.i [new file with mode: 0644]
bindings/python/bt2/native_btclockclass.i [new file with mode: 0644]
bindings/python/bt2/native_btcomponent.i [new file with mode: 0644]
bindings/python/bt2/native_btcomponentclass.i [new file with mode: 0644]
bindings/python/bt2/native_btctfwriter.i [new file with mode: 0644]
bindings/python/bt2/native_btevent.i [new file with mode: 0644]
bindings/python/bt2/native_bteventclass.i [new file with mode: 0644]
bindings/python/bt2/native_btfields.i [new file with mode: 0644]
bindings/python/bt2/native_btft.i [new file with mode: 0644]
bindings/python/bt2/native_btnotification.i [new file with mode: 0644]
bindings/python/bt2/native_btnotifiter.i [new file with mode: 0644]
bindings/python/bt2/native_btpacket.i [new file with mode: 0644]
bindings/python/bt2/native_btplugin.i [new file with mode: 0644]
bindings/python/bt2/native_btref.i [new file with mode: 0644]
bindings/python/bt2/native_btstream.i [new file with mode: 0644]
bindings/python/bt2/native_btstreamclass.i [new file with mode: 0644]
bindings/python/bt2/native_bttrace.i [new file with mode: 0644]
bindings/python/bt2/native_btvalues.i [new file with mode: 0644]
bindings/python/bt2/notification.py [new file with mode: 0644]
bindings/python/bt2/notification_iterator.py [new file with mode: 0644]
bindings/python/bt2/object.py [new file with mode: 0644]
bindings/python/bt2/packet.py [new file with mode: 0644]
bindings/python/bt2/plugin.py [new file with mode: 0644]
bindings/python/bt2/stream.py [new file with mode: 0644]
bindings/python/bt2/stream_class.py [new file with mode: 0644]
bindings/python/bt2/trace.py [new file with mode: 0644]
bindings/python/bt2/utils.py [new file with mode: 0644]
bindings/python/bt2/values.py [new file with mode: 0644]
configure.ac

index d72d3b32b4f1bb90a3562aca73b3a3496fd9f4c5..9505e1e3e1ba90cc7239816b92798b43b61c8d66 100644 (file)
@@ -66,7 +66,7 @@ stamp-h1
 bindings/python/__init__.py
 bindings/python/nativebt.py
 bindings/python/nativebt_wrap.c
-bindings/python/__pycache__
+__pycache__
 babeltrace.pc
 babeltrace-ctf.pc
 TAGS
index 4398289860e4e4c441edc2eced34a83b5c78176b..f3cec072204e50156016b6423f948871ef0cdf08 100644 (file)
@@ -1 +1 @@
-SUBDIRS = babeltrace
+SUBDIRS = bt2
diff --git a/bindings/python/bt2/.gitignore b/bindings/python/bt2/.gitignore
new file mode 100644 (file)
index 0000000..56bd6e9
--- /dev/null
@@ -0,0 +1,3 @@
+__init__.py
+native_bt.py
+native_bt_wrap.c
diff --git a/bindings/python/bt2/Makefile.am b/bindings/python/bt2/Makefile.am
new file mode 100644 (file)
index 0000000..4989a72
--- /dev/null
@@ -0,0 +1,94 @@
+# native module name (without `.i` extension)
+NATIVE_MODULE = native_bt
+
+# interface dependencies (without `native_bt` prefix and `.i` extension)
+NATIVE_MODULE_DEPS =   \
+       clockclass      \
+       eventclass      \
+       event           \
+       fields          \
+       ft              \
+       packet          \
+       ref             \
+       streamclass     \
+       stream          \
+       trace           \
+       values          \
+       ctfwriter       \
+       componentclass  \
+       component       \
+       notification    \
+       notifiter       \
+       plugin
+
+# Python modules (without `.py` extension)
+EXTRA_MODULES =                \
+       clock_class             \
+       event_class             \
+       event                   \
+       fields                  \
+       field_types             \
+       object                  \
+       packet                  \
+       stream_class            \
+       stream                  \
+       trace                   \
+       utils                   \
+       values                  \
+       ctf_writer              \
+       component               \
+       notification            \
+       notification_iterator   \
+       plugin
+
+# automatically generated file lists
+EXTRA_MODULES_PY = $(addprefix $(srcdir)/,$(addsuffix .py,$(EXTRA_MODULES)))
+NATIVE_MODULE_I = $(srcdir)/$(NATIVE_MODULE).i
+NATIVE_MODULE_PY = $(NATIVE_MODULE).py
+NATIVE_MODULE_C = $(NATIVE_MODULE)_wrap.c
+NATIVE_MODULE_DEPS_I = $(addprefix $(srcdir)/native_bt,$(addsuffix .i,$(NATIVE_MODULE_DEPS)))
+
+# installed Python package
+nodist_bt2package_PYTHON = __init__.py $(EXTRA_MODULES_PY) $(NATIVE_MODULE_PY)
+
+# native libraries to build
+nativelibs_LTLIBRARIES = _native_bt.la
+
+# installation directory for the `bt2` package
+bt2packagedir = $(pythondir)/bt2
+nativelibsdir = $(bt2packagedir)
+
+# SWIG to C wrapper (and Python file)
+$(NATIVE_MODULE_C): $(NATIVE_MODULE_I) $(NATIVE_MODULE_DEPS_I)
+       $(SWIG) -python -Wall -I$(srcdir) -I$(top_srcdir)/include -module $(NATIVE_MODULE) -outcurrentdir $(NATIVE_MODULE_I)
+
+# native_bt module
+_native_bt_la_SOURCES = native_bt_wrap.c
+_native_bt_la_LDFLAGS = -module
+_native_bt_la_CFLAGS = $(GLIB_CFLAGS) $(PYTHON_INCLUDE) -I$(top_srcdir)/include/ -I$(srcdir)
+_native_bt_la_LIBS = $(GLIB_LIBS)
+_native_bt_la_LIBADD = \
+       $(top_builddir)/lib/libbabeltrace.la \
+       $(top_builddir)/formats/ctf/libbabeltrace-ctf.la
+
+# extra module sources -> build directory
+all-local:
+       @if [ x"$(srcdir)" != x"$(builddir)" ]; then            \
+               for file in $(EXTRA_MODULES_PY); do             \
+                       cp -f $(srcdir)/$$file $(builddir);     \
+               done;                                           \
+       fi
+
+# clean extra module sources in build directory
+clean-local:
+       @if [ x"$(srcdir)" != x"$(builddir)" ]; then            \
+               for file in $(EXTRA_MODULES_PY); do             \
+                       rm -f $(srcdir)/$$file $(builddir);     \
+               done;                                           \
+       fi
+
+# distribute: extra Python modules and SWIG interface files
+EXTRA_DIST = __init__.py.in $(EXTRA_MODULES_PY) $(NATIVE_MODULE_I) $(NATIVE_MODULE_DEPS_I)
+
+# clean: generated C and Python files (by SWIG)
+CLEANFILES = $(NATIVE_MODULE_PY) $(NATIVE_MODULE_C)
diff --git a/bindings/python/bt2/__init__.py.in b/bindings/python/bt2/__init__.py.in
new file mode 100644 (file)
index 0000000..9a8c93c
--- /dev/null
@@ -0,0 +1,73 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+__version__ = '@PACKAGE_VERSION@'
+
+
+from bt2.clock_class import *
+from bt2.component import *
+from bt2.ctf_writer import *
+from bt2.event_class import *
+from bt2.field_types import *
+from bt2.fields import *
+from bt2.notification import *
+from bt2.notification_iterator import *
+from bt2.plugin import *
+from bt2.stream_class import *
+from bt2.trace import *
+from bt2.values import *
+
+
+class Error(Exception):
+    pass
+
+
+class CreationError(Error):
+    pass
+
+
+class FrozenError(Error):
+    pass
+
+
+class UnsupportedFeature(Exception):
+    pass
+
+
+class TryAgain(Exception):
+    pass
+
+
+class Stop(StopIteration):
+    pass
+
+
+class IncompleteUserClassError(Error):
+    pass
+
+
+
+import bt2.native_bt as _native_bt
+import atexit
+
+atexit.register(_native_bt.py3_cc_exit_handler)
+_native_bt.py3_cc_init_from_bt2()
diff --git a/bindings/python/bt2/clock_class.py b/bindings/python/bt2/clock_class.py
new file mode 100644 (file)
index 0000000..adfba44
--- /dev/null
@@ -0,0 +1,267 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import uuid as uuidp
+import bt2
+
+
+class ClockClassOffset:
+    def __init__(self, seconds=0, cycles=0):
+        utils._check_int64(seconds)
+        utils._check_int64(cycles)
+        self._seconds = seconds
+        self._cycles = cycles
+
+    @property
+    def seconds(self):
+        return self._seconds
+
+    @property
+    def cycles(self):
+        return self._cycles
+
+    def __eq__(self, other):
+        if not isinstance(other, self.__class__):
+            # not comparing apples to apples
+            return False
+
+        return (self.seconds, self.cycles) == (other.seconds, other.cycles)
+
+
+class ClockClass(object._Object):
+    def __init__(self, name, description=None, frequency=None, precision=None,
+                 offset=None, is_absolute=None, uuid=None):
+        utils._check_str(name)
+        ptr = native_bt.ctf_clock_class_create(name)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create clock class object')
+
+        super().__init__(ptr)
+
+        if description is not None:
+            self.description = description
+
+        if frequency is not None:
+            self.frequency = frequency
+
+        if precision is not None:
+            self.precision = precision
+
+        if offset is not None:
+            self.offset = offset
+
+        if is_absolute is not None:
+            self.is_absolute = is_absolute
+
+        if uuid is not None:
+            self.uuid = uuid
+
+    def __eq__(self, other):
+        if type(self) is not type(other):
+            # not comparing apples to apples
+            return False
+
+        self_props = (
+            self.name,
+            self.description,
+            self.frequency,
+            self.precision,
+            self.offset,
+            self.is_absolute,
+            self.uuid
+        )
+        other_props = (
+            other.name,
+            other.description,
+            other.frequency,
+            other.precision,
+            other.offset,
+            other.is_absolute,
+            other.uuid
+        )
+        return self_props == other_props
+
+    def __copy__(self):
+        return ClockClass(name=self.name, description=self.description,
+                          frequency=self.frequency, precision=self.precision,
+                          offset=self.offset, is_absolute=self.is_absolute,
+                          uuid=self.uuid)
+
+    def __deepcopy__(self, memo):
+        cpy = self.__copy__()
+        memo[id(self)] = cpy
+        return cpy
+
+    @property
+    def name(self):
+        name = native_bt.ctf_clock_class_get_name(self._ptr)
+        utils._handle_ptr(name, "cannot get clock class object's name")
+        return name
+
+    @name.setter
+    def name(self, name):
+        utils._check_str(name)
+        ret = native_bt.ctf_clock_class_set_name(self._ptr, name)
+        utils._handle_ret(ret, "cannot set clock class object's name")
+
+    @property
+    def description(self):
+        description = native_bt.ctf_clock_class_get_description(self._ptr)
+        return description
+
+    @description.setter
+    def description(self, description):
+        utils._check_str(description)
+        ret = native_bt.ctf_clock_class_set_description(self._ptr, description)
+        utils._handle_ret(ret, "cannot set clock class object's description")
+
+    @property
+    def frequency(self):
+        frequency = native_bt.ctf_clock_class_get_frequency(self._ptr)
+
+        if utils._is_m1ull(frequency):
+            raise bt2.Error("cannot get clock class object's frequency")
+
+        return frequency
+
+    @frequency.setter
+    def frequency(self, frequency):
+        utils._check_uint64(frequency)
+        ret = native_bt.ctf_clock_class_set_frequency(self._ptr, frequency)
+        utils._handle_ret(ret, "cannot set clock class object's frequency")
+
+    @property
+    def precision(self):
+        precision = native_bt.ctf_clock_class_get_precision(self._ptr)
+
+        if utils._is_m1ull(precision):
+            raise bt2.Error("cannot get clock class object's precision")
+
+        return precision
+
+    @precision.setter
+    def precision(self, precision):
+        utils._check_uint64(precision)
+        ret = native_bt.ctf_clock_class_set_precision(self._ptr, precision)
+        utils._handle_ret(ret, "cannot set clock class object's precision")
+
+    @property
+    def offset(self):
+        ret, offset_s = native_bt.ctf_clock_class_get_offset_s(self._ptr)
+        utils._handle_ret(ret, "cannot get clock class object's offset (seconds)")
+        ret, offset_cycles = native_bt.ctf_clock_class_get_offset_cycles(self._ptr)
+        utils._handle_ret(ret, "cannot get clock class object's offset (cycles)")
+        return ClockClassOffset(offset_s, offset_cycles)
+
+    @offset.setter
+    def offset(self, offset):
+        utils._check_type(offset, ClockClassOffset)
+        ret = native_bt.ctf_clock_class_set_offset_s(self._ptr, offset.seconds)
+        utils._handle_ret(ret, "cannot set clock class object's offset (seconds)")
+        ret = native_bt.ctf_clock_class_set_offset_cycles(self._ptr, offset.cycles)
+        utils._handle_ret(ret, "cannot set clock class object's offset (cycles)")
+
+    @property
+    def is_absolute(self):
+        is_absolute = native_bt.ctf_clock_class_get_is_absolute(self._ptr)
+        utils._handle_ret(is_absolute, "cannot get clock class object's absoluteness")
+        return is_absolute > 0
+
+    @is_absolute.setter
+    def is_absolute(self, is_absolute):
+        utils._check_bool(is_absolute)
+        ret = native_bt.ctf_clock_class_set_is_absolute(self._ptr, int(is_absolute))
+        utils._handle_ret(ret, "cannot set clock class object's absoluteness")
+
+    @property
+    def uuid(self):
+        uuid_bytes = native_bt.ctf_clock_class_get_uuid(self._ptr)
+
+        if uuid_bytes is None:
+            raise bt2.Error("cannot get clock class object's UUID")
+
+        return uuidp.UUID(bytes=uuid_bytes)
+
+    @uuid.setter
+    def uuid(self, uuid):
+        utils._check_type(uuid, uuidp.UUID)
+        ret = native_bt.ctf_clock_class_set_uuid(self._ptr, uuid.bytes)
+        utils._handle_ret(ret, "cannot set clock class object's UUID")
+
+    def create_clock_value(self, cycles):
+        return _ClockValue(self._ptr, cycles)
+
+
+def _create_clock_value_from_ptr(ptr):
+    clock_value = _ClockValue._create_from_ptr(ptr)
+    return clock_value
+
+
+class _ClockValue(object._Object):
+    def __init__(self, clock_class_ptr, cycles):
+        utils._check_uint64(cycles)
+        ptr = native_bt.ctf_clock_value_create(clock_class_ptr, cycles)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create clock value object')
+
+        super().__init__(ptr)
+
+    @property
+    def clock_class(self):
+        ptr = native_bt.ctf_clock_value_get_class(self._ptr)
+        utils._handle_ptr(ptr, "cannot get clock value object's clock class object")
+        return ClockClass._create_from_ptr(ptr)
+
+    @property
+    def cycles(self):
+        ret, cycles = native_bt.ctf_clock_value_get_value(self._ptr)
+        utils._handle_ret(ret, "cannot get clock value object's cycles")
+        return cycles
+
+    @property
+    def ns_from_epoch(self):
+        ret, ns = native_bt.ctf_clock_value_get_value_ns_from_epoch(self._ptr)
+        utils._handle_ret(ret, "cannot get clock value object's nanoseconds from Epoch")
+        return ns
+
+    def __eq__(self, other):
+        if not isinstance(other, self.__class__):
+            # not comparing apples to apples
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        self_props = self.clock_class.addr, self.cycles
+        other_props = other.clock_class.addr, other.cycles
+        return self_props == other_props
+
+    def __copy__(self):
+        return self.clock_class.create_clock_value(self.cycles)
+
+    def __deepcopy__(self, memo):
+        cpy = self.__copy__()
+        memo[id(self)] = cpy
+        return cpy
diff --git a/bindings/python/bt2/component.py b/bindings/python/bt2/component.py
new file mode 100644 (file)
index 0000000..77576c5
--- /dev/null
@@ -0,0 +1,463 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.notification_iterator
+import collections.abc
+import bt2
+
+
+# This class wraps a component class pointer. This component class could
+# have been created by Python code, but since we only have the pointer,
+# we can only wrap it in a generic way and lose the original Python
+# class.
+class _GenericComponentClass(object._Object):
+    @property
+    def name(self):
+        return native_bt.component_class_get_name(self._ptr)
+
+    @property
+    def description(self):
+        return native_bt.component_class_get_description(self._ptr)
+
+    def __call__(self, params=None, name=None):
+        params = bt2.create_value(params)
+        comp_ptr = native_bt.component_create_with_init_method_data(self._ptr,
+                                                                    name,
+                                                                    params._ptr,
+                                                                    None)
+
+        if comp_ptr is None:
+            raise bt2.CreationError('cannot create component object')
+
+        return _create_generic_component_from_ptr(comp_ptr)
+
+
+class _GenericSourceComponentClass(_GenericComponentClass):
+    pass
+
+
+class _GenericFilterComponentClass(_GenericComponentClass):
+    pass
+
+
+class _GenericSinkComponentClass(_GenericComponentClass):
+    pass
+
+
+# This class holds the methods which are common to both generic
+# component objects and Python user component objects. They use the
+# internal native _ptr, however it was set, to call native API
+# functions.
+class _CommonComponentMethods:
+    @property
+    def name(self):
+        return native_bt.component_get_name(self._ptr)
+
+    @property
+    def component_class(self):
+        cc_ptr = native_bt.component_get_class(self._ptr)
+        utils._handle_ptr(cc_ptr, "cannot get component object's class object")
+        return _create_generic_component_class_from_ptr(cc_ptr)
+
+    def _handle_status(self, status, gen_error_msg):
+        if status == native_bt.COMPONENT_STATUS_END:
+            raise bt2.Stop
+        elif status == native_bt.COMPONENT_STATUS_AGAIN:
+            raise bt2.TryAgain
+        elif status == native_bt.COMPONENT_STATUS_UNSUPPORTED:
+            raise bt2.UnsupportedFeature
+        elif status < 0:
+            raise bt2.Error(gen_error_msg)
+
+
+class _CommonSourceComponentMethods(_CommonComponentMethods):
+    def create_notification_iterator(self):
+        iter_ptr = native_bt.component_source_create_notification_iterator_with_init_method_data(self._ptr, None)
+
+        if iter_ptr is None:
+            raise bt2.CreationError('cannot create notification iterator object')
+
+        return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(iter_ptr)
+
+
+class _CommonFilterComponentMethods(_CommonComponentMethods):
+    def create_notification_iterator(self):
+        iter_ptr = native_bt.component_filter_create_notification_iterator_with_init_method_data(self._ptr, None)
+
+        if iter_ptr is None:
+            raise bt2.CreationError('cannot create notification iterator object')
+
+        return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(iter_ptr)
+
+    def add_notification_iterator(self, notif_iter):
+        utils._check_type(notif_iter, bt2.notification_iterator._GenericNotificationIteratorMethods)
+        status = native_bt.component_filter_add_iterator(self._ptr, notif_iter._ptr)
+        self._handle_status(status, 'unexpected error: cannot add notification iterator to filter component object')
+
+
+class _CommonSinkComponentMethods(_CommonComponentMethods):
+    def add_notification_iterator(self, notif_iter):
+        utils._check_type(notif_iter, bt2.notification_iterator._GenericNotificationIteratorMethods)
+        status = native_bt.component_sink_add_iterator(self._ptr, notif_iter._ptr)
+        self._handle_status(status, 'unexpected error: cannot add notification iterator to sink component object')
+
+    def consume(self):
+        status = native_bt.component_sink_consume(self._ptr)
+        self._handle_status(status, 'unexpected error: cannot consume sink component object')
+
+
+# This is analogous to _GenericSourceComponentClass, but for source
+# component objects.
+class _GenericSourceComponent(object._Object, _CommonSourceComponentMethods):
+    pass
+
+
+# This is analogous to _GenericFilterComponentClass, but for filter
+# component objects.
+class _GenericFilterComponent(object._Object, _CommonFilterComponentMethods):
+    pass
+
+
+# This is analogous to _GenericSinkComponentClass, but for sink
+# component objects.
+class _GenericSinkComponent(object._Object, _CommonSinkComponentMethods):
+    pass
+
+
+_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS = {
+    native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponent,
+    native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponent,
+    native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponent,
+}
+
+
+_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_CLS = {
+    native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponentClass,
+    native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponentClass,
+    native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponentClass,
+}
+
+
+def _create_generic_component_from_ptr(ptr):
+    comp_cls_type = native_bt.component_get_class_type(ptr)
+    return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS[comp_cls_type]._create_from_ptr(ptr)
+
+
+def _create_generic_component_class_from_ptr(ptr):
+    comp_cls_type = native_bt.component_class_get_type(ptr)
+    return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_CLS[comp_cls_type]._create_from_ptr(ptr)
+
+
+# Metaclass for component classes defined by Python code.
+#
+# The Python user can create a standard Python class which inherits one
+# of the three base classes (UserSourceComponent, UserFilterComponent,
+# or UserSinkComponent). Those base classes set this class
+# (_UserComponentType) as their metaclass.
+#
+# Once the body of a user-defined component class is executed, this
+# metaclass is used to create and initialize the class. The metaclass
+# creates a native BT component class of the corresponding type and
+# associates it with this user-defined class. The metaclass also defines
+# class methods like the `name` and `description` properties to match
+# the _GenericComponentClass interface.
+#
+# The component class name which is used is either:
+#
+# * The `name` parameter of the class:
+#
+#       class MySink(bt2.SinkComponent, name='my-custom-sink'):
+#           ...
+#
+# * If the `name` class parameter is not used: the name of the class
+#   itself (`MySink` in the example above).
+#
+# The component class description which is used is the user-defined
+# class's docstring:
+#
+#     class MySink(bt2.SinkComponent):
+#         'Description goes here'
+#         ...
+#
+# A user-defined Python component class can have an __init__() method
+# which must at least accept the `params` and `name` arguments:
+#
+#     def __init__(self, params, name, something_else):
+#         ...
+#
+# The user-defined component class can also have a _destroy() method
+# (do NOT use __del__()) to be notified when the component object is
+# (really) destroyed.
+#
+# User-defined source and filter component classes must use the
+# `notification_iterator_class` class parameter to specify the
+# notification iterator class to use for this component class:
+#
+#     class MyNotificationIterator(bt2.UserNotificationIterator):
+#         ...
+#
+#     class MySource(bt2.UserSourceComponent,
+#                    notification_iterator_class=MyNotificationIterator):
+#         ...
+#
+# This notification iterator class must inherit
+# bt2.UserNotificationIterator, and it must define the _get() and
+# _next() methods. The notification iterator class can also define an
+# __init__() method: this method has access to the original Python
+# component object which was used to create it as the `component`
+# property. The notification iterator class can also define a _destroy()
+# method (again, do NOT use __del__()): this is called when the
+# notification iterator is (really) destroyed.
+#
+# When the user-defined class is destroyed, this metaclass's __del__()
+# method is called: the native BT component class pointer is put (not
+# needed anymore, at least not by any Python code since all references
+# are dropped for __del__() to be called).
+class _UserComponentType(type):
+    # __new__() is used to catch custom class parameters
+    def __new__(meta_cls, class_name, bases, attrs, **kwargs):
+        return super().__new__(meta_cls, class_name, bases, attrs)
+
+    def __init__(cls, class_name, bases, namespace, **kwargs):
+        super().__init__(class_name, bases, namespace)
+
+        # skip our own bases; they are never directly instantiated by the user
+        if class_name in ('_UserComponent', 'UserSourceComponent', 'UserFilterComponent', 'UserSinkComponent'):
+            return
+
+        comp_cls_name = kwargs.get('name', class_name)
+        comp_cls_descr = getattr(cls, '__doc__', None)
+        iter_cls = kwargs.get('notification_iterator_class')
+
+        if UserSourceComponent in bases:
+            _UserComponentType._set_iterator_class(cls, iter_cls)
+            has_seek_time = _UserComponentType._has_seek_to_time_method(cls._iter_cls)
+            cc_ptr = native_bt.py3_component_class_source_create(cls,
+                                                                 comp_cls_name,
+                                                                 comp_cls_descr,
+                                                                 has_seek_time)
+        elif UserFilterComponent in bases:
+            _UserComponentType._set_iterator_class(cls, iter_cls)
+            has_seek_time = _UserComponentType._has_seek_to_time_method(cls._iter_cls)
+            cc_ptr = native_bt.py3_component_class_filter_create(cls,
+                                                                 comp_cls_name,
+                                                                 comp_cls_descr,
+                                                                 has_seek_time)
+        elif UserSinkComponent in bases:
+            if not hasattr(cls, '_consume'):
+                raise bt2.IncompleteUserClassError("cannot create component class '{}': missing a _consume() method".format(class_name))
+
+            cc_ptr = native_bt.py3_component_class_sink_create(cls,
+                                                               comp_cls_name,
+                                                               comp_cls_descr)
+        else:
+            raise bt2.IncompleteUserClassError("cannot find a known component class base in the bases of '{}'".format(class_name))
+
+        if cc_ptr is None:
+            raise bt2.CreationError("cannot create component class '{}'".format(class_name))
+
+        cls._cc_ptr = cc_ptr
+
+    def __call__(cls, *args, **kwargs):
+        # create instance
+        self = cls.__new__(cls)
+
+        # assign native component pointer received from caller
+        self._ptr = kwargs.get('__comp_ptr')
+        name = kwargs.get('name')
+
+        if self._ptr is None:
+            # called from Python code
+            self._belongs_to_native_component = False
+
+            # py3_component_create() will call self.__init__() with the
+            # desired arguments and keyword arguments. This is needed
+            # because functions such as
+            # bt_component_sink_set_minimum_input_count() can only be
+            # called _during_ the bt_component_create() call (in
+            # Python words: during self.__init__()).
+            #
+            # The arguments and keyword arguments to use for
+            # self.__init__() are put in the object itself to find them
+            # from the bt_component_create() function.
+            self._init_args = args
+            self._init_kwargs = kwargs
+            native_bt.py3_component_create(cls._cc_ptr, self, name)
+
+            # At this point, self._ptr should be set to non-None. If
+            # it's not, an error occured during the
+            # native_bt.py3_component_create() call. We consider this a
+            # creation error.
+            if self._ptr is None:
+                raise bt2.CreationError("cannot create component object from component class '{}'".format(cls.__name__))
+        else:
+            # Called from non-Python code (within
+            # bt_component_create()): call __init__() here, after
+            # removing the __comp_ptr keyword argument which is just for
+            # this __call__() method.
+            self._belongs_to_native_component = True
+            del kwargs['__comp_ptr']
+
+            # inject `name` into the keyword arguments
+            kwargs['name'] = self.name
+            self.__init__(*args, **kwargs)
+
+        return self
+
+    @staticmethod
+    def _has_seek_to_time_method(iter_cls):
+        return hasattr(iter_cls, '_seek_to_time')
+
+    @staticmethod
+    def _set_iterator_class(cls, iter_cls):
+        if iter_cls is None:
+            raise bt2.IncompleteUserClassError("cannot create component class '{}': missing notification iterator class".format(cls.__name__))
+
+        if not issubclass(iter_cls, bt2.notification_iterator.UserNotificationIterator):
+            raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class does not inherit bt2.UserNotificationIterator".format(cls.__name__))
+
+        if not hasattr(iter_cls, '_get'):
+            raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class is missing a _get() method".format(cls.__name__))
+
+        if not hasattr(iter_cls, '_next'):
+            raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class is missing a _next() method".format(cls.__name__))
+
+        cls._iter_cls = iter_cls
+
+    @property
+    def name(cls):
+        return native_bt.component_class_get_name(cls._cc_ptr)
+
+    @property
+    def description(cls):
+        return native_bt.component_class_get_description(cls._cc_ptr)
+
+    @property
+    def addr(cls):
+        return int(cls._cc_ptr)
+
+    def __del__(cls):
+        if hasattr(cls, '_cc_ptr'):
+            native_bt.put(cls._cc_ptr)
+
+
+class _ComponentInputNotificationIterators(collections.abc.Sequence):
+    def __init__(self, comp, count_func, get_func):
+        self._comp = comp
+        self._count_func = count_func
+        self._get_func = get_func
+
+    def __len__(self):
+        status, count = self._count_func(self._comp._ptr)
+        utils._handle_ret(status, "cannot get component object's input notification iterator count")
+        return count
+
+    def __getitem__(self, index):
+        utils._check_uint64(index)
+
+        if index >= len(self):
+            raise IndexError
+
+        notif_iter_ptr = self._get_func(self._comp._ptr, index)
+        utils._handle_ptr(notif_iter_ptr, "cannot get component object's input notification iterator")
+        return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr)
+
+
+class _UserComponent(metaclass=_UserComponentType):
+    @property
+    def addr(self):
+        return int(self._ptr)
+
+    def __init__(self, *args, **kwargs):
+        pass
+
+    def _destroy(self):
+        pass
+
+    def __del__(self):
+        if not self._belongs_to_native_component:
+            self._belongs_to_native_component = True
+            native_bt.py3_component_on_del(self)
+            native_bt.put(self._ptr)
+
+
+class UserSourceComponent(_UserComponent, _CommonSourceComponentMethods):
+    pass
+
+
+class UserFilterComponent(_UserComponent, _CommonFilterComponentMethods):
+    def _set_minimum_input_notification_iterator_count(self, count):
+        utils._check_uint64(count)
+        status = native_bt.component_filter_set_minimum_input_count(self._ptr, count)
+        self._handle_status(status, "unexpected error: cannot set filter component object's minimum input notification iterator count")
+
+    _minimum_input_notification_iterator_count = property(fset=_set_minimum_input_notification_iterator_count)
+
+    def _set_maximum_input_notification_iterator_count(self, count):
+        utils._check_uint64(count)
+        status = native_bt.component_filter_set_maximum_input_count(self._ptr, count)
+        self._handle_status(status, "unexpected error: cannot set filter component object's maximum input notification iterator count")
+
+    _maximum_input_notification_iterator_count = property(fset=_set_maximum_input_notification_iterator_count)
+
+    @property
+    def _input_notification_iterators(self):
+        return _ComponentInputNotificationIterators(self,
+                                                    native_bt.component_filter_get_input_count,
+                                                    native_bt.component_filter_get_input_iterator_private)
+
+    def _add_iterator_from_bt(self, notif_iter_ptr):
+        notif_iter = bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr)
+        self._add_notification_iterator(notif_iter)
+
+    def _add_notification_iterator(self, notif_iter):
+        pass
+
+
+class UserSinkComponent(_UserComponent, _CommonSinkComponentMethods):
+    def _set_minimum_input_notification_iterator_count(self, count):
+        utils._check_uint64(count)
+        status = native_bt.component_sink_set_minimum_input_count(self._ptr, count)
+        self._handle_status(status, "unexpected error: cannot set sink component object's minimum input notification iterator count")
+
+    _minimum_input_notification_iterator_count = property(fset=_set_minimum_input_notification_iterator_count)
+
+    def _set_maximum_input_notification_iterator_count(self, count):
+        utils._check_uint64(count)
+        status = native_bt.component_sink_set_maximum_input_count(self._ptr, count)
+        self._handle_status(status, "unexpected error: cannot set sink component object's maximum input notification iterator count")
+
+    _maximum_input_notification_iterator_count = property(fset=_set_maximum_input_notification_iterator_count)
+
+    @property
+    def _input_notification_iterators(self):
+        return _ComponentInputNotificationIterators(self,
+                                                    native_bt.component_sink_get_input_count,
+                                                    native_bt.component_sink_get_input_iterator_private)
+
+    def _add_iterator_from_bt(self, notif_iter_ptr):
+        notif_iter = bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr)
+        self._add_notification_iterator(notif_iter)
+
+    def _add_notification_iterator(self, notif_iter):
+        pass
diff --git a/bindings/python/bt2/ctf_writer.py b/bindings/python/bt2/ctf_writer.py
new file mode 100644 (file)
index 0000000..5a167cb
--- /dev/null
@@ -0,0 +1,323 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, stream, utils
+import uuid as uuidp
+import bt2.event
+import abc
+import bt2
+
+
+class CtfWriterClock(object._Object):
+    def __init__(self, name, description=None, frequency=None, precision=None,
+                 offset=None, is_absolute=None, uuid=None):
+        utils._check_str(name)
+        ptr = native_bt.ctf_clock_create(name)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create CTF writer clock object')
+
+        super().__init__(ptr)
+
+        if description is not None:
+            self.description = description
+
+        if frequency is not None:
+            self.frequency = frequency
+
+        if precision is not None:
+            self.precision = precision
+
+        if offset is not None:
+            self.offset = offset
+
+        if is_absolute is not None:
+            self.is_absolute = is_absolute
+
+        if uuid is not None:
+            self.uuid = uuid
+
+    def __eq__(self, other):
+        if type(self) is not type(other):
+            # not comparing apples to apples
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        self_props = (
+            self.name,
+            self.description,
+            self.frequency,
+            self.precision,
+            self.offset,
+            self.is_absolute,
+            self.uuid
+        )
+        other_props = (
+            other.name,
+            other.description,
+            other.frequency,
+            other.precision,
+            other.offset,
+            other.is_absolute,
+            other.uuid
+        )
+        return self_props == other_props
+
+    def __copy__(self):
+        return CtfWriterClock(name=self.name, description=self.description,
+                              frequency=self.frequency,
+                              precision=self.precision, offset=self.offset,
+                              is_absolute=self.is_absolute, uuid=self.uuid)
+
+    def __deepcopy__(self, memo):
+        cpy = self.__copy__()
+        memo[id(self)] = cpy
+        return cpy
+
+    @property
+    def name(self):
+        name = native_bt.ctf_clock_get_name(self._ptr)
+        utils._handle_ptr(name, "cannot get CTF writer clock object's name")
+        return name
+
+    @property
+    def description(self):
+        description = native_bt.ctf_clock_get_description(self._ptr)
+        return description
+
+    @description.setter
+    def description(self, description):
+        utils._check_str(description)
+        ret = native_bt.ctf_clock_set_description(self._ptr, description)
+        utils._handle_ret(ret, "cannot set CTF writer clock object's description")
+
+    @property
+    def frequency(self):
+        frequency = native_bt.ctf_clock_get_frequency(self._ptr)
+
+        if utils._is_m1ull(frequency):
+            raise bt2.Error("cannot get CTF writer clock object's frequency")
+
+        return frequency
+
+    @frequency.setter
+    def frequency(self, frequency):
+        utils._check_uint64(frequency)
+        ret = native_bt.ctf_clock_set_frequency(self._ptr, frequency)
+        utils._handle_ret(ret, "cannot set CTF writer clock object's frequency")
+
+    @property
+    def precision(self):
+        precision = native_bt.ctf_clock_get_precision(self._ptr)
+
+        if utils._is_m1ull(precision):
+            raise bt2.Error("cannot get CTF writer clock object's precision")
+
+        return precision
+
+    @precision.setter
+    def precision(self, precision):
+        utils._check_uint64(precision)
+        ret = native_bt.ctf_clock_set_precision(self._ptr, precision)
+        utils._handle_ret(ret, "cannot set CTF writer clock object's precision")
+
+    @property
+    def offset(self):
+        ret, offset_s = native_bt.ctf_clock_get_offset_s(self._ptr)
+        utils._handle_ret(ret, "cannot get CTF writer clock object's offset (seconds)")
+        ret, offset_cycles = native_bt.ctf_clock_get_offset(self._ptr)
+        utils._handle_ret(ret, "cannot get CTF writer clock object's offset (cycles)")
+        return bt2.ClockClassOffset(offset_s, offset_cycles)
+
+    @offset.setter
+    def offset(self, offset):
+        utils._check_type(offset, bt2.ClockClassOffset)
+        ret = native_bt.ctf_clock_set_offset_s(self._ptr, offset.seconds)
+        utils._handle_ret(ret, "cannot set CTF writer clock object's offset (seconds)")
+        ret = native_bt.ctf_clock_set_offset(self._ptr, offset.cycles)
+        utils._handle_ret(ret, "cannot set CTF writer clock object's offset (cycles)")
+
+    @property
+    def is_absolute(self):
+        is_absolute = native_bt.ctf_clock_get_is_absolute(self._ptr)
+        utils._handle_ret(is_absolute, "cannot get CTF writer clock object's absoluteness")
+        return is_absolute > 0
+
+    @is_absolute.setter
+    def is_absolute(self, is_absolute):
+        utils._check_bool(is_absolute)
+        ret = native_bt.ctf_clock_set_is_absolute(self._ptr, int(is_absolute))
+        utils._handle_ret(ret, "cannot set CTF writer clock object's absoluteness")
+
+    @property
+    def uuid(self):
+        uuid_bytes = native_bt.ctf_clock_get_uuid(self._ptr)
+
+        if uuid_bytes is None:
+            raise bt2.Error("cannot get CTF writer clock object's UUID")
+
+        return uuidp.UUID(bytes=uuid_bytes)
+
+    @uuid.setter
+    def uuid(self, uuid):
+        utils._check_type(uuid, uuidp.UUID)
+        ret = native_bt.ctf_clock_set_uuid(self._ptr, uuid.bytes)
+        utils._handle_ret(ret, "cannot set CTF writer clock object's UUID")
+
+    def _time(self, time):
+        utils._check_int64(time)
+        ret = native_bt.ctf_clock_set_time(self._ptr, time)
+
+    time = property(fset=_time)
+
+
+class _CtfWriterStream(stream._StreamBase):
+    @property
+    def discarded_events_count(self):
+        ret, count = native_bt.ctf_stream_get_discarded_events_count(self._ptr)
+        utils._handle_ret(ret, "cannot get CTF writer stream object's discarded events count")
+        return count
+
+    def append_discarded_events(self, count):
+        utils._check_uint64(count)
+        native_bt.ctf_stream_append_discarded_events(self._ptr, count)
+
+    def append_event(self, event):
+        utils._check_type(event, bt2.event._Event)
+        ret = native_bt.ctf_stream_append_event(self._ptr, event._ptr)
+        utils._handle_ret(ret, 'cannot append event object to CTF writer stream object')
+
+    def flush(self):
+        ret = native_bt.ctf_stream_flush(self._ptr)
+        utils._handle_ret(ret, 'cannot cannot flush CTF writer stream object')
+
+    @property
+    def packet_header_field(self):
+        field_ptr = native_bt.ctf_stream_get_packet_header(self._ptr)
+
+        if field_ptr is None:
+            return
+
+        return fields._create_from_ptr(field_ptr)
+
+    @packet_header_field.setter
+    def packet_header_field(self, packet_header_field):
+        packet_header_field_ptr = None
+
+        if packet_header_field is not None:
+            utils._check_type(packet_header_field, fields._Field)
+            packet_header_field_ptr = packet_header_field._ptr
+
+        ret = native_bt.ctf_stream_set_packet_header(self._ptr,
+                                                     packet_header_field_ptr)
+        utils._handle_ret(ret, "cannot set CTF writer stream object's packet header field")
+
+    @property
+    def packet_context_field(self):
+        field_ptr = native_bt.ctf_stream_get_packet_context(self._ptr)
+
+        if field_ptr is None:
+            return
+
+        return fields._create_from_ptr(field_ptr)
+
+    @packet_context_field.setter
+    def packet_context_field(self, packet_context_field):
+        packet_context_field_ptr = None
+
+        if packet_context_field is not None:
+            utils._check_type(packet_context_field, fields._Field)
+            packet_context_field_ptr = packet_context_field._ptr
+
+        ret = native_bt.ctf_stream_set_packet_context(self._ptr,
+                                                      packet_context_field_ptr)
+        utils._handle_ret(ret, "cannot set CTF writer stream object's packet context field")
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        if not _StreamBase.__eq__(self, other):
+            return False
+
+        self_props = (
+            self.discarded_events_count,
+            self.packet_header_field,
+            self.packet_context_field,
+        )
+        other_props = (
+            other.discarded_events_count,
+            other.packet_header_field,
+            other.packet_context_field,
+        )
+        return self_props == other_props
+
+    def _copy(self, copy_func):
+        cpy = self.stream_class(self.name)
+        cpy.append_discarded_events(self.discarded_events_count)
+        cpy.packet_header_field = copy_func(self.packet_header_field)
+        cpy.packet_context_field = copy_func(self.packet_context_field)
+        return cpy
+
+    def __copy__(self):
+        return self._copy(copy.copy)
+
+    def __deepcopy__(self, memo):
+        cpy = self._copy(copy.deepcopy)
+        memo[id(self)] = cpy
+        return cpy
+
+
+class CtfWriter(object._Object):
+    def __init__(self, path):
+        utils._check_str(path)
+        ptr = native_bt.ctf_writer_create(path)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create CTF writer object')
+
+        super().__init__(ptr)
+
+    @property
+    def trace(self):
+        trace_ptr = native_bt.ctf_writer_get_trace(self._ptr)
+        utils._handle_ptr(name, "cannot get CTF writer object's trace class")
+        return bt2.Trace._create_from_ptr(trace_ptr)
+
+    @property
+    def metadata_string(self):
+        metadata_string = native_bt.ctf_writer_get_metadata_string(self._ptr)
+        utils._handle_ptr(metadata_string, "cannot get CTF writer object's metadata string")
+        return metadata_string
+
+    def flush_metadata(self):
+        native_bt.ctf_writer_flush_metadata(self._ptr)
+
+    def add_clock(self, clock):
+        utils._check_type(clock, CtfWriterClock)
+        ret = native_bt.ctf_writer_add_clock(self._ptr, clock._ptr)
+        utils._handle_ret(ret, 'cannot add CTF writer clock object to CTF writer object')
diff --git a/bindings/python/bt2/event.py b/bindings/python/bt2/event.py
new file mode 100644 (file)
index 0000000..1ca642c
--- /dev/null
@@ -0,0 +1,311 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.clock_class
+import bt2.packet
+import bt2.stream
+import bt2.fields
+import numbers
+import copy
+import abc
+import bt2
+
+
+def _create_from_ptr(ptr):
+    # recreate the event class wrapper of this event's class (the
+    # identity could be different, but the underlying address should be
+    # the same)
+    event_class_ptr = native_bt.ctf_event_get_class(ptr)
+    utils._handle_ptr(event_class_ptr, "cannot get event object's class")
+    event_class = bt2.EventClass._create_from_ptr(event_class_ptr)
+    event = _Event._create_from_ptr(ptr)
+    event._event_class = event_class
+    return event
+
+
+class _Event(object._Object):
+    @property
+    def event_class(self):
+        return self._event_class
+
+    @property
+    def name(self):
+        return self._event_class.name
+
+    @property
+    def id(self):
+        return self._event_class.id
+
+    @property
+    def packet(self):
+        packet_ptr = native_bt.ctf_event_get_packet(self._ptr)
+
+        if packet_ptr is None:
+            return packet_ptr
+
+        return bt2.packet._Packet._create_from_ptr(packet_ptr)
+
+    @packet.setter
+    def packet(self, packet):
+        utils._check_type(packet, bt2.packet._Packet)
+        ret = native_bt.ctf_event_set_packet(self._ptr, packet._ptr)
+        utils._handle_ret(ret, "cannot set event object's packet object")
+
+    @property
+    def stream(self):
+        stream_ptr = native_bt.ctf_event_get_stream(self._ptr)
+
+        if stream_ptr is None:
+            return stream_ptr
+
+        return bt2.stream._Stream._create_from_ptr(stream_ptr)
+
+    @property
+    def header_field(self):
+        field_ptr = native_bt.ctf_event_get_header(self._ptr)
+
+        if field_ptr is None:
+            return
+
+        return bt2.fields._create_from_ptr(field_ptr)
+
+    @header_field.setter
+    def header_field(self, header_field):
+        header_field_ptr = None
+
+        if header_field is not None:
+            utils._check_type(header_field, bt2.fields._Field)
+            header_field_ptr = header_field._ptr
+
+        ret = native_bt.ctf_event_set_header(self._ptr, header_field_ptr)
+        utils._handle_ret(ret, "cannot set event object's header field")
+
+    @property
+    def stream_event_context_field(self):
+        field_ptr = native_bt.ctf_event_get_stream_event_context(self._ptr)
+
+        if field_ptr is None:
+            return
+
+        return bt2.fields._create_from_ptr(field_ptr)
+
+    @stream_event_context_field.setter
+    def stream_event_context_field(self, stream_event_context):
+        stream_event_context_ptr = None
+
+        if stream_event_context is not None:
+            utils._check_type(stream_event_context, bt2.fields._Field)
+            stream_event_context_ptr = stream_event_context._ptr
+
+        ret = native_bt.ctf_event_set_stream_event_context(self._ptr,
+                                                           stream_event_context_ptr)
+        utils._handle_ret(ret, "cannot set event object's stream event context field")
+
+    @property
+    def context_field(self):
+        field_ptr = native_bt.ctf_event_get_event_context(self._ptr)
+
+        if field_ptr is None:
+            return
+
+        return bt2.fields._create_from_ptr(field_ptr)
+
+    @context_field.setter
+    def context_field(self, context):
+        context_ptr = None
+
+        if context is not None:
+            utils._check_type(context, bt2.fields._Field)
+            context_ptr = context._ptr
+
+        ret = native_bt.ctf_event_set_event_context(self._ptr, context_ptr)
+        utils._handle_ret(ret, "cannot set event object's context field")
+
+    @property
+    def payload_field(self):
+        field_ptr = native_bt.ctf_event_get_payload_field(self._ptr)
+
+        if field_ptr is None:
+            return
+
+        return bt2.fields._create_from_ptr(field_ptr)
+
+    @payload_field.setter
+    def payload_field(self, payload):
+        payload_ptr = None
+
+        if payload is not None:
+            utils._check_type(payload, bt2.fields._Field)
+            payload_ptr = payload._ptr
+
+        ret = native_bt.ctf_event_set_payload_field(self._ptr, payload_ptr)
+        utils._handle_ret(ret, "cannot set event object's payload field")
+
+    def _get_clock_value_cycles(self, clock_class_ptr):
+        clock_value_ptr = native_bt.ctf_event_get_clock_value(self._ptr,
+                                                              clock_class_ptr)
+
+        if clock_value_ptr is None:
+            return
+
+        ret, cycles = native_bt.ctf_clock_value_get_value(clock_value_ptr)
+        native_bt.put(clock_value_ptr)
+        utils._handle_ret(ret, "cannot get clock value object's cycles")
+        return cycles
+
+    def get_clock_value(self, clock_class):
+        utils._check_type(clock_class, bt2.ClockClass)
+        clock_value_ptr = native_bt.ctf_event_get_clock_value(self._ptr,
+                                                              clock_class._ptr)
+
+        if clock_value_ptr is None:
+            return
+
+        clock_value = bt2.clock_class._create_clock_value_from_ptr(clock_value_ptr)
+        return clock_value
+
+    def set_clock_value(self, clock_value):
+        utils._check_type(clock_value, bt2.clock_class._ClockValue)
+        ret = native_bt.ctf_event_set_clock_value(self._ptr,
+                                                  clock_value._ptr)
+        utils._handle_ret(ret, "cannot set event object's clock value")
+
+    def __getitem__(self, key):
+        utils._check_str(key)
+        payload_field = self.payload_field
+
+        if payload_field is not None and key in payload_field:
+            return payload_field[key]
+
+        context_field = self.context_field
+
+        if context_field is not None and key in context_field:
+            return context_field[key]
+
+        sec_field = self.stream_event_context_field
+
+        if sec_field is not None and key in sec_field:
+            return sec_field[key]
+
+        header_field = self.header_field
+
+        if header_field is not None and key in header_field:
+            return header_field[key]
+
+        packet = self.packet
+
+        if packet is None:
+            raise KeyError(key)
+
+        pkt_context_field = packet.context_field
+
+        if pkt_context_field is not None and key in pkt_context_field:
+            return pkt_context_field[key]
+
+        pkt_header_field = packet.header_field
+
+        if pkt_header_field is not None and key in pkt_header_field:
+            return pkt_header_field[key]
+
+        raise KeyError(key)
+
+    @property
+    def _clock_classes(self):
+        stream_class = self.event_class.stream_class
+
+        if stream_class is None:
+            return []
+
+        trace = stream_class.trace
+
+        if trace is None:
+            return []
+
+        clock_classes = []
+
+        for clock_class in trace.clock_classes.values():
+            clock_classes.append(clock_class)
+
+        return clock_classes
+
+    @property
+    def _clock_class_ptrs(self):
+        return [cc._ptr for cc in self._clock_classes]
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        self_clock_values = {}
+        other_clock_values = {}
+
+        for clock_class_ptr in self._clock_class_ptrs:
+            self_clock_values[int(clock_class_ptr)] = self._get_clock_value_cycles(clock_class_ptr)
+
+        for clock_class_ptr in other._clock_class_ptrs:
+            other_clock_values[int(clock_class_ptr)] = self._get_clock_value_cycles(clock_class_ptr)
+
+        self_props = (
+            self.header_field,
+            self.stream_event_context_field,
+            self.context_field,
+            self.payload_field,
+            self_clock_values,
+        )
+        other_props = (
+            other.header_field,
+            other.stream_event_context_field,
+            other.context_field,
+            other.payload_field,
+            other_clock_values,
+        )
+        return self_props == other_props
+
+    def _copy(self, copy_func):
+        cpy = self.event_class()
+
+        # copy fields
+        cpy.header_field = copy_func(self.header_field)
+        cpy.stream_event_context_field = copy_func(self.stream_event_context_field)
+        cpy.context_field = copy_func(self.context_field)
+        cpy.payload_field = copy_func(self.payload_field)
+
+        # copy known clock values
+        for clock_class in self._clock_classes:
+            clock_value = self.get_clock_value(clock_class)
+
+            if clock_value is not None:
+                cpy.set_clock_value(clock_value)
+
+        return cpy
+
+    def __copy__(self):
+        return self._copy(copy.copy)
+
+    def __deepcopy__(self, memo):
+        cpy = self._copy(copy.deepcopy)
+        memo[id(self)] = cpy
+        return cpy
diff --git a/bindings/python/bt2/event_class.py b/bindings/python/bt2/event_class.py
new file mode 100644 (file)
index 0000000..6d82b64
--- /dev/null
@@ -0,0 +1,225 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.field_types
+import collections.abc
+import bt2.values
+import bt2.event
+import copy
+import bt2
+
+
+class _EventClassAttributesIterator(collections.abc.Iterator):
+    def __init__(self, attributes):
+        self._attributes = attributes
+        self._at = 0
+
+    def __next__(self):
+        if self._at == len(self._attributes):
+            raise StopIteration
+
+        name = native_bt.ctf_event_class_get_attribute_name(self._attributes._event_class_ptr,
+                                                            self._at)
+        utils._handle_ptr("cannot get event class object's attribute name")
+        self._at += 1
+        return name
+
+
+class _EventClassAttributes(collections.abc.MutableMapping):
+    def __init__(self, event_class_ptr):
+        self._event_class_ptr = event_class_ptr
+
+    def __getitem__(self, key):
+        utils._check_str(key)
+        value_ptr = native_bt.ctf_event_class_get_attribute_value_by_name(self._event_class_ptr,
+                                                                          key)
+
+        if value_ptr is None:
+            raise KeyError(key)
+
+        return bt2.values._create_from_ptr(value_ptr)
+
+    def __setitem__(self, key, value):
+        utils._check_str(key)
+        value = bt2.create_value(value)
+        ret = native_bt.ctf_event_class_set_attribute(self._event_class_ptr, key,
+                                                      value._ptr)
+        utils._handle_ret(ret, "cannot set event class object's attribute")
+
+    def __delitem__(self, key):
+        raise NotImplementedError
+
+    def __len__(self):
+        count = native_bt.ctf_event_class_get_attribute_count(self._event_class_ptr)
+        utils._handle_ret(count, "cannot get event class object's attribute count")
+        return count
+
+    def __iter__(self):
+        return _EventClassAttributesIterator(self)
+
+
+class EventClass(object._Object):
+    def __init__(self, name, id=None, context_field_type=None,
+                 payload_field_type=None, attributes=None):
+        utils._check_str(name)
+        ptr = native_bt.ctf_event_class_create(name)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create event class object')
+
+        super().__init__(ptr)
+
+        if id is not None:
+            self.id = id
+
+        if context_field_type is not None:
+            self.context_field_type = context_field_type
+
+        if payload_field_type is not None:
+            self.payload_field_type = payload_field_type
+
+        if attributes is not None:
+            for name, value in attributes.items():
+                self.attributes[name] = value
+
+    @property
+    def stream_class(self):
+        sc_ptr = native_bt.ctf_event_class_get_stream_class(self._ptr)
+
+        if sc_ptr is not None:
+            return bt2.StreamClass._create_from_ptr(sc_ptr)
+
+    @property
+    def attributes(self):
+        return _EventClassAttributes(self._ptr)
+
+    @property
+    def name(self):
+        return native_bt.ctf_event_class_get_name(self._ptr)
+
+    @property
+    def id(self):
+        id = native_bt.ctf_event_class_get_id(self._ptr)
+
+        if utils._is_m1ull(id):
+            raise bt2.Error("cannot get event class object's ID")
+
+        return id
+
+    @id.setter
+    def id(self, id):
+        utils._check_int64(id)
+        ret = native_bt.ctf_event_class_set_id(self._ptr, id)
+        utils._handle_ret(ret, "cannot set event class object's ID")
+
+    @property
+    def context_field_type(self):
+        ft_ptr = native_bt.ctf_event_class_get_context_type(self._ptr)
+
+        if ft_ptr is None:
+            return
+
+        return bt2.field_types._create_from_ptr(ft_ptr)
+
+    @context_field_type.setter
+    def context_field_type(self, context_field_type):
+        context_field_type_ptr = None
+
+        if context_field_type is not None:
+            utils._check_type(context_field_type, bt2.field_types._FieldType)
+            context_field_type_ptr = context_field_type._ptr
+
+        ret = native_bt.ctf_event_class_set_context_type(self._ptr, context_field_type_ptr)
+        utils._handle_ret(ret, "cannot set event class object's context field type")
+
+    @property
+    def payload_field_type(self):
+        ft_ptr = native_bt.ctf_event_class_get_payload_type(self._ptr)
+
+        if ft_ptr is None:
+            return
+
+        return bt2.field_types._create_from_ptr(ft_ptr)
+
+    @payload_field_type.setter
+    def payload_field_type(self, payload_field_type):
+        payload_field_type_ptr = None
+
+        if payload_field_type is not None:
+            utils._check_type(payload_field_type, bt2.field_types._FieldType)
+            payload_field_type_ptr = payload_field_type._ptr
+
+        ret = native_bt.ctf_event_class_set_payload_type(self._ptr, payload_field_type_ptr)
+        utils._handle_ret(ret, "cannot set event class object's payload field type")
+
+    def __call__(self):
+        event_ptr = native_bt.ctf_event_create(self._ptr)
+
+        if event_ptr is None:
+            raise bt2.CreationError('cannot create event field object')
+
+        return bt2.event._create_from_ptr(event_ptr)
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        self_attributes = {name: val for name, val in self.attributes.items()}
+        other_attributes = {name: val for name, val in other.attributes.items()}
+        self_props = (
+            self_attributes,
+            self.name,
+            self.id,
+            self.context_field_type,
+            self.payload_field_type
+        )
+        other_props = (
+            other_attributes,
+            other.name,
+            other.id,
+            other.context_field_type,
+            other.payload_field_type
+        )
+        return self_props == other_props
+
+    def _copy(self, ft_copy_func):
+        cpy = EventClass(self.name)
+        cpy.id = self.id
+
+        for name, value in self.attributes.items():
+            cpy.attributes[name] = value
+
+        cpy.context_field_type = ft_copy_func(self.context_field_type)
+        cpy.payload_field_type = ft_copy_func(self.payload_field_type)
+        return cpy
+
+    def __copy__(self):
+        return self._copy(lambda ft: ft)
+
+    def __deepcopy__(self, memo):
+        cpy = self._copy(copy.deepcopy)
+        memo[id(self)] = cpy
+        return cpy
diff --git a/bindings/python/bt2/field_types.py b/bindings/python/bt2/field_types.py
new file mode 100644 (file)
index 0000000..5268836
--- /dev/null
@@ -0,0 +1,673 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import collections.abc
+import bt2.fields
+import abc
+import bt2
+
+
+def _create_from_ptr(ptr):
+    typeid = native_bt.ctf_field_type_get_type_id(ptr)
+    return _TYPE_ID_TO_OBJ[typeid]._create_from_ptr(ptr)
+
+
+class _FieldType(object._Object, metaclass=abc.ABCMeta):
+    def __init__(self, ptr):
+        super().__init__(ptr)
+
+    def __eq__(self, other):
+        if not isinstance(other, self.__class__):
+            # not comparing apples to apples
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        ret = native_bt.ctf_field_type_compare(self._ptr, other._ptr)
+        utils._handle_ret(ret, "cannot compare field types")
+        return ret == 0
+
+    def _check_create_status(self, ptr):
+        if ptr is None:
+            raise bt2.CreationError('cannot create {} field type object'.format(self._NAME.lower()))
+
+    def __copy__(self):
+        ptr = native_bt.ctf_field_type_copy(self._ptr)
+        utils._handle_ptr(ptr, 'cannot copy {} field type object'.format(self._NAME.lower()))
+        return _create_from_ptr(ptr)
+
+    def __deepcopy__(self, memo):
+        cpy = self.__copy__()
+        memo[id(self)] = cpy
+        return cpy
+
+    def __call__(self, value=None):
+        field_ptr = native_bt.ctf_field_create(self._ptr)
+
+        if field_ptr is None:
+            raise bt2.CreationError('cannot create {} field object'.format(self._NAME.lower()))
+
+        field = bt2.fields._create_from_ptr(field_ptr)
+
+        if value is not None:
+            if not isinstance(field, (bt2.fields._IntegerField, bt2.fields._FloatingPointNumberField, bt2.fields._StringField)):
+                raise bt2.Error('cannot assign an initial value to a {} field object'.format(field._NAME))
+
+            field.value = value
+
+        return field
+
+
+class ByteOrder:
+    NATIVE = native_bt.CTF_BYTE_ORDER_NATIVE
+    LITTLE_ENDIAN = native_bt.CTF_BYTE_ORDER_LITTLE_ENDIAN
+    BIG_ENDIAN = native_bt.CTF_BYTE_ORDER_BIG_ENDIAN
+    NETWORK = native_bt.CTF_BYTE_ORDER_NETWORK
+
+
+class Encoding:
+    NONE = native_bt.CTF_STRING_ENCODING_NONE
+    UTF8 = native_bt.CTF_STRING_ENCODING_UTF8
+    ASCII = native_bt.CTF_STRING_ENCODING_ASCII
+
+
+class Base:
+    BINARY = native_bt.CTF_INTEGER_BASE_BINARY
+    OCTAL = native_bt.CTF_INTEGER_BASE_OCTAL
+    DECIMAL = native_bt.CTF_INTEGER_BASE_DECIMAL
+    HEXADECIMAL = native_bt.CTF_INTEGER_BASE_HEXADECIMAL
+
+
+class _AlignmentProp:
+    @property
+    def alignment(self):
+        alignment = native_bt.ctf_field_type_get_alignment(self._ptr)
+        utils._handle_ret(alignment, "cannot get field type object's alignment")
+        return alignment
+
+    @alignment.setter
+    def alignment(self, alignment):
+        utils._check_alignment(alignment)
+        ret = native_bt.ctf_field_type_set_alignment(self._ptr, alignment)
+        utils._handle_ret(ret, "cannot set field type object's alignment")
+
+
+class _ByteOrderProp:
+    @property
+    def byte_order(self):
+        bo = native_bt.ctf_field_type_get_byte_order(self._ptr)
+        utils._handle_ret(bo, "cannot get field type object's byte order")
+        return bo
+
+    @byte_order.setter
+    def byte_order(self, byte_order):
+        utils._check_int(byte_order)
+        ret = native_bt.ctf_field_type_set_byte_order(self._ptr, byte_order)
+        utils._handle_ret(ret, "cannot set field type object's byte order")
+
+
+class IntegerFieldType(_FieldType, _AlignmentProp, _ByteOrderProp):
+    _NAME = 'Integer'
+
+    def __init__(self, size, alignment=None, byte_order=None, is_signed=None,
+                 base=None, encoding=None, mapped_clock_class=None):
+        utils._check_uint64(size)
+
+        if size == 0:
+            raise ValueError('size is 0 bits')
+
+        ptr = native_bt.ctf_field_type_integer_create(size)
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+        if alignment is not None:
+            self.alignment = alignment
+
+        if byte_order is not None:
+            self.byte_order = byte_order
+
+        if is_signed is not None:
+            self.is_signed = is_signed
+
+        if base is not None:
+            self.base = base
+
+        if encoding is not None:
+            self.encoding = encoding
+
+        if mapped_clock_class is not None:
+            self.mapped_clock_class = mapped_clock_class
+
+    @property
+    def size(self):
+        size = native_bt.ctf_field_type_integer_get_size(self._ptr)
+        utils._handle_ret(size, "cannot get integer field type object's size")
+        return size
+
+    @property
+    def is_signed(self):
+        is_signed = native_bt.ctf_field_type_integer_get_signed(self._ptr)
+        utils._handle_ret(is_signed,
+                    "cannot get integer field type object's signedness")
+        return is_signed > 0
+
+    @is_signed.setter
+    def is_signed(self, is_signed):
+        utils._check_bool(is_signed)
+        ret = native_bt.ctf_field_type_integer_set_signed(self._ptr, int(is_signed))
+        utils._handle_ret(ret, "cannot set integer field type object's signedness")
+
+    @property
+    def base(self):
+        base = native_bt.ctf_field_type_integer_get_base(self._ptr)
+        utils._handle_ret(base, "cannot get integer field type object's base")
+        return base
+
+    @base.setter
+    def base(self, base):
+        utils._check_int(base)
+        ret = native_bt.ctf_field_type_integer_set_base(self._ptr, base)
+        utils._handle_ret(ret, "cannot set integer field type object's base")
+
+    @property
+    def encoding(self):
+        encoding = native_bt.ctf_field_type_integer_get_encoding(self._ptr)
+        utils._handle_ret(encoding, "cannot get integer field type object's encoding")
+        return encoding
+
+    @encoding.setter
+    def encoding(self, encoding):
+        utils._check_int(encoding)
+        ret = native_bt.ctf_field_type_integer_set_encoding(self._ptr, encoding)
+        utils._handle_ret(ret, "cannot set integer field type object's encoding")
+
+    @property
+    def mapped_clock_class(self):
+        ptr = native_bt.ctf_field_type_integer_get_mapped_clock_class(self._ptr)
+        utils._handle_ptr(ptr, "cannot get integer field type object's mapped clock class")
+        return bt2.ClockClass._create_from_ptr(ptr)
+
+    @mapped_clock_class.setter
+    def mapped_clock_class(self, clock_class):
+        utils._check_type(clock_class, bt2.ClockClass)
+        ret = native_bt.ctf_field_type_integer_set_mapped_clock_class(self._ptr, clock_class._ptr)
+        utils._handle_ret(ret, "cannot set integer field type object's mapped clock class")
+
+
+class FloatingPointNumberFieldType(_FieldType, _AlignmentProp, _ByteOrderProp):
+    _NAME = 'Floating point number'
+
+    def __init__(self, alignment=None, byte_order=None, exponent_size=None,
+                 mantissa_size=None):
+        ptr = native_bt.ctf_field_type_floating_point_create()
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+        if alignment is not None:
+            self.alignment = alignment
+
+        if byte_order is not None:
+            self.byte_order = byte_order
+
+        if exponent_size is not None:
+            self.exponent_size = exponent_size
+
+        if mantissa_size is not None:
+            self.mantissa_size = mantissa_size
+
+    @property
+    def exponent_size(self):
+        exp_size = native_bt.ctf_field_type_floating_point_get_exponent_digits(self._ptr)
+        utils._handle_ret(exp_size, "cannot get floating point number field type object's exponent size")
+        return exp_size
+
+    @exponent_size.setter
+    def exponent_size(self, exponent_size):
+        utils._check_uint64(exponent_size)
+        ret = native_bt.ctf_field_type_floating_point_set_exponent_digits(self._ptr, exponent_size)
+        utils._handle_ret(ret, "cannot set floating point number field type object's exponent size")
+
+    @property
+    def mantissa_size(self):
+        exp_size = native_bt.ctf_field_type_floating_point_get_mantissa_digits(self._ptr)
+        utils._handle_ret(exp_size, "cannot get floating point number field type object's mantissa size")
+        return exp_size
+
+    @mantissa_size.setter
+    def mantissa_size(self, mantissa_size):
+        utils._check_uint64(mantissa_size)
+        ret = native_bt.ctf_field_type_floating_point_set_mantissa_digits(self._ptr, mantissa_size)
+        utils._handle_ret(ret, "cannot set floating point number field type object's mantissa size")
+
+
+class _EnumerationFieldTypeMapping:
+    def __init__(self, name, lower, upper):
+        self._name = name
+        self._lower = lower
+        self._upper = upper
+
+    @property
+    def name(self):
+        return self._name
+
+    @property
+    def lower(self):
+        return self._lower
+
+    @property
+    def upper(self):
+        return self._upper
+
+    def __eq__(self, other):
+        if type(other) is not self.__class__:
+            return False
+
+        return (self.name, self.lower, self.upper) == (other.name, other.lower, other.upper)
+
+
+class _EnumerationFieldTypeMappingIterator(object._Object,
+                                           collections.abc.Iterator):
+    def __init__(self, iter_ptr, is_signed):
+        super().__init__(iter_ptr)
+        self._is_signed = is_signed
+        self._done = (iter_ptr is None)
+
+    def __next__(self):
+        if self._done:
+            raise StopIteration
+
+        if self._is_signed:
+            ret, name, lower, upper = native_bt.ctf_field_type_enumeration_mapping_iterator_get_signed(self._ptr)
+        else:
+            ret, name, lower, upper = native_bt.ctf_field_type_enumeration_mapping_iterator_get_unsigned(self._ptr)
+
+        utils._handle_ret(ret, "cannot get enumeration field type mapping iterator object's current mapping")
+        mapping = _EnumerationFieldTypeMapping(name, lower, upper)
+        ret = native_bt.ctf_field_type_enumeration_mapping_iterator_next(self._ptr)
+
+        if ret < 0:
+            self._done = True
+
+        return mapping
+
+
+class EnumerationFieldType(IntegerFieldType, collections.abc.Sequence):
+    _NAME = 'Enumeration'
+
+    def __init__(self, int_field_type=None, size=None, alignment=None,
+                 byte_order=None, is_signed=None, base=None, encoding=None,
+                 mapped_clock_class=None):
+        if int_field_type is None:
+            int_field_type = IntegerFieldType(size=size, alignment=alignment,
+                                              byte_order=byte_order,
+                                              is_signed=is_signed, base=base,
+                                              encoding=encoding,
+                                              mapped_clock_class=mapped_clock_class)
+
+        utils._check_type(int_field_type, IntegerFieldType)
+        ptr = native_bt.ctf_field_type_enumeration_create(int_field_type._ptr)
+        self._check_create_status(ptr)
+        _FieldType.__init__(self, ptr)
+
+    @property
+    def integer_field_type(self):
+        ptr = native_bt.ctf_field_type_enumeration_get_container_type(self._ptr)
+        utils._handle_ptr(ptr, "cannot get enumeration field type object's integer field type")
+        return _create_from_ptr(ptr)
+
+    @property
+    def size(self):
+        return self.integer_field_type.size
+
+    @property
+    def alignment(self):
+        return self.integer_field_type.alignment
+
+    @alignment.setter
+    def alignment(self, alignment):
+        self.integer_field_type.alignment = alignment
+
+    @property
+    def byte_order(self):
+        return self.integer_field_type.byte_order
+
+    @byte_order.setter
+    def byte_order(self, byte_order):
+        self.integer_field_type.byte_order = byte_order
+
+    @property
+    def is_signed(self):
+        return self.integer_field_type.is_signed
+
+    @is_signed.setter
+    def is_signed(self, is_signed):
+        self.integer_field_type.is_signed = is_signed
+
+    @property
+    def base(self):
+        return self.integer_field_type.base
+
+    @base.setter
+    def base(self, base):
+        self.integer_field_type.base = base
+
+    @property
+    def encoding(self):
+        return self.integer_field_type.encoding
+
+    @encoding.setter
+    def encoding(self, encoding):
+        self.integer_field_type.encoding = encoding
+
+    @property
+    def mapped_clock_class(self):
+        return self.integer_field_type.mapped_clock_class
+
+    @mapped_clock_class.setter
+    def mapped_clock_class(self, mapped_clock_class):
+        self.integer_field_type.mapped_clock_class = mapped_clock_class
+
+    def __len__(self):
+        count = native_bt.ctf_field_type_enumeration_get_mapping_count(self._ptr)
+        utils._handle_ret(count, "cannot get enumeration field type object's mapping count")
+        return count
+
+    def __getitem__(self, index):
+        utils._check_uint64(index)
+
+        if index >= len(self):
+            raise IndexError
+
+        if self.is_signed:
+            get_fn = native_bt.ctf_field_type_enumeration_get_mapping_signed
+        else:
+            get_fn = native_bt.ctf_field_type_enumeration_get_mapping_unsigned
+
+        ret, name, lower, upper = get_fn(self._ptr, index)
+        utils._handle_ret(ret, "cannot get enumeration field type object's mapping")
+        return _EnumerationFieldTypeMapping(name, lower, upper)
+
+    def _get_mapping_iter(self, iter_ptr):
+        return _EnumerationFieldTypeMappingIterator(iter_ptr, self.is_signed)
+
+    def mappings_by_name(self, name):
+        utils._check_str(name)
+        iter_ptr = native_bt.ctf_field_type_enumeration_find_mappings_by_name(self._ptr, name)
+        return self._get_mapping_iter(iter_ptr)
+
+    def mappings_by_value(self, value):
+        if self.is_signed:
+            utils._check_int64(value)
+            iter_ptr = native_bt.ctf_field_type_enumeration_find_mappings_by_signed_value(self._ptr, value)
+        else:
+            utils._check_uint64(value)
+            iter_ptr = native_bt.ctf_field_type_enumeration_find_mappings_by_unsigned_value(self._ptr, value)
+
+        return self._get_mapping_iter(iter_ptr)
+
+    def append_mapping(self, name, lower, upper=None):
+        utils._check_str(name)
+
+        if upper is None:
+            upper = lower
+
+        if self.is_signed:
+            add_fn = native_bt.ctf_field_type_enumeration_add_mapping
+            utils._check_int64(lower)
+            utils._check_int64(upper)
+        else:
+            add_fn = native_bt.ctf_field_type_enumeration_add_mapping_unsigned
+            utils._check_uint64(lower)
+            utils._check_uint64(upper)
+
+        ret = add_fn(self._ptr, name, lower, upper)
+        utils._handle_ret(ret, "cannot add mapping to enumeration field type object")
+
+    def __iadd__(self, mappings):
+        for mapping in mappings:
+            self.append_mapping(mapping.name, mapping.lower, mapping.upper)
+
+        return self
+
+
+class StringFieldType(_FieldType):
+    _NAME = 'String'
+
+    def __init__(self, encoding=None):
+        ptr = native_bt.ctf_field_type_string_create()
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+        if encoding is not None:
+            self.encoding = encoding
+
+    @property
+    def encoding(self):
+        encoding = native_bt.ctf_field_type_string_get_encoding(self._ptr)
+        utils._handle_ret(encoding, "cannot get string field type object's encoding")
+        return encoding
+
+    @encoding.setter
+    def encoding(self, encoding):
+        utils._check_int(encoding)
+        ret = native_bt.ctf_field_type_string_set_encoding(self._ptr, encoding)
+        utils._handle_ret(ret, "cannot set string field type object's encoding")
+
+
+class _FieldContainer(collections.abc.Mapping):
+    def __len__(self):
+        count = self._count()
+        utils._handle_ret(count, "cannot get {} field type object's field count".format(self._NAME.lower()))
+        return count
+
+    def __getitem__(self, key):
+        if not isinstance(key, str):
+            raise TypeError("'{}' is not a 'str' object".format(key.__class__.__name__))
+
+        ptr = self._get_field_by_name(key)
+
+        if ptr is None:
+            raise KeyError(key)
+
+        return _create_from_ptr(ptr)
+
+    def __iter__(self):
+        return self._ITER_CLS(self)
+
+    def append_field(self, name, field_type):
+        utils._check_str(name)
+        utils._check_type(field_type, _FieldType)
+        ret = self._add_field(field_type._ptr, name)
+        utils._handle_ret(ret, "cannot add field to {} field type object".format(self._NAME.lower()))
+
+    def __iadd__(self, fields):
+        for name, field_type in fields.items():
+            self.append_field(name, field_type)
+
+        return self
+
+    def at_index(self, index):
+        utils._check_uint64(index)
+        return self._at(index)
+
+
+class _StructureFieldTypeFieldIterator(collections.abc.Iterator):
+    def __init__(self, struct_field_type):
+        self._struct_field_type = struct_field_type
+        self._at = 0
+
+    def __next__(self):
+        if self._at == len(self._struct_field_type):
+            raise StopIteration
+
+        ret, name, field_type_ptr = native_bt.ctf_field_type_structure_get_field(self._struct_field_type._ptr, self._at)
+        utils._handle_ret(ret, "cannot get structure field type object's field")
+        native_bt.put(field_type_ptr)
+        self._at += 1
+        return name
+
+
+class StructureFieldType(_FieldType, _FieldContainer, _AlignmentProp):
+    _NAME = 'Structure'
+    _ITER_CLS = _StructureFieldTypeFieldIterator
+
+    def __init__(self, min_alignment=None):
+        ptr = native_bt.ctf_field_type_structure_create()
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+        if min_alignment is not None:
+            self.min_alignment = min_alignment
+
+    def _count(self):
+        return native_bt.ctf_field_type_structure_get_field_count(self._ptr)
+
+    def _get_field_by_name(self, key):
+        return native_bt.ctf_field_type_structure_get_field_type_by_name(self._ptr, key)
+
+    def _add_field(self, ptr, name):
+        return native_bt.ctf_field_type_structure_add_field(self._ptr, ptr,
+                                                            name)
+
+    def _at(self, index):
+        ret, name, field_type_ptr = native_bt.ctf_field_type_structure_get_field(self._ptr, index)
+        utils._handle_ret(ret, "cannot get structure field type object's field")
+        return _create_from_ptr(field_type_ptr)
+
+
+StructureFieldType.min_alignment = property(fset=StructureFieldType.alignment.fset)
+StructureFieldType.alignment = property(fget=StructureFieldType.alignment.fget)
+
+
+class _VariantFieldTypeFieldIterator(collections.abc.Iterator):
+    def __init__(self, variant_field_type):
+        self._variant_field_type = variant_field_type
+        self._at = 0
+
+    def __next__(self):
+        if self._at == len(self._variant_field_type):
+            raise StopIteration
+
+        ret, name, field_type_ptr = native_bt.ctf_field_type_variant_get_field(self._variant_field_type._ptr, self._at)
+        utils._handle_ret(ret, "cannot get variant field type object's field")
+        native_bt.put(field_type_ptr)
+        self._at += 1
+        return name
+
+
+class VariantFieldType(_FieldType, _FieldContainer, _AlignmentProp):
+    _NAME = 'Variant'
+    _ITER_CLS = _VariantFieldTypeFieldIterator
+
+    def __init__(self, tag_name):
+        utils._check_str(tag_name)
+        ptr = native_bt.ctf_field_type_variant_create(None, tag_name)
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+    @property
+    def tag_name(self):
+        tag_name = native_bt.ctf_field_type_variant_get_tag_name(self._ptr)
+        utils._handle_ptr(tag_name, "cannot get variant field type object's tag name")
+        return tag_name
+
+    @tag_name.setter
+    def tag_name(self, tag_name):
+        utils._check_str(tag_name)
+        ret = native_bt.ctf_field_type_variant_set_tag_name(self._ptr, tag_name)
+        utils._handle_ret(ret, "cannot set variant field type object's tag name")
+
+    def _count(self):
+        return native_bt.ctf_field_type_variant_get_field_count(self._ptr)
+
+    def _get_field_by_name(self, key):
+        return native_bt.ctf_field_type_variant_get_field_type_by_name(self._ptr, key)
+
+    def _add_field(self, ptr, name):
+        return native_bt.ctf_field_type_variant_add_field(self._ptr, ptr, name)
+
+    def _at(self, index):
+        ret, name, field_type_ptr = native_bt.ctf_field_type_variant_get_field(self._ptr, index)
+        utils._handle_ret(ret, "cannot get variant field type object's field")
+        return _create_from_ptr(field_type_ptr)
+
+
+class ArrayFieldType(_FieldType):
+    _NAME = 'Array'
+
+    def __init__(self, element_field_type, length):
+        utils._check_type(element_field_type, _FieldType)
+        utils._check_uint64(length)
+        ptr = native_bt.ctf_field_type_array_create(element_field_type._ptr, length)
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+    @property
+    def length(self):
+        length = native_bt.ctf_field_type_array_get_length(self._ptr)
+        utils._handle_ret(length, "cannot get array field type object's length")
+        return length
+
+    @property
+    def element_field_type(self):
+        ptr = native_bt.ctf_field_type_array_get_element_type(self._ptr)
+        utils._handle_ptr(ptr, "cannot get array field type object's element field type")
+        return _create_from_ptr(ptr)
+
+
+class SequenceFieldType(_FieldType):
+    _NAME = 'Sequence'
+
+    def __init__(self, element_field_type, length_name):
+        utils._check_type(element_field_type, _FieldType)
+        utils._check_str(length_name)
+        ptr = native_bt.ctf_field_type_sequence_create(element_field_type._ptr,
+                                                       length_name)
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+    @property
+    def length_name(self):
+        length_name = native_bt.ctf_field_type_sequence_get_length_field_name(self._ptr)
+        utils._handle_ptr(length_name, "cannot get sequence field type object's length name")
+        return length_name
+
+    @property
+    def element_field_type(self):
+        ptr = native_bt.ctf_field_type_sequence_get_element_type(self._ptr)
+        utils._handle_ptr(ptr, "cannot get sequence field type object's element field type")
+        return _create_from_ptr(ptr)
+
+
+_TYPE_ID_TO_OBJ = {
+    native_bt.CTF_TYPE_ID_INTEGER: IntegerFieldType,
+    native_bt.CTF_TYPE_ID_FLOAT: FloatingPointNumberFieldType,
+    native_bt.CTF_TYPE_ID_ENUM: EnumerationFieldType,
+    native_bt.CTF_TYPE_ID_STRING: StringFieldType,
+    native_bt.CTF_TYPE_ID_STRUCT: StructureFieldType,
+    native_bt.CTF_TYPE_ID_ARRAY: ArrayFieldType,
+    native_bt.CTF_TYPE_ID_SEQUENCE: SequenceFieldType,
+    native_bt.CTF_TYPE_ID_VARIANT: VariantFieldType,
+    native_bt.CTF_TYPE_ID_UNTAGGED_VARIANT: VariantFieldType,
+}
diff --git a/bindings/python/bt2/fields.py b/bindings/python/bt2/fields.py
new file mode 100644 (file)
index 0000000..c63cf35
--- /dev/null
@@ -0,0 +1,587 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.field_types
+import collections.abc
+import functools
+import numbers
+import math
+import abc
+import bt2
+
+
+def _create_from_ptr(ptr):
+    # recreate the field type wrapper of this field's type (the identity
+    # could be different, but the underlying address should be the
+    # same)
+    field_type_ptr = native_bt.ctf_field_get_type(ptr)
+    utils._handle_ptr(field_type_ptr, "cannot get field object's type")
+    field_type = bt2.field_types._create_from_ptr(field_type_ptr)
+    typeid = native_bt.ctf_field_type_get_type_id(field_type._ptr)
+    field = _TYPE_ID_TO_OBJ[typeid]._create_from_ptr(ptr)
+    field._field_type = field_type
+    return field
+
+
+class _Field(object._Object, metaclass=abc.ABCMeta):
+    def __copy__(self):
+        ptr = native_bt.ctf_field_copy(self._ptr)
+        utils._handle_ptr(ptr, 'cannot copy {} field object'.format(self._NAME.lower()))
+        return _create_from_ptr(ptr)
+
+    def __deepcopy__(self, memo):
+        cpy = self.__copy__()
+        memo[id(self)] = cpy
+        return cpy
+
+    @property
+    def field_type(self):
+        return self._field_type
+
+
+@functools.total_ordering
+class _NumericField(_Field):
+    @staticmethod
+    def _extract_value(other):
+        if other is True or other is False:
+            return other
+
+        if isinstance(other, numbers.Integral):
+            return int(other)
+
+        if isinstance(other, numbers.Real):
+            return float(other)
+
+        if isinstance(other, numbers.Complex):
+            return complex(other)
+
+        raise TypeError("'{}' object is not a number object".format(other.__class__.__name__))
+
+    def __int__(self):
+        return int(self.value)
+
+    def __float__(self):
+        return float(self.value)
+
+    def __str__(self):
+        return str(self.value)
+
+    def __lt__(self, other):
+        if not isinstance(other, numbers.Number):
+            raise TypeError('unorderable types: {}() < {}()'.format(self.__class__.__name__,
+                                                                    other.__class__.__name__))
+
+        return self.value < float(other)
+
+    def __le__(self, other):
+        if not isinstance(other, numbers.Number):
+            raise TypeError('unorderable types: {}() <= {}()'.format(self.__class__.__name__,
+                                                                     other.__class__.__name__))
+
+        return self.value <= float(other)
+
+    def __eq__(self, other):
+        if not isinstance(other, numbers.Number):
+            return False
+
+        return self.value == complex(other)
+
+    def __rmod__(self, other):
+        return self._extract_value(other) % self.value
+
+    def __mod__(self, other):
+        return self.value % self._extract_value(other)
+
+    def __rfloordiv__(self, other):
+        return self._extract_value(other) // self.value
+
+    def __floordiv__(self, other):
+        return self.value // self._extract_value(other)
+
+    def __round__(self, ndigits=None):
+        if ndigits is None:
+            return round(self.value)
+        else:
+            return round(self.value, ndigits)
+
+    def __ceil__(self):
+        return math.ceil(self.value)
+
+    def __floor__(self):
+        return math.floor(self.value)
+
+    def __trunc__(self):
+        return int(self.value)
+
+    def __abs__(self):
+        return abs(self.value)
+
+    def __add__(self, other):
+        return self.value + self._extract_value(other)
+
+    def __radd__(self, other):
+        return self.__add__(other)
+
+    def __neg__(self):
+        return -self.value
+
+    def __pos__(self):
+        return +self.value
+
+    def __mul__(self, other):
+        return self.value * self._extract_value(other)
+
+    def __rmul__(self, other):
+        return self.__mul__(other)
+
+    def __truediv__(self, other):
+        return self.value / self._extract_value(other)
+
+    def __rtruediv__(self, other):
+        return self._extract_value(other) / self.value
+
+    def __pow__(self, exponent):
+        return self.value ** self._extract_value(exponent)
+
+    def __rpow__(self, base):
+        return self._extract_value(base) ** self.value
+
+    def __iadd__(self, other):
+        self.value = self + other
+        return self
+
+    def __isub__(self, other):
+        self.value = self - other
+        return self
+
+    def __imul__(self, other):
+        self.value = self * other
+        return self
+
+    def __itruediv__(self, other):
+        self.value = self / other
+        return self
+
+    def __ifloordiv__(self, other):
+        self.value = self // other
+        return self
+
+    def __imod__(self, other):
+        self.value = self % other
+        return self
+
+    def __ipow__(self, other):
+        self.value = self ** other
+        return self
+
+
+class _IntegralField(_NumericField, numbers.Integral):
+    def __lshift__(self, other):
+        return self.value << self._extract_value(other)
+
+    def __rlshift__(self, other):
+        return self._extract_value(other) << self.value
+
+    def __rshift__(self, other):
+        return self.value >> self._extract_value(other)
+
+    def __rrshift__(self, other):
+        return self._extract_value(other) >> self.value
+
+    def __and__(self, other):
+        return self.value & self._extract_value(other)
+
+    def __rand__(self, other):
+        return self._extract_value(other) & self.value
+
+    def __xor__(self, other):
+        return self.value ^ self._extract_value(other)
+
+    def __rxor__(self, other):
+        return self._extract_value(other) ^ self.value
+
+    def __or__(self, other):
+        return self.value | self._extract_value(other)
+
+    def __ror__(self, other):
+        return self._extract_value(other) | self.value
+
+    def __invert__(self):
+        return ~self.value
+
+    def __ilshift__(self, other):
+        self.value = self << other
+        return self
+
+    def __irshift__(self, other):
+        self.value = self >> other
+        return self
+
+    def __iand__(self, other):
+        self.value = self & other
+        return self
+
+    def __ixor__(self, other):
+        self.value = self ^ other
+        return self
+
+    def __ior__(self, other):
+        self.value = self | other
+        return self
+
+
+class _RealField(_NumericField, numbers.Real):
+    pass
+
+
+class _IntegerField(_IntegralField):
+    _NAME = 'Integer'
+
+    def _value_to_int(self, value):
+        if not isinstance(value, numbers.Real):
+            raise TypeError('expecting a real number object')
+
+        value = int(value)
+
+        if self.field_type.is_signed:
+            utils._check_int64(value)
+        else:
+            utils._check_uint64(value)
+
+        return value
+
+    @property
+    def value(self):
+        if self.field_type.is_signed:
+            ret, value = native_bt.ctf_field_signed_integer_get_value(self._ptr)
+        else:
+            ret, value = native_bt.ctf_field_unsigned_integer_get_value(self._ptr)
+
+        utils._handle_ret(ret, "cannot get integer field object's value")
+        return value
+
+    @value.setter
+    def value(self, value):
+        value = self._value_to_int(value)
+
+        if self.field_type.is_signed:
+            ret = native_bt.ctf_field_signed_integer_set_value(self._ptr, value)
+        else:
+            ret = native_bt.ctf_field_unsigned_integer_set_value(self._ptr, value)
+
+        utils._handle_ret(ret, "cannot set integer field object's value")
+
+
+class _FloatingPointNumberField(_RealField):
+    _NAME = 'Floating point number'
+
+    def _value_to_float(self, value):
+        if not isinstance(value, numbers.Real):
+            raise TypeError("expecting a real number object")
+
+        return float(value)
+
+    @property
+    def value(self):
+        ret, value = native_bt.ctf_field_floating_point_get_value(self._ptr)
+        utils._handle_ret(ret, "cannot get floating point number field object's value")
+        return value
+
+    @value.setter
+    def value(self, value):
+        value = self._value_to_float(value)
+        ret = native_bt.ctf_field_floating_point_set_value(self._ptr, value)
+        utils._handle_ret(ret, "cannot set floating point number field object's value")
+
+
+class _EnumerationField(_IntegerField):
+    _NAME = 'Enumeration'
+
+    @property
+    def integer_field(self):
+        int_field_ptr = native_bt.ctf_field_enumeration_get_container(self._ptr)
+        utils._handle_ptr(int_field_ptr,
+                          "cannot get enumeration field object's underlying integer field")
+        return _create_from_ptr(int_field_ptr)
+
+    @property
+    def value(self):
+        return self.integer_field.value
+
+    @value.setter
+    def value(self, value):
+        self.integer_field.value = value
+
+    @property
+    def mappings(self):
+        iter_ptr = bt_ctf_field_enumeration_get_mappings(self._ptr)
+        return self.field_type._get_mapping_iter(iter_ptr)
+
+
+@functools.total_ordering
+class _StringField(_Field, collections.abc.Sequence):
+    _NAME = 'String'
+
+    def _value_to_str(self, value):
+        if isinstance(value, self.__class__):
+            value = value.value
+
+        if not isinstance(value, str):
+            raise TypeError("expecting a 'str' object")
+
+        return value
+
+    @property
+    def value(self):
+        value = native_bt.ctf_field_string_get_value(self._ptr)
+        utils._handle_ptr(value, "cannot get string field object's value")
+        return value
+
+    @value.setter
+    def value(self, value):
+        value = self._value_to_str(value)
+        ret = native_bt.ctf_field_string_set_value(self._ptr, value)
+        utils._handle_ret(ret, "cannot set string field object's value")
+
+    def __eq__(self, other):
+        try:
+            other = self._value_to_str(other)
+        except:
+            return False
+
+        return self.value == other
+
+    def __le__(self, other):
+        return self.value <= self._value_to_str(other)
+
+    def __lt__(self, other):
+        return self.value < self._value_to_str(other)
+
+    def __bool__(self):
+        return bool(self.value)
+
+    def __str__(self):
+        return self.value
+
+    def __getitem__(self, index):
+        return self.value[index]
+
+    def __len__(self):
+        return len(self.value)
+
+    def __iadd__(self, value):
+        value = self._value_to_str(value)
+        ret = native_bt.ctf_field_string_append(self._ptr, value)
+        utils._handle_ret(ret, "cannot append to string field object's value")
+        return self
+
+
+class _ContainerField(_Field):
+    def __bool__(self):
+        return len(self) != 0
+
+    def __len__(self):
+        count = self._count()
+        utils._handle_ret(count, "cannot get {} field object's field count".format(self._NAME.lower()))
+        return count
+
+    def __delitem__(self, index):
+        raise NotImplementedError
+
+
+class _StructureField(_ContainerField, collections.abc.MutableMapping):
+    _NAME = 'Structure'
+
+    def _count(self):
+        return len(self.field_type)
+
+    def __getitem__(self, key):
+        utils._check_str(key)
+        ptr = native_bt.ctf_field_structure_get_field(self._ptr, key)
+
+        if ptr is None:
+            raise KeyError(key)
+
+        return _create_from_ptr(ptr)
+
+    def __setitem__(self, key, value):
+        # we can only set numbers and strings
+        if not isinstance(value, (numbers.Number, str)):
+            raise TypeError('expecting number object or string')
+
+        # raises if index is somehow invalid
+        field = self[key]
+
+        if not isinstance(field, (_NumericField, _StringField)):
+            raise TypeError('can only set the value of a number or string field')
+
+        # the field's property does the appropriate conversion or raises
+        # the appropriate exception
+        field.value = value
+
+    def at_index(self, index):
+        utils._check_uint64(index)
+        field_ptr = native_bt.ctf_field_structure_get_field_by_index(self._ptr, index)
+        utils._handle_ptr(field_ptr, "cannot get structure field object's field")
+        return _create_from_ptr(field_ptr)
+
+    def __iter__(self):
+        # same name iterator
+        return iter(self.field_type)
+
+    def __eq__(self, other):
+        if not isinstance(other, collections.abc.Mapping):
+            return False
+
+        if len(self) != len(other):
+            return False
+
+        for self_key, self_value in self.items():
+            if self_key not in other:
+                return False
+
+            other_value = other[self_key]
+
+            if self_value != other_value:
+                return False
+
+        return True
+
+
+class _VariantField(_Field):
+    _NAME = 'Variant'
+
+    @property
+    def tag_field(self):
+        field_ptr = native_bt.ctf_field_variant_get_tag(self._ptr)
+        utils._handle_ptr(field_ptr, "cannot get variant field object's tag field")
+        return _create_from_ptr(field_ptr)
+
+    @property
+    def selected_field(self):
+        return self.field()
+
+    def field(self, tag_field=None):
+        if tag_field is None:
+            field_ptr = native_bt.ctf_field_variant_get_current_field(self._ptr)
+            utils._handle_ptr(field_ptr, "cannot get variant field object's selected field")
+        else:
+            utils._check_type(tag_field, _EnumerationField)
+            field_ptr = native_bt.ctf_field_variant_get_field(self._ptr, tag_field._ptr)
+            utils._handle_ptr(field_ptr, "cannot select variant field object's field")
+
+        return _create_from_ptr(field_ptr)
+
+    def __eq__(self, other):
+        return self.field == other
+
+
+class _ArraySequenceField(_ContainerField, collections.abc.MutableSequence):
+    def __getitem__(self, index):
+        if not isinstance(index, numbers.Integral):
+            raise TypeError("'{}' is not an integral number object: invalid index".format(index.__class__.__name__))
+
+        index = int(index)
+
+        if index < 0 or index >= len(self):
+            raise IndexError('{} field object index is out of range'.format(self._NAME))
+
+        field_ptr = self._get_field_ptr_at_index(index)
+        utils._handle_ptr(field_ptr, "cannot get {} field object's field".format(self._NAME))
+        return _create_from_ptr(field_ptr)
+
+    def __setitem__(self, index, value):
+        # we can only set numbers and strings
+        if not isinstance(value, (numbers.Number, _StringField, str)):
+            raise TypeError('expecting number or string object')
+
+        # raises if index is somehow invalid
+        field = self[index]
+
+        if not isinstance(field, (_NumericField, _StringField)):
+            raise TypeError('can only set the value of a number or string field')
+
+        # the field's property does the appropriate conversion or raises
+        # the appropriate exception
+        field.value = value
+
+    def insert(self, index, value):
+        raise NotImplementedError
+
+    def __eq__(self, other):
+        if not isinstance(other, collections.abc.Sequence):
+            return False
+
+        if len(self) != len(other):
+            return False
+
+        for self_field, other_field in zip(self, other):
+            if self_field != other_field:
+                return False
+
+        return True
+
+
+class _ArrayField(_ArraySequenceField):
+    _NAME = 'Array'
+
+    def _count(self):
+        return self.field_type.length
+
+    def _get_field_ptr_at_index(self, index):
+        return native_bt.ctf_field_array_get_field(self._ptr, index)
+
+
+class _SequenceField(_ArraySequenceField):
+    _NAME = 'Sequence'
+
+    def _count(self):
+        return self.length_field.value
+
+    @property
+    def length_field(self):
+        field_ptr = native_bt.ctf_field_sequence_get_length(self._ptr)
+        utils._handle_ptr("cannot get sequence field object's length field")
+        return _create_from_ptr(field_ptr)
+
+    @length_field.setter
+    def length_field(self, length_field):
+        utils._check_type(length_field, _IntegerField)
+        ret = native_bt.ctf_field_sequence_set_length(self._ptr, length_field._ptr)
+        utils._handle_ret(ret, "cannot set sequence field object's length field")
+
+    def _get_field_ptr_at_index(self, index):
+        return native_bt.ctf_field_sequence_get_field(self._ptr, index)
+
+
+_TYPE_ID_TO_OBJ = {
+    native_bt.CTF_TYPE_ID_INTEGER: _IntegerField,
+    native_bt.CTF_TYPE_ID_FLOAT: _FloatingPointNumberField,
+    native_bt.CTF_TYPE_ID_ENUM: _EnumerationField,
+    native_bt.CTF_TYPE_ID_STRING: _StringField,
+    native_bt.CTF_TYPE_ID_STRUCT: _StructureField,
+    native_bt.CTF_TYPE_ID_ARRAY: _ArrayField,
+    native_bt.CTF_TYPE_ID_SEQUENCE: _SequenceField,
+    native_bt.CTF_TYPE_ID_VARIANT: _VariantField,
+    native_bt.CTF_TYPE_ID_UNTAGGED_VARIANT: _VariantField,
+}
diff --git a/bindings/python/bt2/native_bt.i b/bindings/python/bt2/native_bt.i
new file mode 100644 (file)
index 0000000..88953af
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+typedef const unsigned char *BTUUID;
+%}
+
+#ifndef SWIGPYTHON
+# error Unsupported output language
+#endif
+
+/* For uint*_t/int*_t */
+%include "stdint.i"
+
+/* Remove `bt_` and `BT_` prefixes from function names and enumeration items */
+%rename("%(strip:[bt_])s", %$isfunction) "";
+%rename("%(strip:[BT_])s", %$isenumitem) "";
+
+/* Output argument typemap for string output (always appends) */
+%typemap(in, numinputs=0) const char **BTOUTSTR (char *temp_value = NULL) {
+       $1 = &temp_value;
+}
+
+%typemap(argout) const char **BTOUTSTR {
+       if (*$1) {
+               /* SWIG_Python_AppendOutput() steals the created object */
+               $result = SWIG_Python_AppendOutput($result, SWIG_Python_str_FromChar(*$1));
+       } else {
+               /* SWIG_Python_AppendOutput() steals Py_None */
+               Py_INCREF(Py_None);
+               $result = SWIG_Python_AppendOutput($result, Py_None);
+       }
+}
+
+/* Output argument typemap for field type output (always appends) */
+%typemap(in, numinputs=0) struct bt_ctf_field_type **BTOUTFT (struct bt_ctf_field_type *temp_ft = NULL) {
+       $1 = &temp_ft;
+}
+
+%typemap(argout) struct bt_ctf_field_type **BTOUTFT {
+       if (*$1) {
+               /* SWIG_Python_AppendOutput() steals the created object */
+               $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj(SWIG_as_voidptr(*$1), SWIGTYPE_p_bt_ctf_field_type, 0));
+       } else {
+               /* SWIG_Python_AppendOutput() steals Py_None */
+               Py_INCREF(Py_None);
+               $result = SWIG_Python_AppendOutput($result, Py_None);
+       }
+}
+
+/* Input argument typemap for UUID bytes */
+%typemap(in) BTUUID {
+       $1 = (unsigned char *) PyBytes_AsString($input);
+}
+
+/* Output argument typemap for UUID bytes */
+%typemap(out) BTUUID {
+       if (!$1) {
+               Py_INCREF(Py_None);
+               $result = Py_None;
+       } else {
+               $result = PyBytes_FromStringAndSize((const char *) $1, 16);
+       }
+}
+
+/*
+ * Input and output argument typemaps for raw Python objects (direct).
+ *
+ * Those typemaps honor the convention of Python C function calls with
+ * respect to reference counting: parameters are passed as borrowed
+ * references, and objects are returned as new references. The wrapped
+ * C function must ensure that the return value is always a new
+ * reference, and never steal parameter references.
+ */
+%typemap(in) PyObject * {
+       $1 = $input;
+}
+
+%typemap(out) PyObject * {
+       $result = $1;
+}
+
+/* Per-module interface files */
+%include "native_btclockclass.i"
+%include "native_btevent.i"
+%include "native_bteventclass.i"
+%include "native_btfields.i"
+%include "native_btft.i"
+%include "native_btpacket.i"
+%include "native_btref.i"
+%include "native_btstream.i"
+%include "native_btstreamclass.i"
+%include "native_bttrace.i"
+%include "native_btvalues.i"
+%include "native_btctfwriter.i"
+%include "native_btcomponentclass.i"
+%include "native_btcomponent.i"
+%include "native_btnotifiter.i"
+%include "native_btnotification.i"
+%include "native_btplugin.i"
diff --git a/bindings/python/bt2/native_btclockclass.i b/bindings/python/bt2/native_btclockclass.i
new file mode 100644 (file)
index 0000000..c013b8b
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/clock-class.h>
+%}
+
+/* Types */
+struct bt_ctf_clock_class;
+struct bt_ctf_clock_value;
+
+/* Clock class functions */
+struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name);
+const char *bt_ctf_clock_class_get_name(struct bt_ctf_clock_class *clock_class);
+int bt_ctf_clock_class_set_name(struct bt_ctf_clock_class *clock_class, const char *name);
+const char *bt_ctf_clock_class_get_description(struct bt_ctf_clock_class *clock_class);
+int bt_ctf_clock_class_set_description(struct bt_ctf_clock_class *clock_class,
+               const char *desc);
+uint64_t bt_ctf_clock_class_get_frequency(struct bt_ctf_clock_class *clock_class);
+int bt_ctf_clock_class_set_frequency(struct bt_ctf_clock_class *clock_class,
+               uint64_t freq);
+uint64_t bt_ctf_clock_class_get_precision(struct bt_ctf_clock_class *clock_class);
+int bt_ctf_clock_class_set_precision(struct bt_ctf_clock_class *clock_class,
+               uint64_t precision);
+int bt_ctf_clock_class_get_offset_s(struct bt_ctf_clock_class *clock_class,
+               int64_t *OUTPUT);
+int bt_ctf_clock_class_set_offset_s(struct bt_ctf_clock_class *clock_class,
+               int64_t offset_s);
+int bt_ctf_clock_class_get_offset_cycles(struct bt_ctf_clock_class *clock_class,
+               int64_t *OUTPUT);
+int bt_ctf_clock_class_set_offset_cycles(struct bt_ctf_clock_class *clock_class,
+               int64_t offset);
+int bt_ctf_clock_class_get_is_absolute(struct bt_ctf_clock_class *clock_class);
+int bt_ctf_clock_class_set_is_absolute(struct bt_ctf_clock_class *clock_class,
+               int is_absolute);
+BTUUID bt_ctf_clock_class_get_uuid(struct bt_ctf_clock_class *clock_class);
+int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class,
+               BTUUID uuid);
+
+/* Clock value functions */
+struct bt_ctf_clock_value *bt_ctf_clock_value_create(
+               struct bt_ctf_clock_class *clock_class, uint64_t value);
+int bt_ctf_clock_value_get_value(
+               struct bt_ctf_clock_value *clock_value, uint64_t *OUTPUT);
+int bt_ctf_clock_value_get_value_ns_from_epoch(
+               struct bt_ctf_clock_value *clock_value, int64_t *OUTPUT);
+struct bt_ctf_clock_class *bt_ctf_clock_value_get_class(
+               struct bt_ctf_clock_value *clock_value);
diff --git a/bindings/python/bt2/native_btcomponent.i b/bindings/python/bt2/native_btcomponent.i
new file mode 100644 (file)
index 0000000..6394efd
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/component/component.h>
+#include <babeltrace/component/component-source.h>
+#include <babeltrace/component/component-sink.h>
+#include <babeltrace/component/component-filter.h>
+%}
+
+/* Types */
+struct bt_component;
+
+/* Status */
+enum bt_component_status {
+       BT_COMPONENT_STATUS_OK =                0,
+       BT_COMPONENT_STATUS_END =               1,
+       BT_COMPONENT_STATUS_AGAIN =             2,
+       BT_COMPONENT_STATUS_ERROR =             -1,
+       BT_COMPONENT_STATUS_UNSUPPORTED =       -2,
+       BT_COMPONENT_STATUS_INVALID =           -3,
+       BT_COMPONENT_STATUS_NOMEM =             -4,
+};
+
+/* General functions */
+struct bt_component *bt_component_create_with_init_method_data(
+               struct bt_component_class *component_class, const char *name,
+               struct bt_value *params, void *init_method_data);
+const char *bt_component_get_name(struct bt_component *component);
+struct bt_component_class *bt_component_get_class(
+               struct bt_component *component);
+enum bt_component_class_type bt_component_get_class_type(
+               struct bt_component *component);
+
+/* Source component functions */
+struct bt_notification_iterator *bt_component_source_create_notification_iterator_with_init_method_data(
+        struct bt_component *component, void *init_method_data);
+
+/* Filter component functions */
+%{
+static struct bt_notification_iterator *bt_component_filter_get_input_iterator_private(
+               struct bt_component *filter, unsigned int index)
+{
+       struct bt_notification_iterator *iterator = NULL;
+
+       if (bt_component_filter_get_input_iterator(filter, index, &iterator)) {
+               iterator = NULL;
+               goto end;
+       }
+
+end:
+       return iterator;
+}
+%}
+
+struct bt_notification_iterator *bt_component_filter_create_notification_iterator_with_init_method_data(
+               struct bt_component *component, void *init_method_data);
+enum bt_component_status bt_component_filter_add_iterator(
+               struct bt_component *component,
+               struct bt_notification_iterator *iterator);
+enum bt_component_status bt_component_filter_set_minimum_input_count(
+               struct bt_component *filter, unsigned int minimum);
+enum bt_component_status bt_component_filter_set_maximum_input_count(
+               struct bt_component *filter, unsigned int maximum);
+enum bt_component_status bt_component_filter_get_input_count(
+               struct bt_component *filter, unsigned int *OUTPUT);
+struct bt_notification_iterator *bt_component_filter_get_input_iterator_private(
+               struct bt_component *filter, unsigned int index);
+
+/* Sink component functions */
+%{
+static struct bt_notification_iterator *bt_component_sink_get_input_iterator_private(
+               struct bt_component *sink, unsigned int index)
+{
+       struct bt_notification_iterator *iterator = NULL;
+
+       if (bt_component_sink_get_input_iterator(sink, index, &iterator)) {
+               iterator = NULL;
+               goto end;
+       }
+
+end:
+       return iterator;
+}
+%}
+
+enum bt_component_status bt_component_sink_add_iterator(
+               struct bt_component *component,
+               struct bt_notification_iterator *iterator);
+enum bt_component_status bt_component_sink_consume(
+               struct bt_component *component);
+enum bt_component_status bt_component_sink_set_minimum_input_count(
+               struct bt_component *sink, unsigned int minimum);
+enum bt_component_status bt_component_sink_set_maximum_input_count(
+               struct bt_component *sink, unsigned int maximum);
+enum bt_component_status bt_component_sink_get_input_count(
+               struct bt_component *sink, unsigned int *OUTPUT);
+struct bt_notification_iterator *bt_component_sink_get_input_iterator_private(
+               struct bt_component *sink, unsigned int index);
diff --git a/bindings/python/bt2/native_btcomponentclass.i b/bindings/python/bt2/native_btcomponentclass.i
new file mode 100644 (file)
index 0000000..a7df3a7
--- /dev/null
@@ -0,0 +1,1121 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/component/component-class.h>
+#include <babeltrace/component/component-class-source.h>
+#include <babeltrace/component/component-class-sink.h>
+#include <babeltrace/component/component-class-filter.h>
+#include <babeltrace/component/notification/iterator.h>
+#include <babeltrace/component/notification/notification.h>
+#include <assert.h>
+#include <glib.h>
+%}
+
+/* Types */
+struct bt_component_class;
+
+/* Status */
+enum bt_component_class_type {
+       BT_COMPONENT_CLASS_TYPE_UNKNOWN =       -1,
+       BT_COMPONENT_CLASS_TYPE_SOURCE =        0,
+       BT_COMPONENT_CLASS_TYPE_SINK =          1,
+       BT_COMPONENT_CLASS_TYPE_FILTER =        2,
+};
+
+/* General functions */
+const char *bt_component_class_get_name(
+               struct bt_component_class *component_class);
+const char *bt_component_class_get_description(
+               struct bt_component_class *component_class);
+enum bt_component_class_type bt_component_class_get_type(
+               struct bt_component_class *component_class);
+
+%{
+/*
+ * This hash table associates a BT component class object address to a
+ * user-defined Python class (PyObject *). The keys and values are NOT
+ * owned by this hash table. The Python class objects are owned by the
+ * Python module, which should not be unloaded until it is not possible
+ * to create a user Python component anyway.
+ *
+ * This hash table is written to when a user-defined Python component
+ * class is created by one of the bt_py3_component_class_*_create()
+ * functions.
+ *
+ * This function is read from when a user calls bt_component_create()
+ * with a component class pointer created by one of the functions above.
+ * In this case, the original Python class needs to be found to
+ * instantiate it and associate the created Python component object with
+ * a BT component object instance.
+ */
+
+static GHashTable *bt_cc_ptr_to_py_cls;
+
+static void register_cc_ptr_to_py_cls(struct bt_component_class *bt_cc,
+               PyObject *py_cls)
+{
+       g_hash_table_insert(bt_cc_ptr_to_py_cls, (gpointer) bt_cc,
+               (gpointer) py_cls);
+}
+
+static PyObject *lookup_cc_ptr_to_py_cls(struct bt_component_class *bt_cc)
+{
+       return (PyObject *) g_hash_table_lookup(bt_cc_ptr_to_py_cls,
+               (gconstpointer) bt_cc);
+}
+
+
+/*
+ * Useful Python objects.
+ */
+
+static PyObject *py_mod_bt2 = NULL;
+static PyObject *py_mod_bt2_exc_error_type = NULL;
+static PyObject *py_mod_bt2_exc_unsupported_feature_type = NULL;
+static PyObject *py_mod_bt2_exc_try_again_type = NULL;
+static PyObject *py_mod_bt2_exc_stop_type = NULL;
+static PyObject *py_mod_bt2_values = NULL;
+static PyObject *py_mod_bt2_values_create_from_ptr_func = NULL;
+
+static void bt_py3_cc_init_from_bt2(void)
+{
+       /*
+        * This is called once the bt2 package is loaded.
+        *
+        * Those modules and functions are needed while the package is
+        * used. Loading them here is safe because we know the bt2
+        * package is imported, and we know that the user cannot use the
+        * code here without importing bt2 first.
+        */
+       py_mod_bt2 = PyImport_ImportModule("bt2");
+       assert(py_mod_bt2);
+       py_mod_bt2_values = PyImport_ImportModule("bt2.values");
+       assert(py_mod_bt2_values);
+       py_mod_bt2_values_create_from_ptr_func =
+               PyObject_GetAttrString(py_mod_bt2_values,
+                       "_create_from_ptr");
+       assert(py_mod_bt2_values_create_from_ptr_func);
+       py_mod_bt2_exc_error_type =
+               PyObject_GetAttrString(py_mod_bt2, "Error");
+       assert(py_mod_bt2_exc_error_type);
+       py_mod_bt2_exc_unsupported_feature_type =
+               PyObject_GetAttrString(py_mod_bt2, "UnsupportedFeature");
+       py_mod_bt2_exc_try_again_type =
+               PyObject_GetAttrString(py_mod_bt2, "TryAgain");
+       py_mod_bt2_exc_stop_type =
+               PyObject_GetAttrString(py_mod_bt2, "Stop");
+       assert(py_mod_bt2_exc_stop_type);
+}
+
+static void bt_py3_cc_exit_handler(void)
+{
+       /*
+        * This is an exit handler (set by the bt2 package).
+        *
+        * We only give back the references that we took in
+        * bt_py3_cc_init_from_bt2() here. The global variables continue
+        * to exist for the code of this file, but they are now borrowed
+        * references. If this code is executed, it means that somehow
+        * to modules are still loaded, so it should be safe to use them
+        * even without a strong reference.
+        *
+        * We cannot do this in the library's destructor because it
+        * gets executed once Python is already finalized.
+        */
+       Py_XDECREF(py_mod_bt2_values_create_from_ptr_func);
+       Py_XDECREF(py_mod_bt2_values);
+       Py_XDECREF(py_mod_bt2);
+       Py_XDECREF(py_mod_bt2_exc_error_type);
+       Py_XDECREF(py_mod_bt2_exc_unsupported_feature_type);
+       Py_XDECREF(py_mod_bt2_exc_try_again_type);
+       Py_XDECREF(py_mod_bt2_exc_stop_type);
+}
+
+
+/* Library constructor and destructor */
+
+__attribute__((constructor))
+static void bt_py3_native_comp_class_ctor(void) {
+       /* Initialize component class association hash table */
+       bt_cc_ptr_to_py_cls = g_hash_table_new(g_direct_hash, g_direct_equal);
+       assert(bt_cc_ptr_to_py_cls);
+}
+
+__attribute__((destructor))
+static void bt_py3_native_comp_class_dtor(void) {
+       /* Destroy component class association hash table */
+       if (bt_cc_ptr_to_py_cls) {
+               g_hash_table_destroy(bt_cc_ptr_to_py_cls);
+       }
+}
+
+
+/* Converts a BT value object to a bt2.values object */
+
+static PyObject *bt_py3_bt_value_from_ptr(struct bt_value *value)
+{
+       PyObject *py_create_from_ptr_func = NULL;
+       PyObject *py_value = NULL;
+       PyObject *py_ptr = NULL;
+
+       py_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(value),
+               SWIGTYPE_p_bt_value, 0);
+       py_value = PyObject_CallFunctionObjArgs(
+               py_mod_bt2_values_create_from_ptr_func, py_ptr, NULL);
+       if (!py_value) {
+               goto end;
+       }
+
+       /*
+        * _create_from_ptr() only wraps an existing reference. `value`
+        * is a borrowed reference here, so increment its reference
+        * count for this new owner.
+        */
+       bt_get(value);
+
+end:
+       Py_XDECREF(py_ptr);
+       PyErr_Clear();
+       return py_value;
+}
+
+
+/* self.__init__(*args, **kwargs) */
+
+static int bt_py3_call_self_init(PyObject *py_self, PyObject *py_args,
+               PyObject *py_kwargs)
+{
+       int ret = 0;
+       size_t i;
+       PyObject *py_init_method_result = NULL;
+       PyObject *py_init_method = NULL;
+       PyObject *py_init_method_args = NULL;
+       PyObject *py_class = NULL;
+
+       py_class = PyObject_GetAttrString(py_self, "__class__");
+       if (!py_class) {
+               goto error;
+       }
+
+       py_init_method = PyObject_GetAttrString(py_class, "__init__");
+       if (!py_init_method) {
+               goto error;
+       }
+
+       /* Prepend py_self to the arguments (copy them to new tuple) */
+       py_init_method_args = PyTuple_New(PyTuple_Size(py_args) + 1);
+       if (!py_init_method_args) {
+               goto error;
+       }
+
+       Py_INCREF(py_self);
+       ret = PyTuple_SetItem(py_init_method_args, 0, py_self);
+       if (ret < 0) {
+               Py_DECREF(py_self);
+               goto error;
+       }
+
+       for (i = 0; i < PyTuple_Size(py_args); i++) {
+               PyObject *obj = PyTuple_GetItem(py_args, i);
+               if (!obj) {
+                       goto error;
+               }
+
+               Py_INCREF(obj);
+               ret = PyTuple_SetItem(py_init_method_args, i + 1, obj);
+               if (ret < 0) {
+                       Py_DECREF(obj);
+                       goto error;
+               }
+       }
+
+       py_init_method_result = PyObject_Call(py_init_method,
+               py_init_method_args, py_kwargs);
+       if (!py_init_method_result) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       Py_XDECREF(py_init_method_args);
+       Py_XDECREF(py_init_method_result);
+       Py_XDECREF(py_init_method);
+       Py_XDECREF(py_class);
+       return ret;
+}
+
+
+/* Component class proxy methods (delegate to the attached Python object) */
+
+static enum bt_component_status bt_py3_cc_init(
+               struct bt_component *component, struct bt_value *params,
+               void *init_method_data)
+{
+       enum bt_component_status status = BT_COMPONENT_STATUS_OK;
+       int ret;
+
+       /*
+        * This function is either called from
+        * _UserComponentType.__call__() (when init_method_data contains
+        * the PyObject * currently creating this component) or from
+        * other code (init_method_data is NULL).
+        */
+       if (init_method_data) {
+               /*
+                * Called from _UserComponentType.__call__().
+                *
+                * The `params` argument is completely ignored here,
+                * because if the user creating the component object
+                * from the component class object wants parameters,
+                * they'll be in the keyword arguments anyway.
+                */
+               PyObject *py_comp = init_method_data;
+               PyObject *py_comp_ptr = NULL;
+               PyObject *py_init_args = NULL;
+               PyObject *py_init_kwargs = NULL;
+
+               /*
+                * No need to take a Python reference on py_comp here:
+                * we store it as a _borrowed_ reference. When all the
+                * Python references are dropped, the object's __del__()
+                * method is called. This method calls this side
+                * (bt_py3_component_on_del()) to swap the ownership:
+                * this BT component becomes the only owner of the
+                * Python object.
+                */
+               bt_component_set_private_data(component, py_comp);
+
+               /*
+                * Set this BT component pointer as py_comp._ptr, and
+                * take a reference on it (on success, at the end).
+                *
+                * This reference is put in the Python component
+                * object's __del__() method.
+                */
+               py_comp_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(component),
+                       SWIGTYPE_p_bt_component, 0);
+               if (!py_comp_ptr) {
+                       PyErr_Clear();
+                       goto with_init_method_data_error;
+               }
+
+               ret = PyObject_SetAttrString(py_comp, "_ptr", py_comp_ptr);
+               if (ret < 0) {
+                       PyErr_Clear();
+                       goto with_init_method_data_error;
+               }
+
+               /*
+                * _UserComponentType.__call__() puts the arguments and
+                * keyword arguments to use for the py_comp.__init__()
+                * call in py_comp._init_args and py_comp._init_kwargs.
+                *
+                * We need to get them and remove them from py_comp before
+                * calling py_comp.__init__().
+                */
+               py_init_args = PyObject_GetAttrString(py_comp, "_init_args");
+               if (!py_init_args) {
+                       PyErr_Clear();
+                       goto with_init_method_data_error;
+               }
+
+               py_init_kwargs = PyObject_GetAttrString(py_comp, "_init_kwargs");
+               if (!py_init_kwargs) {
+                       PyErr_Clear();
+                       goto with_init_method_data_error;
+               }
+
+               ret = PyObject_DelAttrString(py_comp, "_init_args");
+               if (ret < 0) {
+                       PyErr_Clear();
+                       goto with_init_method_data_error;
+               }
+
+               ret = PyObject_DelAttrString(py_comp, "_init_kwargs");
+               if (ret < 0) {
+                       PyErr_Clear();
+                       goto with_init_method_data_error;
+               }
+
+               /* Ready to call py_comp.__init__() */
+               ret = bt_py3_call_self_init(py_comp, py_init_args,
+                       py_init_kwargs);
+               if (ret) {
+                       /*
+                        * We do not clear any raised exception here
+                        * so that the Python caller (the one who is
+                        * instantiating the class) gets the exception
+                        * from the __init__() method.
+                        */
+                       goto with_init_method_data_error;
+               }
+
+               /*
+                * py_comp.__init__() went well: get the native BT
+                * component object reference for the Python object.
+                */
+               bt_get(component);
+               goto with_init_method_data_end;
+
+with_init_method_data_error:
+               status = BT_COMPONENT_STATUS_ERROR;
+
+               /*
+                * Reset py_comp._ptr to None to signal the error to
+                * _UserComponentType.__call__(). For exceptions raised
+                * from py_comp.__init__(), this is not important,
+                * because __call__() will also raise before even
+                * checking py_comp._ptr.
+                */
+               if (PyObject_HasAttrString(py_comp, "_ptr")) {
+                       PyObject *py_err_type, *py_err_value, *py_err_traceback;
+                       PyErr_Fetch(&py_err_type, &py_err_value,
+                               &py_err_traceback);
+                       Py_INCREF(Py_None);
+                       PyObject_SetAttrString(py_comp, "_ptr", Py_None);
+                       PyErr_Restore(py_err_type, py_err_value,
+                               py_err_traceback);
+               }
+
+               /*
+                * Important: remove py_comp from our the BT component's
+                * private data. Since we're failing,
+                * bt_py3_cc_destroy() is about to be called: it must
+                * not consider py_comp. py_comp's __del__() method
+                * will be called (because the instance exists), but
+                * since py_comp._ptr is None, it won't do anything.
+                */
+               bt_component_set_private_data(component, NULL);
+
+with_init_method_data_end:
+               Py_XDECREF(py_comp_ptr);
+               Py_XDECREF(py_init_args);
+               Py_XDECREF(py_init_kwargs);
+       } else {
+               /*
+                * Not called from _UserComponentType.__call__().
+                *
+                * In this case we need to instantiate the user-defined
+                * Python class. To do this we call the user-defined
+                * class manually with this already-created BT component
+                * object pointer as the __comp_ptr keyword argument.
+                *
+                * We keep a reference, the only existing one, to the
+                * created Python object. bt_py3_component_on_del() will
+                * never be called by the object's __del__() method
+                * because _UserComponentType.__call__() marks the
+                * object as owned by the native BT component object.
+                */
+               PyObject *py_cls = NULL;
+               PyObject *py_comp = NULL;
+               PyObject *py_params = NULL;
+               PyObject *py_comp_ptr = NULL;
+               PyObject *py_cls_ctor_args = NULL;
+               PyObject *py_cls_ctor_kwargs = NULL;
+
+               /*
+                * Get the user-defined Python class which created this
+                * component's class in the first place (borrowed
+                * reference).
+                */
+               py_cls = lookup_cc_ptr_to_py_cls(bt_component_get_class(component));
+               if (!py_cls) {
+                       goto without_init_method_data_error;
+               }
+
+               /* BT value object -> Python bt2.values object */
+               py_params = bt_py3_bt_value_from_ptr(params);
+               if (!py_params) {
+                       goto without_init_method_data_error;
+               }
+
+               /* Component pointer -> SWIG pointer Python object */
+               py_comp_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(component),
+                       SWIGTYPE_p_bt_component, 0);
+               if (!py_comp_ptr) {
+                       goto without_init_method_data_error;
+               }
+
+               /*
+                * Do the equivalent of this:
+                *
+                *     py_comp = py_cls(__comp_ptr=py_comp_ptr, params=py_params)
+                *
+                * _UserComponentType.__call__() calls the Python
+                * component object's __init__() function itself in this
+                * case.
+                */
+               py_cls_ctor_args = PyTuple_New(0);
+               if (!py_cls_ctor_args) {
+                       goto without_init_method_data_error;
+               }
+
+               py_cls_ctor_kwargs = PyDict_New();
+               if (!py_cls_ctor_kwargs) {
+                       goto without_init_method_data_error;
+               }
+
+               ret = PyDict_SetItemString(py_cls_ctor_kwargs, "__comp_ptr",
+                       py_comp_ptr);
+               if (ret < 0) {
+                       goto without_init_method_data_error;
+               }
+
+               ret = PyDict_SetItemString(py_cls_ctor_kwargs, "params",
+                       py_params);
+               if (ret < 0) {
+                       goto without_init_method_data_error;
+               }
+
+               py_comp = PyObject_Call(py_cls, py_cls_ctor_args,
+                       py_cls_ctor_kwargs);
+               if (!py_comp) {
+                       goto without_init_method_data_error;
+               }
+
+               /*
+                * Our user Python component object is now fully created
+                * and initialized by the user. Since we just created
+                * it, this BT component is its only (persistent) owner.
+                */
+               bt_component_set_private_data(component, py_comp);
+               py_comp = NULL;
+               goto without_init_method_data_end;
+
+without_init_method_data_error:
+               status = BT_COMPONENT_STATUS_ERROR;
+
+               /*
+                * Clear any exception: we're returning a bad status
+                * anyway. If this call originated from Python (creation
+                * from a plugin's component class, for example), then
+                * the user gets an appropriate creation error.
+                */
+               PyErr_Clear();
+
+without_init_method_data_end:
+               Py_XDECREF(py_comp);
+               Py_XDECREF(py_params);
+               Py_XDECREF(py_comp_ptr);
+               Py_XDECREF(py_cls_ctor_args);
+               Py_XDECREF(py_cls_ctor_kwargs);
+       }
+
+       return status;
+}
+
+static void bt_py3_cc_destroy(struct bt_component *component)
+{
+       PyObject *py_comp = bt_component_get_private_data(component);
+       PyObject *py_destroy_method_result = NULL;
+
+       if (py_comp) {
+               /*
+                * If we're here, this BT component is the only owner of
+                * its private user Python object. It is safe to
+                * decrement its reference count, and thus destroy it.
+                *
+                * We call its _destroy() method before doing so to notify
+                * the Python user.
+                */
+               py_destroy_method_result = PyObject_CallMethod(py_comp,
+                       "_destroy", NULL);
+
+               /*
+                * Ignore any exception raised by the _destroy() method
+                * because it won't change anything at this point: the
+                * component is being destroyed anyway.
+                */
+               PyErr_Clear();
+               Py_XDECREF(py_destroy_method_result);
+               Py_DECREF(py_comp);
+       }
+}
+
+static enum bt_notification_iterator_status bt_py3_exc_to_notif_iter_status(void)
+{
+       enum bt_notification_iterator_status status =
+               BT_NOTIFICATION_ITERATOR_STATUS_OK;
+       PyObject *exc = PyErr_Occurred();
+
+       if (!exc) {
+               goto end;
+       }
+
+       if (PyErr_GivenExceptionMatches(exc,
+                       py_mod_bt2_exc_unsupported_feature_type)) {
+               status = BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED;
+       } else if (PyErr_GivenExceptionMatches(exc,
+                       py_mod_bt2_exc_stop_type)) {
+               status = BT_NOTIFICATION_ITERATOR_STATUS_END;
+       } else {
+               status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
+       }
+
+end:
+       PyErr_Clear();
+       return status;
+}
+
+static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_init(
+               struct bt_component *component,
+               struct bt_notification_iterator *iter, void *init_method_data)
+{
+       enum bt_notification_iterator_status status =
+               BT_NOTIFICATION_ITERATOR_STATUS_OK;
+       PyObject *py_comp_cls = NULL;
+       PyObject *py_iter_cls = NULL;
+       PyObject *py_iter_ptr = NULL;
+       PyObject *py_init_method_result = NULL;
+       PyObject *py_iter = NULL;
+       PyObject *py_comp = bt_component_get_private_data(component);
+
+       /* Find user's Python notification iterator class */
+       py_comp_cls = PyObject_GetAttrString(py_comp, "__class__");
+       if (!py_comp_cls) {
+               goto error;
+       }
+
+       py_iter_cls = PyObject_GetAttrString(py_comp_cls, "_iter_cls");
+       if (!py_iter_cls) {
+               goto error;
+       }
+
+       /*
+        * Create object with borrowed BT notification iterator
+        * reference:
+        *
+        *     py_iter = py_iter_cls.__new__(py_iter_cls, py_iter_ptr)
+        */
+       py_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(iter),
+               SWIGTYPE_p_bt_notification_iterator, 0);
+       if (!py_iter_ptr) {
+               goto error;
+       }
+
+       py_iter = PyObject_CallMethod(py_iter_cls, "__new__",
+               "(OO)", py_iter_cls, py_iter_ptr);
+       if (!py_iter) {
+               goto error;
+       }
+
+       /*
+        * Initialize object:
+        *
+        *     py_iter.__init__()
+        *
+        * At this point, py_iter._ptr is set, so this initialization
+        * function has access to the py_iter.component property (which
+        * gives it the user Python component object from which the
+        * iterator was created).
+        */
+       py_init_method_result = PyObject_CallMethod(py_iter, "__init__", NULL);
+       if (!py_init_method_result) {
+               goto error;
+       }
+
+       /*
+        * Since the Python code can never instantiate a user-defined
+        * notification iterator class, the BT notification iterator
+        * object does NOT belong to a user Python notification iterator
+        * object (borrowed reference). However this Python object is
+        * owned by this BT notification iterator object.
+        *
+        * In the Python world, the lifetime of the BT notification
+        * iterator is managed by a _GenericNotificationIterator
+        * instance:
+        *
+        *     _GenericNotificationIterator instance:
+        *         owns a native bt_notification_iterator object (iter)
+        *             owns a UserNotificationIterator instance (py_iter)
+        *                 self._ptr is a borrowed reference to the
+        *                 native bt_notification_iterator object (iter)
+        */
+       bt_notification_iterator_set_private_data(iter, py_iter);
+       py_iter = NULL;
+       goto end;
+
+error:
+       status = bt_py3_exc_to_notif_iter_status();
+       if (status == BT_NOTIFICATION_ITERATOR_STATUS_OK) {
+               /*
+                * Looks like there wasn't any exception from the Python
+                * side, but we're still in an error state here.
+                */
+               status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
+       }
+
+       /*
+        * Clear any exception: we're returning a bad status anyway. If
+        * this call originated from Python, then the user gets an
+        * appropriate creation error.
+        */
+       PyErr_Clear();
+
+end:
+       Py_XDECREF(py_comp_cls);
+       Py_XDECREF(py_iter_cls);
+       Py_XDECREF(py_iter_ptr);
+       Py_XDECREF(py_init_method_result);
+       Py_XDECREF(py_iter);
+       return status;
+}
+
+static void bt_py3_cc_notification_iterator_destroy(
+               struct bt_notification_iterator *iter)
+{
+       PyObject *py_notif_iter =
+               bt_notification_iterator_get_private_data(iter);
+       PyObject *py_destroy_method_result = NULL;
+
+       assert(py_notif_iter);
+       py_destroy_method_result = PyObject_CallMethod(py_notif_iter,
+               "_destroy", NULL);
+
+       /*
+        * Ignore any exception raised by the _destroy() method because
+        * it won't change anything at this point. The notification
+        * iterator is being destroyed anyway.
+        */
+       PyErr_Clear();
+       Py_XDECREF(py_destroy_method_result);
+       Py_DECREF(py_notif_iter);
+}
+
+static struct bt_notification *bt_py3_cc_notification_iterator_get(
+               struct bt_notification_iterator *iter)
+{
+       PyObject *py_notif_iter =
+               bt_notification_iterator_get_private_data(iter);
+       PyObject *py_get_method_result = NULL;
+       struct bt_notification *notif = NULL;
+
+       assert(py_notif_iter);
+       py_get_method_result = PyObject_CallMethod(py_notif_iter,
+               "_get_from_bt", NULL);
+       if (!py_get_method_result) {
+               goto error;
+       }
+
+       /*
+        * The returned object, on success, is an integer object
+        * (PyLong) containing the address of a BT notification
+        * object (which is not ours).
+        */
+       notif = (struct bt_notification *) PyLong_AsUnsignedLongLong(
+               py_get_method_result);
+
+       /* Clear potential overflow error; should never happen */
+       if (PyErr_Occurred()) {
+               goto error;
+       }
+
+       if (!notif) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_PUT(notif);
+
+       /*
+        * Clear any exception: we're returning NULL anyway. If this
+        * call originated from Python, then the user gets an
+        * appropriate error.
+        */
+       PyErr_Clear();
+
+end:
+       Py_XDECREF(py_get_method_result);
+       return notif;
+}
+
+static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_next(
+               struct bt_notification_iterator *iter)
+{
+       enum bt_notification_iterator_status status;
+       PyObject *py_notif_iter =
+               bt_notification_iterator_get_private_data(iter);
+       PyObject *py_next_method_result = NULL;
+
+       assert(py_notif_iter);
+       py_next_method_result = PyObject_CallMethod(py_notif_iter,
+               "_next", NULL);
+       status = bt_py3_exc_to_notif_iter_status();
+       if (!py_next_method_result
+                       && status == BT_NOTIFICATION_ITERATOR_STATUS_OK) {
+               /* Pretty sure this should never happen, but just in case */
+               status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
+       }
+
+       Py_XDECREF(py_next_method_result);
+       return status;
+}
+
+static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_seek_time(
+               struct bt_notification_iterator *iter, int64_t time)
+{
+       enum bt_notification_iterator_status status;
+       PyObject *py_notif_iter =
+               bt_notification_iterator_get_private_data(iter);
+       PyObject *py_seek_to_time_method_result = NULL;
+
+       assert(py_notif_iter);
+       py_seek_to_time_method_result = PyObject_CallMethod(py_notif_iter,
+               "_seek_to_time", "(L)", time);
+       status = bt_py3_exc_to_notif_iter_status();
+       if (!py_seek_to_time_method_result
+                       && status == BT_NOTIFICATION_ITERATOR_STATUS_OK) {
+               /* Pretty sure this should never happen, but just in case */
+               status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
+       }
+
+       Py_XDECREF(py_seek_to_time_method_result);
+       return status;
+}
+
+static enum bt_component_status bt_py3_exc_to_component_status(void)
+{
+       enum bt_component_status status = BT_COMPONENT_STATUS_OK;
+       PyObject *exc = PyErr_Occurred();
+
+       if (!exc) {
+               goto end;
+       }
+
+       if (PyErr_GivenExceptionMatches(exc,
+                       py_mod_bt2_exc_unsupported_feature_type)) {
+               status = BT_COMPONENT_STATUS_UNSUPPORTED;
+       } else if (PyErr_GivenExceptionMatches(exc,
+                       py_mod_bt2_exc_try_again_type)) {
+               status = BT_COMPONENT_STATUS_AGAIN;
+       } else if (PyErr_GivenExceptionMatches(exc,
+                       py_mod_bt2_exc_stop_type)) {
+               status = BT_COMPONENT_STATUS_END;
+       } else {
+               status = BT_COMPONENT_STATUS_ERROR;
+       }
+
+end:
+       PyErr_Clear();
+       return status;
+}
+
+static enum bt_component_status bt_py3_cc_sink_consume(
+               struct bt_component *component)
+{
+       PyObject *py_comp = bt_component_get_private_data(component);
+       PyObject *py_consume_method_result = NULL;
+       enum bt_component_status status;
+
+       py_consume_method_result = PyObject_CallMethod(py_comp,
+               "_consume", NULL);
+       status = bt_py3_exc_to_component_status();
+       if (!py_consume_method_result && status == BT_COMPONENT_STATUS_OK) {
+               /* Pretty sure this should never happen, but just in case */
+               status = BT_COMPONENT_STATUS_ERROR;
+       }
+
+       Py_XDECREF(py_consume_method_result);
+       return status;
+}
+
+static enum bt_component_status bt_py3_cc_sink_filter_add_iterator(
+               struct bt_component *component,
+               struct bt_notification_iterator *iter)
+{
+       PyObject *py_comp = bt_component_get_private_data(component);
+       PyObject *py_add_iterator_method_result = NULL;
+       PyObject *py_notif_iter_ptr = NULL;
+       enum bt_component_status status;
+
+       py_notif_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(iter),
+               SWIGTYPE_p_bt_notification_iterator, 0);
+       if (!py_notif_iter_ptr) {
+               PyErr_Clear();
+               status = BT_COMPONENT_STATUS_ERROR;
+               goto end;
+       }
+
+       bt_get(iter);
+       py_add_iterator_method_result = PyObject_CallMethod(py_comp,
+               "_add_iterator_from_bt", "(O)", py_notif_iter_ptr);
+       status = bt_py3_exc_to_component_status();
+       if (!py_add_iterator_method_result
+                       && status == BT_COMPONENT_STATUS_OK) {
+               /* Pretty sure this should never happen, but just in case */
+               status = BT_COMPONENT_STATUS_ERROR;
+       }
+
+end:
+       Py_XDECREF(py_notif_iter_ptr);
+       Py_XDECREF(py_add_iterator_method_result);
+       return status;
+}
+
+
+/* Component class creation functions (called from Python module) */
+
+static int bt_py3_cc_set_optional_attrs_methods(struct bt_component_class *cc,
+               const char *description)
+{
+       int ret = 0;
+
+       if (description) {
+               ret = bt_component_class_set_description(cc, description);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       ret = bt_component_class_set_init_method(cc, bt_py3_cc_init);
+       if (ret) {
+               goto end;
+       }
+
+       ret = bt_component_class_set_destroy_method(cc, bt_py3_cc_destroy);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static int bt_py3_cc_set_optional_iter_methods(struct bt_component_class *cc,
+               bool has_seek_time,
+               int (*set_notif_iter_init_method)(struct bt_component_class *, bt_component_class_notification_iterator_init_method),
+               int (*set_notif_iter_destroy_method)(struct bt_component_class *, bt_component_class_notification_iterator_destroy_method),
+               int (*set_notif_iter_seek_time_method)(struct bt_component_class *, bt_component_class_notification_iterator_seek_time_method))
+{
+       int ret;
+
+       ret = set_notif_iter_init_method(
+               cc, bt_py3_cc_notification_iterator_init);
+       if (ret) {
+               goto end;
+       }
+
+       ret = set_notif_iter_destroy_method(
+               cc, bt_py3_cc_notification_iterator_destroy);
+       if (ret) {
+               goto end;
+       }
+
+       if (has_seek_time) {
+               ret = set_notif_iter_seek_time_method(
+                       cc, bt_py3_cc_notification_iterator_seek_time);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static struct bt_component_class *bt_py3_component_class_source_create(
+               PyObject *py_cls, const char *name, const char *description,
+               bool has_seek_time)
+{
+       struct bt_component_class *cc;
+       int ret;
+
+       assert(py_cls);
+       cc = bt_component_class_source_create(name,
+               bt_py3_cc_notification_iterator_get,
+               bt_py3_cc_notification_iterator_next);
+       if (!cc) {
+               goto end;
+       }
+
+       ret = bt_py3_cc_set_optional_attrs_methods(cc, description);
+       if (ret) {
+               BT_PUT(cc);
+               goto end;
+       }
+
+       ret = bt_py3_cc_set_optional_iter_methods(cc, has_seek_time,
+               bt_component_class_source_set_notification_iterator_init_method,
+               bt_component_class_source_set_notification_iterator_destroy_method,
+               bt_component_class_source_set_notification_iterator_seek_time_method);
+       if (ret) {
+               BT_PUT(cc);
+               goto end;
+       }
+
+       register_cc_ptr_to_py_cls(cc, py_cls);
+       bt_component_class_freeze(cc);
+
+end:
+       return cc;
+}
+
+static struct bt_component_class *bt_py3_component_class_filter_create(
+               PyObject *py_cls, const char *name, const char *description,
+               bool has_seek_time)
+{
+       struct bt_component_class *cc;
+       int ret;
+
+       assert(py_cls);
+       cc = bt_component_class_filter_create(name,
+               bt_py3_cc_notification_iterator_get,
+               bt_py3_cc_notification_iterator_next);
+       if (!cc) {
+               goto end;
+       }
+
+       ret = bt_py3_cc_set_optional_attrs_methods(cc, description);
+       if (ret) {
+               BT_PUT(cc);
+               goto end;
+       }
+
+       ret = bt_py3_cc_set_optional_iter_methods(cc, has_seek_time,
+               bt_component_class_filter_set_notification_iterator_init_method,
+               bt_component_class_filter_set_notification_iterator_destroy_method,
+               bt_component_class_filter_set_notification_iterator_seek_time_method);
+       if (ret) {
+               BT_PUT(cc);
+               goto end;
+       }
+
+       ret = bt_component_class_filter_set_add_iterator_method(cc,
+               bt_py3_cc_sink_filter_add_iterator);
+       if (ret) {
+               BT_PUT(cc);
+               goto end;
+       }
+
+       register_cc_ptr_to_py_cls(cc, py_cls);
+       bt_component_class_freeze(cc);
+
+end:
+       return cc;
+}
+
+static struct bt_component_class *bt_py3_component_class_sink_create(
+               PyObject *py_cls, const char *name, const char *description)
+{
+       struct bt_component_class *cc;
+       int ret;
+
+       assert(py_cls);
+       cc = bt_component_class_sink_create(name, bt_py3_cc_sink_consume);
+       if (!cc) {
+               goto end;
+       }
+
+       ret = bt_py3_cc_set_optional_attrs_methods(cc, description);
+       if (ret) {
+               BT_PUT(cc);
+               goto end;
+       }
+
+       ret = bt_component_class_sink_set_add_iterator_method(cc,
+               bt_py3_cc_sink_filter_add_iterator);
+       if (ret) {
+               BT_PUT(cc);
+               goto end;
+       }
+
+       register_cc_ptr_to_py_cls(cc, py_cls);
+       bt_component_class_freeze(cc);
+
+end:
+       return cc;
+}
+
+
+/* Component creation function (called from Python module) */
+
+static void bt_py3_component_create(
+               struct bt_component_class *comp_class, PyObject *py_self,
+               const char *name)
+{
+       struct bt_component *component =
+               bt_component_create_with_init_method_data(comp_class,
+                       name, NULL, py_self);
+
+       /*
+        * Our component initialization function, bt_py3_cc_init(), sets
+        * a new reference to the created component in the user's Python
+        * object (py_self._ptr). This is where the reference is kept.
+        * We don't need the returned component in this case (if any,
+        * because it might be NULL if the creation failed, most
+        * probably because py_self.__init__() raised something).
+        */
+       bt_put(component);
+}
+
+
+/* Component delete notification */
+
+static void bt_py3_component_on_del(PyObject *py_comp)
+{
+       /*
+        * The Python component's __del__() function calls this to
+        * indicate that there's no more reference on it from the
+        * Python world.
+        *
+        * Since the BT component can continue to live once the Python
+        * component object is deleted, we increment the Python
+        * component's reference count so that it now only belong to the
+        * BT component. We will decrement this reference count once
+        * the BT component is destroyed in bt_py3_cc_destroy().
+        */
+       assert(py_comp);
+       Py_INCREF(py_comp);
+}
+%}
+
+%exception bt_py3_component_create {
+       $action
+       if (PyErr_Occurred()) {
+               /* Exception is already set by bt_py3_component_create() */
+               SWIG_fail;
+       }
+}
+
+struct bt_component_class *bt_py3_component_class_source_create(
+               PyObject *py_cls, const char *name, const char *description,
+               bool has_seek_time);
+struct bt_component_class *bt_py3_component_class_filter_create(
+               PyObject *py_cls, const char *name, const char *description,
+               bool has_seek_time);
+struct bt_component_class *bt_py3_component_class_sink_create(
+               PyObject *py_cls, const char *name, const char *description);
+void bt_py3_component_create(
+               struct bt_component_class *comp_class, PyObject *py_self,
+               const char *name);
+void bt_py3_component_on_del(PyObject *py_comp);
+void bt_py3_cc_init_from_bt2(void);
+void bt_py3_cc_exit_handler(void);
diff --git a/bindings/python/bt2/native_btctfwriter.i b/bindings/python/bt2/native_btctfwriter.i
new file mode 100644 (file)
index 0000000..479e1e3
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-writer/clock.h>
+#include <babeltrace/ctf-writer/stream.h>
+#include <babeltrace/ctf-writer/stream-class.h>
+#include <babeltrace/ctf-writer/writer.h>
+%}
+
+/* Types */
+struct bt_ctf_clock;
+struct bt_ctf_writer;
+
+/* Clock functions */
+struct bt_ctf_clock *bt_ctf_clock_create(const char *name);
+const char *bt_ctf_clock_get_name(struct bt_ctf_clock *clock);
+const char *bt_ctf_clock_get_description(struct bt_ctf_clock *clock);
+int bt_ctf_clock_set_description(struct bt_ctf_clock *clock,
+               const char *desc);
+uint64_t bt_ctf_clock_get_frequency(struct bt_ctf_clock *clock);
+int bt_ctf_clock_set_frequency(struct bt_ctf_clock *clock,
+               uint64_t freq);
+uint64_t bt_ctf_clock_get_precision(struct bt_ctf_clock *clock);
+int bt_ctf_clock_set_precision(struct bt_ctf_clock *clock,
+               uint64_t precision);
+int bt_ctf_clock_get_offset_s(struct bt_ctf_clock *clock,
+               int64_t *OUTPUT);
+int bt_ctf_clock_set_offset_s(struct bt_ctf_clock *clock,
+               int64_t offset_s);
+int bt_ctf_clock_get_offset(struct bt_ctf_clock *clock,
+               int64_t *OUTPUT);
+int bt_ctf_clock_set_offset(struct bt_ctf_clock *clock,
+               int64_t offset);
+int bt_ctf_clock_get_is_absolute(struct bt_ctf_clock *clock);
+int bt_ctf_clock_set_is_absolute(struct bt_ctf_clock *clock,
+               int is_absolute);
+BTUUID bt_ctf_clock_get_uuid(struct bt_ctf_clock *clock);
+int bt_ctf_clock_set_uuid(struct bt_ctf_clock *clock,
+               BTUUID uuid);
+int bt_ctf_clock_set_time(struct bt_ctf_clock *clock,
+               int64_t time);
+
+/* Stream functions */
+int bt_ctf_stream_get_discarded_events_count(
+               struct bt_ctf_stream *stream, uint64_t *OUTPUT);
+void bt_ctf_stream_append_discarded_events(struct bt_ctf_stream *stream,
+               uint64_t event_count);
+int bt_ctf_stream_append_event(struct bt_ctf_stream *stream,
+               struct bt_ctf_event *event);
+struct bt_ctf_field *bt_ctf_stream_get_packet_header(
+               struct bt_ctf_stream *stream);
+int bt_ctf_stream_set_packet_header(
+               struct bt_ctf_stream *stream,
+               struct bt_ctf_field *packet_header);
+struct bt_ctf_field *bt_ctf_stream_get_packet_context(
+               struct bt_ctf_stream *stream);
+int bt_ctf_stream_set_packet_context(
+               struct bt_ctf_stream *stream,
+               struct bt_ctf_field *packet_context);
+int bt_ctf_stream_flush(struct bt_ctf_stream *stream);
+int bt_ctf_stream_is_writer(struct bt_ctf_stream *stream);
+
+/* Stream class functions */
+int bt_ctf_stream_class_set_clock(
+               struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_clock *clock);
+struct bt_ctf_clock *bt_ctf_stream_class_get_clock(
+               struct bt_ctf_stream_class *stream_class);
+
+/* Writer functions */
+struct bt_ctf_writer *bt_ctf_writer_create(const char *path);
+struct bt_ctf_trace *bt_ctf_writer_get_trace(
+               struct bt_ctf_writer *writer);
+int bt_ctf_writer_add_clock(struct bt_ctf_writer *writer,
+               struct bt_ctf_clock *clock);
+char *bt_ctf_writer_get_metadata_string(struct bt_ctf_writer *writer);
+void bt_ctf_writer_flush_metadata(struct bt_ctf_writer *writer);
diff --git a/bindings/python/bt2/native_btevent.i b/bindings/python/bt2/native_btevent.i
new file mode 100644 (file)
index 0000000..3d73b2f
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/event.h>
+%}
+
+/* Type */
+struct bt_ctf_event;
+
+/* Functions */
+struct bt_ctf_event *bt_ctf_event_create(
+               struct bt_ctf_event_class *event_class);
+struct bt_ctf_event_class *bt_ctf_event_get_class(
+               struct bt_ctf_event *event);
+struct bt_ctf_packet *bt_ctf_event_get_packet(
+               struct bt_ctf_event *event);
+int bt_ctf_event_set_packet(struct bt_ctf_event *event,
+               struct bt_ctf_packet *packet);
+struct bt_ctf_stream *bt_ctf_event_get_stream(
+               struct bt_ctf_event *event);
+struct bt_ctf_field *bt_ctf_event_get_header(
+               struct bt_ctf_event *event);
+int bt_ctf_event_set_header(struct bt_ctf_event *event,
+               struct bt_ctf_field *header);
+struct bt_ctf_field *bt_ctf_event_get_stream_event_context(
+               struct bt_ctf_event *event);
+int bt_ctf_event_set_stream_event_context(struct bt_ctf_event *event,
+               struct bt_ctf_field *context);
+struct bt_ctf_field *bt_ctf_event_get_event_context(
+               struct bt_ctf_event *event);
+int bt_ctf_event_set_event_context(struct bt_ctf_event *event,
+               struct bt_ctf_field *context);
+struct bt_ctf_field *bt_ctf_event_get_payload_field(
+               struct bt_ctf_event *event);
+int bt_ctf_event_set_payload_field(struct bt_ctf_event *event,
+               struct bt_ctf_field *payload);
+struct bt_ctf_clock_value *bt_ctf_event_get_clock_value(
+               struct bt_ctf_event *event,
+               struct bt_ctf_clock_class *clock_class);
+int bt_ctf_event_set_clock_value(
+               struct bt_ctf_event *event,
+               struct bt_ctf_clock_value *clock_value);
diff --git a/bindings/python/bt2/native_bteventclass.i b/bindings/python/bt2/native_bteventclass.i
new file mode 100644 (file)
index 0000000..66a5983
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/event-class.h>
+%}
+
+/* Type */
+struct bt_ctf_event_class;
+
+/* Functions */
+struct bt_ctf_event_class *bt_ctf_event_class_create(const char *name);
+struct bt_ctf_stream_class *bt_ctf_event_class_get_stream_class(
+               struct bt_ctf_event_class *event_class);
+const char *bt_ctf_event_class_get_name(
+               struct bt_ctf_event_class *event_class);
+int64_t bt_ctf_event_class_get_id(
+               struct bt_ctf_event_class *event_class);
+int bt_ctf_event_class_set_id(
+               struct bt_ctf_event_class *event_class, uint32_t id);
+int bt_ctf_event_class_get_attribute_count(
+               struct bt_ctf_event_class *event_class);
+const char *bt_ctf_event_class_get_attribute_name(
+               struct bt_ctf_event_class *event_class, int index);
+struct bt_value *
+bt_ctf_event_class_get_attribute_value_by_name(
+               struct bt_ctf_event_class *event_class, const char *name);
+int bt_ctf_event_class_set_attribute(
+               struct bt_ctf_event_class *event_class, const char *name,
+               struct bt_value *value);
+struct bt_ctf_field_type *bt_ctf_event_class_get_context_type(
+               struct bt_ctf_event_class *event_class);
+int bt_ctf_event_class_set_context_type(
+               struct bt_ctf_event_class *event_class,
+               struct bt_ctf_field_type *context_type);
+struct bt_ctf_field_type *bt_ctf_event_class_get_payload_type(
+               struct bt_ctf_event_class *event_class);
+int bt_ctf_event_class_set_payload_type(
+               struct bt_ctf_event_class *event_class,
+               struct bt_ctf_field_type *payload_type);
diff --git a/bindings/python/bt2/native_btfields.i b/bindings/python/bt2/native_btfields.i
new file mode 100644 (file)
index 0000000..e3753fd
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/fields.h>
+%}
+
+/* Type */
+struct bt_ctf_field;
+
+/* Common functions */
+struct bt_ctf_field *bt_ctf_field_create(
+               struct bt_ctf_field_type *type);
+struct bt_ctf_field_type *bt_ctf_field_get_type(
+               struct bt_ctf_field *field);
+struct bt_ctf_field *bt_ctf_field_copy(struct bt_ctf_field *field);
+
+/* Integer field functions */
+int bt_ctf_field_signed_integer_get_value(struct bt_ctf_field *integer,
+               int64_t *OUTPUT);
+int bt_ctf_field_signed_integer_set_value(struct bt_ctf_field *integer,
+               int64_t value);
+int bt_ctf_field_unsigned_integer_get_value(struct bt_ctf_field *integer,
+               uint64_t *OUTPUT);
+int bt_ctf_field_unsigned_integer_set_value(struct bt_ctf_field *integer,
+               uint64_t value);
+
+/* Floating point number field functions */
+int bt_ctf_field_floating_point_get_value(
+               struct bt_ctf_field *floating_point, double *OUTPUT);
+int bt_ctf_field_floating_point_set_value(
+               struct bt_ctf_field *floating_point,
+               double value);
+
+/* Enumeration field functions */
+struct bt_ctf_field *bt_ctf_field_enumeration_get_container(
+               struct bt_ctf_field *enumeration);
+struct bt_ctf_field_type_enumeration_mapping_iterator *
+bt_ctf_field_enumeration_get_mappings(struct bt_ctf_field *enum_field);
+
+/* String field functions */
+const char *bt_ctf_field_string_get_value(
+               struct bt_ctf_field *string_field);
+int bt_ctf_field_string_set_value(struct bt_ctf_field *string_field,
+               const char *value);
+int bt_ctf_field_string_append(struct bt_ctf_field *string_field,
+               const char *value);
+int bt_ctf_field_string_append_len(
+               struct bt_ctf_field *string_field, const char *value,
+               unsigned int length);
+
+/* Structure field functions */
+struct bt_ctf_field *bt_ctf_field_structure_get_field_by_index(
+               struct bt_ctf_field *structure, int index);
+struct bt_ctf_field *bt_ctf_field_structure_get_field(
+               struct bt_ctf_field *structure, const char *name);
+
+/* Array field functions */
+struct bt_ctf_field *bt_ctf_field_array_get_field(
+               struct bt_ctf_field *array, uint64_t index);
+
+/* Sequence field functions */
+struct bt_ctf_field *bt_ctf_field_sequence_get_length(
+               struct bt_ctf_field *sequence);
+int bt_ctf_field_sequence_set_length(struct bt_ctf_field *sequence,
+               struct bt_ctf_field *length_field);
+struct bt_ctf_field *bt_ctf_field_sequence_get_field(
+               struct bt_ctf_field *sequence, uint64_t index);
+
+/* Variant field functions */
+struct bt_ctf_field *bt_ctf_field_variant_get_field(
+               struct bt_ctf_field *variant, struct bt_ctf_field *tag);
+struct bt_ctf_field *bt_ctf_field_variant_get_current_field(
+               struct bt_ctf_field *variant);
+struct bt_ctf_field *bt_ctf_field_variant_get_tag(
+               struct bt_ctf_field *variant);
diff --git a/bindings/python/bt2/native_btft.i b/bindings/python/bt2/native_btft.i
new file mode 100644 (file)
index 0000000..1636e83
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/field-types.h>
+%}
+
+/* Type */
+struct bt_ctf_field_type;
+
+/* Common enumerations */
+enum bt_ctf_scope {
+       BT_CTF_SCOPE_UNKNOWN = -1,
+       BT_CTF_SCOPE_TRACE_PACKET_HEADER = 1,
+       BT_CTF_SCOPE_STREAM_PACKET_CONTEXT = 2,
+       BT_CTF_SCOPE_STREAM_EVENT_HEADER = 3,
+       BT_CTF_SCOPE_STREAM_EVENT_CONTEXT = 4,
+       BT_CTF_SCOPE_EVENT_CONTEXT = 5,
+       BT_CTF_SCOPE_EVENT_PAYLOAD = 6,
+       BT_CTF_SCOPE_ENV = 0,
+       BT_CTF_SCOPE_EVENT_FIELDS = 6,
+};
+
+enum bt_ctf_type_id {
+       BT_CTF_TYPE_ID_UNKNOWN = CTF_TYPE_UNKNOWN,
+       BT_CTF_TYPE_ID_INTEGER = CTF_TYPE_INTEGER,
+       BT_CTF_TYPE_ID_FLOAT = CTF_TYPE_FLOAT,
+       BT_CTF_TYPE_ID_ENUM = CTF_TYPE_ENUM,
+       BT_CTF_TYPE_ID_STRING = CTF_TYPE_STRING,
+       BT_CTF_TYPE_ID_STRUCT = CTF_TYPE_STRUCT,
+       BT_CTF_TYPE_ID_UNTAGGED_VARIANT = CTF_TYPE_UNTAGGED_VARIANT,
+       BT_CTF_TYPE_ID_ARRAY = CTF_TYPE_ARRAY,
+       BT_CTF_TYPE_ID_SEQUENCE = CTF_TYPE_SEQUENCE,
+       BT_CTF_TYPE_ID_VARIANT = CTF_TYPE_VARIANT,
+       BT_CTF_NR_TYPE_IDS = NR_CTF_TYPES,
+};
+
+enum bt_ctf_byte_order {
+       BT_CTF_BYTE_ORDER_UNKNOWN = -1,
+       BT_CTF_BYTE_ORDER_NATIVE = 0,
+       BT_CTF_BYTE_ORDER_LITTLE_ENDIAN,
+       BT_CTF_BYTE_ORDER_BIG_ENDIAN,
+       BT_CTF_BYTE_ORDER_NETWORK,
+};
+
+enum bt_ctf_string_encoding {
+       BT_CTF_STRING_ENCODING_UNKNOWN = CTF_STRING_UNKNOWN,
+       BT_CTF_STRING_ENCODING_NONE = CTF_STRING_NONE,
+       BT_CTF_STRING_ENCODING_UTF8 = CTF_STRING_UTF8,
+       BT_CTF_STRING_ENCODING_ASCII = CTF_STRING_ASCII,
+};
+
+/* Common functions */
+enum bt_ctf_type_id bt_ctf_field_type_get_type_id(
+               struct bt_ctf_field_type *field_type);
+int bt_ctf_field_type_get_alignment(
+               struct bt_ctf_field_type *field_type);
+int bt_ctf_field_type_set_alignment(struct bt_ctf_field_type *field_type,
+               unsigned int alignment);
+enum bt_ctf_byte_order bt_ctf_field_type_get_byte_order(
+               struct bt_ctf_field_type *field_type);
+int bt_ctf_field_type_set_byte_order(
+               struct bt_ctf_field_type *field_type,
+               enum bt_ctf_byte_order byte_order);
+int bt_ctf_field_type_compare(struct bt_ctf_field_type *field_type_a,
+               struct bt_ctf_field_type *field_type_b);
+struct bt_ctf_field_type *bt_ctf_field_type_copy(
+               struct bt_ctf_field_type *field_type);
+
+/* Integer field type base enumeration */
+enum bt_ctf_integer_base {
+       BT_CTF_INTEGER_BASE_UNKNOWN = -1,
+       BT_CTF_INTEGER_BASE_BINARY = 2,
+       BT_CTF_INTEGER_BASE_OCTAL = 8,
+       BT_CTF_INTEGER_BASE_DECIMAL = 10,
+       BT_CTF_INTEGER_BASE_HEXADECIMAL = 16,
+};
+
+/* Integer field type functions */
+struct bt_ctf_field_type *bt_ctf_field_type_integer_create(
+               unsigned int size);
+int bt_ctf_field_type_integer_get_size(
+               struct bt_ctf_field_type *int_field_type);
+int bt_ctf_field_type_integer_get_signed(
+               struct bt_ctf_field_type *int_field_type);
+int bt_ctf_field_type_integer_set_signed(
+               struct bt_ctf_field_type *int_field_type, int is_signed);
+enum bt_ctf_integer_base bt_ctf_field_type_integer_get_base(
+               struct bt_ctf_field_type *int_field_type);
+int bt_ctf_field_type_integer_set_base(
+               struct bt_ctf_field_type *int_field_type,
+               enum bt_ctf_integer_base base);
+enum bt_ctf_string_encoding bt_ctf_field_type_integer_get_encoding(
+               struct bt_ctf_field_type *int_field_type);
+int bt_ctf_field_type_integer_set_encoding(
+               struct bt_ctf_field_type *int_field_type,
+               enum bt_ctf_string_encoding encoding);
+struct bt_ctf_clock_class *bt_ctf_field_type_integer_get_mapped_clock_class(
+               struct bt_ctf_field_type *int_field_type);
+int bt_ctf_field_type_integer_set_mapped_clock_class(
+               struct bt_ctf_field_type *int_field_type,
+               struct bt_ctf_clock_class *clock_class);
+
+/* Floating point number field type functions */
+struct bt_ctf_field_type *bt_ctf_field_type_floating_point_create(void);
+int bt_ctf_field_type_floating_point_get_exponent_digits(
+               struct bt_ctf_field_type *float_field_type);
+int bt_ctf_field_type_floating_point_set_exponent_digits(
+               struct bt_ctf_field_type *float_field_type,
+               unsigned int exponent_size);
+int bt_ctf_field_type_floating_point_get_mantissa_digits(
+               struct bt_ctf_field_type *float_field_type);
+int bt_ctf_field_type_floating_point_set_mantissa_digits(
+               struct bt_ctf_field_type *float_field_type,
+               unsigned int mantissa_sign_size);
+
+/* Enumeration field type functions */
+struct bt_ctf_field_type *bt_ctf_field_type_enumeration_create(
+               struct bt_ctf_field_type *int_field_type);
+struct bt_ctf_field_type *bt_ctf_field_type_enumeration_get_container_type(
+               struct bt_ctf_field_type *enum_field_type);
+int bt_ctf_field_type_enumeration_get_mapping_count(
+               struct bt_ctf_field_type *enum_field_type);
+int bt_ctf_field_type_enumeration_get_mapping_signed(
+               struct bt_ctf_field_type *enum_field_type, int index,
+               const char **BTOUTSTR, int64_t *OUTPUT, int64_t *OUTPUT);
+int bt_ctf_field_type_enumeration_get_mapping_unsigned(
+               struct bt_ctf_field_type *enum_field_type, int index,
+               const char **BTOUTSTR, uint64_t *OUTPUT,
+               uint64_t *OUTPUT);
+int bt_ctf_field_type_enumeration_add_mapping(
+               struct bt_ctf_field_type *enum_field_type, const char *name,
+               int64_t range_begin, int64_t range_end);
+int bt_ctf_field_type_enumeration_add_mapping_unsigned(
+               struct bt_ctf_field_type *enum_field_type, const char *name,
+               uint64_t range_begin, uint64_t range_end);
+struct bt_ctf_field_type_enumeration_mapping_iterator *
+bt_ctf_field_type_enumeration_find_mappings_by_name(
+               struct bt_ctf_field_type *enum_field_type,
+               const char *name);
+struct bt_ctf_field_type_enumeration_mapping_iterator *
+bt_ctf_field_type_enumeration_find_mappings_by_signed_value(
+               struct bt_ctf_field_type *enum_field_type,
+               int64_t value);
+struct bt_ctf_field_type_enumeration_mapping_iterator *
+bt_ctf_field_type_enumeration_find_mappings_by_unsigned_value(
+               struct bt_ctf_field_type *enum_field_type,
+               uint64_t value);
+
+/* Enumeration field type mapping iterator functions */
+int bt_ctf_field_type_enumeration_mapping_iterator_get_signed(
+               struct bt_ctf_field_type_enumeration_mapping_iterator *iter,
+               const char **BTOUTSTR, int64_t *OUTPUT, int64_t *OUTPUT);
+int bt_ctf_field_type_enumeration_mapping_iterator_get_unsigned(
+               struct bt_ctf_field_type_enumeration_mapping_iterator *iter,
+               const char **BTOUTSTR, int64_t *OUTPUT, int64_t *OUTPUT);
+int bt_ctf_field_type_enumeration_mapping_iterator_next(
+               struct bt_ctf_field_type_enumeration_mapping_iterator *iter);
+
+/* String field type functions */
+struct bt_ctf_field_type *bt_ctf_field_type_string_create(void);
+enum bt_ctf_string_encoding bt_ctf_field_type_string_get_encoding(
+               struct bt_ctf_field_type *string_field_type);
+int bt_ctf_field_type_string_set_encoding(
+               struct bt_ctf_field_type *string_field_type,
+               enum bt_ctf_string_encoding encoding);
+
+/* Structure field type functions */
+struct bt_ctf_field_type *bt_ctf_field_type_structure_create(void);
+int bt_ctf_field_type_structure_get_field_count(
+               struct bt_ctf_field_type *struct_field_type);
+int bt_ctf_field_type_structure_get_field(
+               struct bt_ctf_field_type *struct_field_type,
+               const char **BTOUTSTR, struct bt_ctf_field_type **BTOUTFT,
+               int index);
+struct bt_ctf_field_type *bt_ctf_field_type_structure_get_field_type_by_name(
+               struct bt_ctf_field_type *struct_field_type,
+               const char *field_name);
+int bt_ctf_field_type_structure_add_field(
+               struct bt_ctf_field_type *struct_field_type,
+               struct bt_ctf_field_type *field_type,
+               const char *field_name);
+
+/* Array field type functions */
+struct bt_ctf_field_type *bt_ctf_field_type_array_create(
+               struct bt_ctf_field_type *element_field_type,
+               unsigned int length);
+struct bt_ctf_field_type *bt_ctf_field_type_array_get_element_type(
+               struct bt_ctf_field_type *array_field_type);
+int64_t bt_ctf_field_type_array_get_length(
+               struct bt_ctf_field_type *array_field_type);
+
+/* Sequence field type functions */
+extern struct bt_ctf_field_type *bt_ctf_field_type_sequence_create(
+               struct bt_ctf_field_type *element_field_type,
+               const char *length_name);
+extern struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_type(
+               struct bt_ctf_field_type *sequence_field_type);
+extern const char *bt_ctf_field_type_sequence_get_length_field_name(
+               struct bt_ctf_field_type *sequence_field_type);
+extern struct bt_ctf_field_path *bt_ctf_field_type_sequence_get_length_field_path(
+               struct bt_ctf_field_type *sequence_field_type);
+
+/* Variant field type functions */
+struct bt_ctf_field_type *bt_ctf_field_type_variant_create(
+               struct bt_ctf_field_type *tag_field_type,
+               const char *tag_name);
+struct bt_ctf_field_type *bt_ctf_field_type_variant_get_tag_type(
+               struct bt_ctf_field_type *variant_field_type);
+const char *bt_ctf_field_type_variant_get_tag_name(
+               struct bt_ctf_field_type *variant_field_type);
+int bt_ctf_field_type_variant_set_tag_name(
+               struct bt_ctf_field_type *variant_field_type,
+               const char *tag_name);
+struct bt_ctf_field_path *bt_ctf_field_type_variant_get_tag_field_path(
+               struct bt_ctf_field_type *variant_field_type);
+int bt_ctf_field_type_variant_get_field_count(
+               struct bt_ctf_field_type *variant_field_type);
+int bt_ctf_field_type_variant_get_field(
+               struct bt_ctf_field_type *variant_field_type,
+               const char **BTOUTSTR,
+               struct bt_ctf_field_type **BTOUTFT, int index);
+struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_by_name(
+               struct bt_ctf_field_type *variant_field_type,
+               const char *field_name);
+int bt_ctf_field_type_variant_add_field(
+               struct bt_ctf_field_type *variant_field_type,
+               struct bt_ctf_field_type *field_type,
+               const char *field_name);
diff --git a/bindings/python/bt2/native_btnotification.i b/bindings/python/bt2/native_btnotification.i
new file mode 100644 (file)
index 0000000..294110e
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/component/notification/event.h>
+#include <babeltrace/component/notification/packet.h>
+#include <babeltrace/component/notification/schema.h>
+#include <babeltrace/component/notification/stream.h>
+%}
+
+/* Type */
+struct bt_notification;;
+
+/* Notification type */
+enum bt_notification_type {
+       BT_NOTIFICATION_TYPE_UNKNOWN = -1,
+       BT_NOTIFICATION_TYPE_ALL = 0,
+       BT_NOTIFICATION_TYPE_EVENT = 1,
+       BT_NOTIFICATION_TYPE_PACKET_BEGIN = 2,
+       BT_NOTIFICATION_TYPE_PACKET_END = 3,
+       BT_NOTIFICATION_TYPE_STREAM_END = 4,
+       BT_NOTIFICATION_TYPE_NEW_TRACE = 5,
+       BT_NOTIFICATION_TYPE_NEW_STREAM_CLASS = 6,
+       BT_NOTIFICATION_TYPE_NEW_EVENT_CLASS = 7,
+       BT_NOTIFICATION_TYPE_END_OF_TRACE = 8,
+       BT_NOTIFICATION_TYPE_NR,
+};
+
+/* General functions */
+enum bt_notification_type bt_notification_get_type(
+               struct bt_notification *notification);
+
+/* Event notification functions */
+struct bt_notification *bt_notification_event_create(
+               struct bt_ctf_event *event);
+struct bt_ctf_event *bt_notification_event_get_event(
+               struct bt_notification *notification);
+
+/* Packet notification functions */
+struct bt_notification *bt_notification_packet_begin_create(
+               struct bt_ctf_packet *packet);
+struct bt_ctf_packet *bt_notification_packet_begin_get_packet(
+               struct bt_notification *notification);
+struct bt_notification *bt_notification_packet_end_create(
+               struct bt_ctf_packet *packet);
+struct bt_ctf_packet *bt_notification_packet_end_get_packet(
+               struct bt_notification *notification);
+
+/* Schema notification functions */
+/*
+struct bt_notification *bt_notification_new_trace_create(
+               struct bt_ctf_trace *trace);
+struct bt_ctf_trace *bt_notification_new_trace_get_trace(
+               struct bt_notification *notification);
+struct bt_notification *bt_notification_new_stream_class_create(
+               struct bt_ctf_stream_class *stream_class);
+struct bt_ctf_trace *bt_notification_new_stream_class_get_stream_class(
+               struct bt_notification *notification);
+*/
+
+/* Stream notification functions */
+struct bt_notification *bt_notification_stream_end_create(
+               struct bt_ctf_stream *stream);
+struct bt_ctf_stream *bt_notification_stream_end_get_stream(
+               struct bt_notification *notification);
diff --git a/bindings/python/bt2/native_btnotifiter.i b/bindings/python/bt2/native_btnotifiter.i
new file mode 100644 (file)
index 0000000..d1deb48
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/component/notification/iterator.h>
+%}
+
+/* Type */
+struct bt_notification_iterator;
+
+/* Status */
+enum bt_notification_iterator_status {
+       BT_NOTIFICATION_ITERATOR_STATUS_END = 1,
+       BT_NOTIFICATION_ITERATOR_STATUS_OK = 0,
+       BT_NOTIFICATION_ITERATOR_STATUS_INVAL = -1,
+       BT_NOTIFICATION_ITERATOR_STATUS_ERROR = -2,
+       BT_NOTIFICATION_ITERATOR_STATUS_NOMEM = -3,
+       BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED = -4,
+};
+
+/* Seek reference */
+enum bt_notification_iterator_seek_origin {
+       BT_NOTIFICATION_ITERATOR_SEEK_ORIGIN_BEGIN = 0,
+       BT_NOTIFICATION_ITERATOR_SEEK_ORIGIN_CURRENT = 1,
+       BT_NOTIFICATION_ITERATOR_SEEK_ORIGIN_END = 2,
+       BT_NOTIFICATION_ITERATOR_SEEK_ORIGIN_EPOCH = 3,
+};
+
+/* Functions */
+struct bt_notification *bt_notification_iterator_get_notification(
+               struct bt_notification_iterator *iterator);
+enum bt_notification_iterator_status bt_notification_iterator_next(
+               struct bt_notification_iterator *iterator);
+enum bt_notification_iterator_status bt_notification_iterator_seek_time(
+               struct bt_notification_iterator *iterator,
+               enum bt_notification_iterator_seek_origin seek_origin,
+               int64_t time);
+struct bt_component *bt_notification_iterator_get_component(
+               struct bt_notification_iterator *iterator);
+enum bt_notification_iterator_status bt_notification_iterator_set_private_data(
+               struct bt_notification_iterator *iterator, void *data);
+void *bt_notification_iterator_get_private_data(
+               struct bt_notification_iterator *iterator);
+
+/* Helper functions for Python */
+%{
+static PyObject *bt_py3_get_component_from_notif_iter(
+               struct bt_notification_iterator *iter)
+{
+       struct bt_component *component = bt_notification_iterator_get_component(iter);
+       PyObject *py_comp;
+
+       assert(component);
+       py_comp = bt_component_get_private_data(component);
+       BT_PUT(component);
+       assert(py_comp);
+
+       /* Return new reference */
+       Py_INCREF(py_comp);
+       return py_comp;
+}
+%}
+
+PyObject *bt_py3_get_component_from_notif_iter(
+               struct bt_notification_iterator *iter);
diff --git a/bindings/python/bt2/native_btpacket.i b/bindings/python/bt2/native_btpacket.i
new file mode 100644 (file)
index 0000000..e9181e4
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/packet.h>
+%}
+
+/* Type */
+struct bt_ctf_packet;
+
+/* Functions */
+struct bt_ctf_packet *bt_ctf_packet_create(
+               struct bt_ctf_stream *stream);
+struct bt_ctf_stream *bt_ctf_packet_get_stream(
+               struct bt_ctf_packet *packet);
+struct bt_ctf_field *bt_ctf_packet_get_header(
+               struct bt_ctf_packet *packet);
+int bt_ctf_packet_set_header(
+               struct bt_ctf_packet *packet, struct bt_ctf_field *header);
+struct bt_ctf_field *bt_ctf_packet_get_context(
+               struct bt_ctf_packet *packet);
+int bt_ctf_packet_set_context(
+               struct bt_ctf_packet *packet, struct bt_ctf_field *context);
diff --git a/bindings/python/bt2/native_btplugin.i b/bindings/python/bt2/native_btplugin.i
new file mode 100644 (file)
index 0000000..9b1bb56
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/plugin/plugin.h>
+#include <assert.h>
+%}
+
+/* Types */
+struct bt_plugin;
+
+/* Status */
+enum bt_plugin_status {
+       BT_PLUGIN_STATUS_OK =           0,
+       BT_PLUGIN_STATUS_ERROR =        -1,
+       BT_PLUGIN_STATUS_NOMEM =        -4,
+};
+
+/* Functions */
+const char *bt_plugin_get_name(struct bt_plugin *plugin);
+const char *bt_plugin_get_author(struct bt_plugin *plugin);
+const char *bt_plugin_get_license(struct bt_plugin *plugin);
+const char *bt_plugin_get_description(struct bt_plugin *plugin);
+const char *bt_plugin_get_path(struct bt_plugin *plugin);
+enum bt_plugin_status bt_plugin_get_version(struct bt_plugin *plugin,
+               unsigned int *OUTPUT, unsigned int *OUTPUT, unsigned int *OUTPUT,
+               const char **BTOUTSTR);
+int bt_plugin_get_component_class_count(struct bt_plugin *plugin);
+struct bt_component_class *bt_plugin_get_component_class(
+               struct bt_plugin *plugin, size_t index);
+struct bt_component_class *bt_plugin_get_component_class_by_name_and_type(
+               struct bt_plugin *plugin, const char *name,
+               enum bt_component_class_type type);
+
+%{
+static PyObject *bt_py3_plugin_ptrs_list_from_bt_plugins(struct bt_plugin **plugins)
+{
+       PyObject *py_plugin_ptrs = NULL;
+       struct bt_plugin **plugin_at;
+
+       if (!plugins) {
+               goto error;
+       }
+
+       py_plugin_ptrs = PyList_New(0);
+       if (!py_plugin_ptrs) {
+               goto error;
+       }
+
+       plugin_at = plugins;
+
+       while (*plugin_at) {
+               struct bt_plugin *plugin = *plugin_at;
+               PyObject *py_plugin_ptr;
+               int ret;
+
+               py_plugin_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(plugin),
+                       SWIGTYPE_p_bt_plugin, 0);
+               if (!py_plugin_ptr) {
+                       goto error;
+               }
+
+               ret = PyList_Append(py_plugin_ptrs, py_plugin_ptr);
+               Py_DECREF(py_plugin_ptr);
+               if (ret < 0) {
+                       goto error;
+               }
+
+               plugin_at++;
+       }
+
+       goto end;
+
+error:
+       Py_XDECREF(py_plugin_ptrs);
+       py_plugin_ptrs = Py_None;
+       Py_INCREF(py_plugin_ptrs);
+
+       if (plugins) {
+               /*
+                * Put existing plugin references since they are not
+                * moved to the caller.
+                */
+               plugin_at = plugins;
+
+               while (*plugin_at) {
+                       bt_put(*plugin_at);
+                       plugin_at++;
+               }
+       }
+
+end:
+       PyErr_Clear();
+       free(plugins);
+       return py_plugin_ptrs;
+}
+
+static PyObject *bt_py3_plugin_create_all_from_file(const char *path)
+{
+       return bt_py3_plugin_ptrs_list_from_bt_plugins(
+               bt_plugin_create_all_from_file(path));
+}
+
+static PyObject *bt_py3_plugin_create_all_from_dir(const char *path,
+               bool recurse)
+{
+       return bt_py3_plugin_ptrs_list_from_bt_plugins(
+               bt_plugin_create_all_from_dir(path, recurse));
+}
+%}
+
+PyObject *bt_py3_plugin_create_all_from_file(const char *path);
+PyObject *bt_py3_plugin_create_all_from_dir(const char *path,
+               bool recurse);
diff --git a/bindings/python/bt2/native_btref.i b/bindings/python/bt2/native_btref.i
new file mode 100644 (file)
index 0000000..1d7a2d3
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ref.h>
+%}
+
+/* Functions */
+void *bt_get(void *obj);
+void bt_put(void *obj);
diff --git a/bindings/python/bt2/native_btstream.i b/bindings/python/bt2/native_btstream.i
new file mode 100644 (file)
index 0000000..91d4dd8
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/stream.h>
+%}
+
+/* Type */
+struct bt_ctf_stream;
+
+/* Functions */
+struct bt_ctf_stream *bt_ctf_stream_create(
+               struct bt_ctf_stream_class *stream_class,
+               const char *name);
+const char *bt_ctf_stream_get_name(struct bt_ctf_stream *stream);
+struct bt_ctf_stream_class *bt_ctf_stream_get_class(
+               struct bt_ctf_stream *stream);
diff --git a/bindings/python/bt2/native_btstreamclass.i b/bindings/python/bt2/native_btstreamclass.i
new file mode 100644 (file)
index 0000000..2ba6024
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/stream-class.h>
+%}
+
+/* Type */
+struct bt_ctf_stream_class;
+
+/* Functions */
+struct bt_ctf_stream_class *bt_ctf_stream_class_create(const char *name);
+struct bt_ctf_trace *bt_ctf_stream_class_get_trace(
+               struct bt_ctf_stream_class *stream_class);
+const char *bt_ctf_stream_class_get_name(
+               struct bt_ctf_stream_class *stream_class);
+int bt_ctf_stream_class_set_name(
+               struct bt_ctf_stream_class *stream_class, const char *name);
+int64_t bt_ctf_stream_class_get_id(
+               struct bt_ctf_stream_class *stream_class);
+int bt_ctf_stream_class_set_id(
+               struct bt_ctf_stream_class *stream_class, uint32_t id);
+struct bt_ctf_field_type *bt_ctf_stream_class_get_packet_context_type(
+               struct bt_ctf_stream_class *stream_class);
+int bt_ctf_stream_class_set_packet_context_type(
+               struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_field_type *packet_context_type);
+struct bt_ctf_field_type *
+bt_ctf_stream_class_get_event_header_type(
+               struct bt_ctf_stream_class *stream_class);
+int bt_ctf_stream_class_set_event_header_type(
+               struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_field_type *event_header_type);
+struct bt_ctf_field_type *
+bt_ctf_stream_class_get_event_context_type(
+               struct bt_ctf_stream_class *stream_class);
+int bt_ctf_stream_class_set_event_context_type(
+               struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_field_type *event_context_type);
+int bt_ctf_stream_class_get_event_class_count(
+               struct bt_ctf_stream_class *stream_class);
+struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class(
+               struct bt_ctf_stream_class *stream_class, int index);
+struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_name(
+               struct bt_ctf_stream_class *stream_class, const char *name);
+struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_id(
+               struct bt_ctf_stream_class *stream_class, uint32_t id);
+int bt_ctf_stream_class_add_event_class(
+               struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_event_class *event_class);
diff --git a/bindings/python/bt2/native_bttrace.i b/bindings/python/bt2/native_bttrace.i
new file mode 100644 (file)
index 0000000..523acf7
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/ctf-ir/trace.h>
+%}
+
+/* Type */
+struct bt_ctf_trace;
+
+/* Functions */
+struct bt_ctf_trace *bt_ctf_trace_create(void);
+const char *bt_ctf_trace_get_name(struct bt_ctf_trace *trace);
+int bt_ctf_trace_set_name(struct bt_ctf_trace *trace,
+               const char *name);
+enum bt_ctf_byte_order bt_ctf_trace_get_byte_order(
+               struct bt_ctf_trace *trace);
+int bt_ctf_trace_set_byte_order(struct bt_ctf_trace *trace,
+               enum bt_ctf_byte_order byte_order);
+int bt_ctf_trace_get_environment_field_count(
+               struct bt_ctf_trace *trace);
+const char *
+bt_ctf_trace_get_environment_field_name(struct bt_ctf_trace *trace,
+               int index);
+struct bt_value *
+bt_ctf_trace_get_environment_field_value(struct bt_ctf_trace *trace,
+               int index);
+struct bt_value *
+bt_ctf_trace_get_environment_field_value_by_name(
+               struct bt_ctf_trace *trace, const char *name);
+int bt_ctf_trace_set_environment_field(
+               struct bt_ctf_trace *trace, const char *name,
+               struct bt_value *value);
+struct bt_ctf_field_type *bt_ctf_trace_get_packet_header_type(
+               struct bt_ctf_trace *trace);
+int bt_ctf_trace_set_packet_header_type(struct bt_ctf_trace *trace,
+               struct bt_ctf_field_type *packet_header_type);
+int bt_ctf_trace_get_clock_class_count(struct bt_ctf_trace *trace);
+struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class(
+               struct bt_ctf_trace *trace, int index);
+struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name(
+               struct bt_ctf_trace *trace, const char *name);
+int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace,
+               struct bt_ctf_clock_class *clock_class);
+int bt_ctf_trace_get_stream_class_count(struct bt_ctf_trace *trace);
+struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class(
+               struct bt_ctf_trace *trace, int index);
+struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_id(
+               struct bt_ctf_trace *trace, uint32_t id);
+int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace,
+               struct bt_ctf_stream_class *stream_class);
diff --git a/bindings/python/bt2/native_btvalues.i b/bindings/python/bt2/native_btvalues.i
new file mode 100644 (file)
index 0000000..57fdae7
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+%{
+#include <babeltrace/values.h>
+%}
+
+/* For uint*_t/int*_t */
+%include "stdint.i"
+
+/* Remove prefix from `bt_value_null` */
+%rename(value_null) bt_value_null;
+
+/* Type and status */
+struct bt_value;
+
+enum bt_value_type {
+       BT_VALUE_TYPE_UNKNOWN =         -1,
+       BT_VALUE_TYPE_NULL =            0,
+       BT_VALUE_TYPE_BOOL =            1,
+       BT_VALUE_TYPE_INTEGER =         2,
+       BT_VALUE_TYPE_FLOAT =           3,
+       BT_VALUE_TYPE_STRING =          4,
+       BT_VALUE_TYPE_ARRAY =           5,
+       BT_VALUE_TYPE_MAP =             6,
+};
+
+enum bt_value_type bt_value_get_type(const struct bt_value *object);
+
+enum bt_value_status {
+       BT_VALUE_STATUS_FROZEN =        -4,
+       BT_VALUE_STATUS_CANCELLED =     -3,
+       BT_VALUE_STATUS_INVAL =         -22,
+       BT_VALUE_STATUS_ERROR =         -1,
+       BT_VALUE_STATUS_OK =            0,
+};
+
+/* Null value object singleton */
+struct bt_value * const bt_value_null;
+
+/* Common functions */
+enum bt_value_status bt_value_freeze(struct bt_value *object);
+bool bt_value_is_frozen(const struct bt_value *object);
+struct bt_value *bt_value_copy(const struct bt_value *object);
+bool bt_value_compare(const struct bt_value *object_a,
+               const struct bt_value *object_b);
+
+/* Boolean value object functions */
+struct bt_value *bt_value_bool_create(void);
+struct bt_value *bt_value_bool_create_init(bool val);
+enum bt_value_status bt_value_bool_get(
+               const struct bt_value *bool_obj, bool *OUTPUT);
+enum bt_value_status bt_value_bool_set(struct bt_value *bool_obj,
+               bool val);
+
+/* Integer value object functions */
+struct bt_value *bt_value_integer_create(void);
+struct bt_value *bt_value_integer_create_init(int64_t val);
+enum bt_value_status bt_value_integer_get(
+               const struct bt_value *integer_obj, int64_t *OUTPUT);
+enum bt_value_status bt_value_integer_set(
+               struct bt_value *integer_obj, int64_t val);
+
+/* Floating point number value object functions */
+struct bt_value *bt_value_float_create(void);
+struct bt_value *bt_value_float_create_init(double val);
+enum bt_value_status bt_value_float_get(
+               const struct bt_value *float_obj, double *OUTPUT);
+enum bt_value_status bt_value_float_set(
+               struct bt_value *float_obj, double val);
+
+/* String value object functions */
+struct bt_value *bt_value_string_create(void);
+struct bt_value *bt_value_string_create_init(const char *val);
+enum bt_value_status bt_value_string_set(struct bt_value *string_obj,
+               const char *val);
+enum bt_value_status bt_value_string_get(
+               const struct bt_value *string_obj, const char **BTOUTSTR);
+
+/* Array value object functions */
+struct bt_value *bt_value_array_create(void);
+int bt_value_array_size(const struct bt_value *array_obj);
+struct bt_value *bt_value_array_get(const struct bt_value *array_obj,
+               size_t index);
+enum bt_value_status bt_value_array_append(struct bt_value *array_obj,
+               struct bt_value *element_obj);
+enum bt_value_status bt_value_array_set(struct bt_value *array_obj,
+               size_t index, struct bt_value *element_obj);
+
+/* Map value object functions */
+struct bt_value *bt_value_map_create(void);
+int bt_value_map_size(const struct bt_value *map_obj);
+struct bt_value *bt_value_map_get(const struct bt_value *map_obj,
+               const char *key);
+bool bt_value_map_has_key(const struct bt_value *map_obj,
+               const char *key);
+enum bt_value_status bt_value_map_insert(
+               struct bt_value *map_obj, const char *key,
+               struct bt_value *element_obj);
+
+%{
+struct bt_value_map_get_keys_private_data {
+       struct bt_value *keys;
+};
+
+static bool bt_value_map_get_keys_private_cb(const char *key,
+               struct bt_value *object, void *data)
+{
+       enum bt_value_status status;
+       struct bt_value_map_get_keys_private_data *priv_data = data;
+
+       status = bt_value_array_append_string(priv_data->keys, key);
+       if (status != BT_VALUE_STATUS_OK) {
+               return false;
+       }
+
+       return true;
+}
+
+static struct bt_value *bt_value_map_get_keys_private(
+               const struct bt_value *map_obj)
+{
+       enum bt_value_status status;
+       struct bt_value_map_get_keys_private_data data;
+
+       data.keys = bt_value_array_create();
+       if (!data.keys) {
+               return NULL;
+       }
+
+       status = bt_value_map_foreach(map_obj, bt_value_map_get_keys_private_cb,
+               &data);
+       if (status != BT_VALUE_STATUS_OK) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       if (data.keys) {
+               BT_PUT(data.keys);
+       }
+
+end:
+       return data.keys;
+}
+%}
+
+struct bt_value *bt_value_map_get_keys_private(const struct bt_value *map_obj);
diff --git a/bindings/python/bt2/notification.py b/bindings/python/bt2/notification.py
new file mode 100644 (file)
index 0000000..66ed4cd
--- /dev/null
@@ -0,0 +1,153 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.packet
+import bt2.stream
+import bt2.event
+import bt2
+
+
+def _create_from_ptr(ptr):
+    notif_type = native_bt.notification_get_type(ptr)
+    cls = None
+
+    if notif_type not in _NOTIF_TYPE_TO_CLS:
+        raise bt2.Error('unknown notification type: {}'.format(notif_type))
+
+    return _NOTIF_TYPE_TO_CLS[notif_type]._create_from_ptr(ptr)
+
+
+class _Notification(object._Object):
+    pass
+
+
+class TraceEventNotification(_Notification):
+    def __init__(self, event):
+        utils._check_type(event, bt2.event._Event)
+        ptr = native_bt.notification_event_create(event._ptr)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create trace event notification object')
+
+        super().__init__(ptr)
+
+    @property
+    def event(self):
+        event_ptr = native_bt.notification_event_get_event(self._ptr)
+        utils._handle_ptr(event_ptr, "cannot get trace event notification object's event object")
+        return bt2.event._create_from_ptr(event_ptr)
+
+
+class BeginningOfPacketNotification(_Notification):
+    def __init__(self, packet):
+        utils._check_type(packet, bt2.packet._Packet)
+        ptr = native_bt.notification_packet_begin_create(packet._ptr)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create beginning of packet notification object')
+
+        super().__init__(ptr)
+
+    @property
+    def packet(self):
+        packet_ptr = native_bt.notification_packet_begin_get_packet(self._ptr)
+        utils._handle_ptr(packet_ptr, "cannot get beginning of packet notification object's packet object")
+        return bt2.packet._Packet._create_from_ptr(packet_ptr)
+
+
+class EndOfPacketNotification(_Notification):
+    def __init__(self, packet):
+        utils._check_type(packet, bt2.packet._Packet)
+        ptr = native_bt.notification_packet_end_create(packet._ptr)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create end of packet notification object')
+
+        super().__init__(ptr)
+
+    @property
+    def packet(self):
+        packet_ptr = native_bt.notification_packet_end_get_packet(self._ptr)
+        utils._handle_ptr(packet_ptr, "cannot get end of packet notification object's packet object")
+        return bt2.packet._Packet._create_from_ptr(packet_ptr)
+
+
+class EndOfStreamNotification(_Notification):
+    def __init__(self, stream):
+        utils._check_type(stream, bt2.stream._Stream)
+        ptr = native_bt.notification_stream_end_create(stream._ptr)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create end of stream notification object')
+
+        super().__init__(ptr)
+
+    @property
+    def stream(self):
+        stream_ptr = native_bt.notification_stream_end_get_stream(self._ptr)
+        utils._handle_ptr(stream_ptr, "cannot get end of stream notification object's stream object")
+        return bt2.stream._create_from_ptr(stream_ptr)
+
+
+class NewTraceNotification(_Notification):
+    def __init__(self, trace):
+        utils._check_type(trace, bt2.Trace)
+        ptr = native_bt.notification_new_trace_create(trace._ptr)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create new trace notification object')
+
+        super().__init__(ptr)
+
+    @property
+    def trace(self):
+        trace_ptr = native_bt.notification_new_trace_get_trace(self._ptr)
+        utils._handle_ptr(trace_ptr, "cannot get new trace notification object's trace object")
+        return bt2.trace._create_from_ptr(trace_ptr)
+
+
+class NewStreamClassNotification(_Notification):
+    def __init__(self, stream_class):
+        utils._check_type(stream_class, bt2.StreamClass)
+        ptr = native_bt.notification_new_stream_class_create(stream_class._ptr)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create new stream class notification object')
+
+        super().__init__(ptr)
+
+    @property
+    def stream_class(self):
+        stream_class_ptr = native_bt.notification_new_stream_class_get_stream_class(self._ptr)
+        utils._handle_ptr(stream_class_ptr, "cannot get new stream class notification object's stream class object")
+        return bt2.stream_class.StreamClass._create_from_ptr(stream_class_ptr)
+
+
+_NOTIF_TYPE_TO_CLS = {
+    native_bt.NOTIFICATION_TYPE_EVENT: TraceEventNotification,
+    native_bt.NOTIFICATION_TYPE_PACKET_BEGIN: BeginningOfPacketNotification,
+    native_bt.NOTIFICATION_TYPE_PACKET_END: EndOfPacketNotification,
+    native_bt.NOTIFICATION_TYPE_STREAM_END: EndOfStreamNotification,
+    native_bt.NOTIFICATION_TYPE_NEW_TRACE: NewTraceNotification,
+    native_bt.NOTIFICATION_TYPE_NEW_STREAM_CLASS: NewStreamClassNotification,
+}
diff --git a/bindings/python/bt2/notification_iterator.py b/bindings/python/bt2/notification_iterator.py
new file mode 100644 (file)
index 0000000..e0d0b0c
--- /dev/null
@@ -0,0 +1,115 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.notification
+import collections.abc
+import bt2.component
+import bt2
+
+
+class NotificationIteratorSeekOrigin:
+    BEGIN = native_bt.NOTIFICATION_ITERATOR_SEEK_ORIGIN_BEGIN
+    CURRENT = native_bt.NOTIFICATION_ITERATOR_SEEK_ORIGIN_CURRENT
+    END = native_bt.NOTIFICATION_ITERATOR_SEEK_ORIGIN_END
+
+
+class _GenericNotificationIteratorMethods(collections.abc.Iterator):
+    @property
+    def notification(self):
+        notif_ptr = native_bt.notification_iterator_get_notification(self._ptr)
+        utils._handle_ptr(notif_ptr, "cannot get notification iterator object's current notification object")
+        return bt2.notification._create_from_ptr(notif_ptr)
+
+    def _handle_status(self, status, gen_error_msg):
+        if status == native_bt.NOTIFICATION_ITERATOR_STATUS_END:
+            raise bt2.Stop
+        elif status == native_bt.NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED:
+            raise bt2.UnsupportedFeature
+        elif status < 0:
+            raise bt2.Error(gen_error_msg)
+
+    def next(self):
+        status = native_bt.notification_iterator_next(self._ptr)
+        self._handle_status(status,
+                            'unexpected error: cannot go to the next notification')
+
+    def __next__(self):
+        self.next()
+        return self.notification
+
+    def seek_to_time(self, origin, time):
+        utils._check_int64(origin)
+        utils._check_int64(time)
+        status = native_bt.notification_iterator_seek_time(self._ptr, origin,
+                                                           time)
+        self._handle_status(status,
+                            'unexpected error: cannot seek notification iterator to time')
+
+
+class _GenericNotificationIterator(object._Object, _GenericNotificationIteratorMethods):
+    @property
+    def component(self):
+        comp_ptr = native_bt.notification_iterator_get_component(self._ptr)
+        utils._handle_ptr(comp_ptr, "cannot get notification iterator object's component object")
+        return bt2.component._create_generic_component_from_ptr(comp_ptr)
+
+
+class UserNotificationIterator(_GenericNotificationIteratorMethods):
+    def __new__(cls, ptr):
+        # User iterator objects are always created by the BT system,
+        # that is, never instantiated directly by Python code.
+        #
+        # The system calls this, then manually calls self.__init__()
+        # without the `ptr` argument. The user has access to
+        # self.component during this call, thanks to this self._ptr
+        # argument being set.
+        #
+        # self._ptr is NOT owned by this object here, so there's nothing
+        # to do in __del__().
+        self = super().__new__(cls)
+        self._ptr = ptr
+        return self
+
+    def __init__(self):
+        pass
+
+    @property
+    def component(self):
+        return native_bt.py3_get_component_from_notif_iter(self._ptr)
+
+    @property
+    def addr(self):
+        return int(self._ptr)
+
+    def _destroy(self):
+        pass
+
+    def _get_from_bt(self):
+        # this can raise anything: it's catched by the system
+        notif = self._get()
+        utils._check_type(notif, bt2.notification._Notification)
+
+        # steal the underlying native notification object for the caller
+        notif_ptr = notif._ptr
+        notif._ptr = None
+        return int(notif_ptr)
diff --git a/bindings/python/bt2/object.py b/bindings/python/bt2/object.py
new file mode 100644 (file)
index 0000000..ef574e1
--- /dev/null
@@ -0,0 +1,72 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt
+import abc
+
+
+class _Object:
+    def __init__(self, ptr):
+        self._ptr = ptr
+
+    @property
+    def addr(self):
+        return int(self._ptr)
+
+    @classmethod
+    def _create_from_ptr(cls, ptr):
+        obj = cls.__new__(cls)
+        obj._ptr = ptr
+        return obj
+
+    def _get(self):
+        native_bt.get(self._ptr)
+
+    def __del__(self):
+        ptr = getattr(self, '_ptr', None)
+        native_bt.put(ptr)
+
+    def __repr__(self):
+        return '<{}.{} object @ {}>'.format(self.__class__.__module__,
+                                            self.__class__.__name__,
+                                            hex(self.addr))
+
+
+class _Freezable(metaclass=abc.ABCMeta):
+    @property
+    def is_frozen(self):
+        return self._is_frozen()
+
+    @property
+    def frozen(self):
+        return self.is_frozen
+
+    def freeze(self):
+        self._freeze()
+
+    @abc.abstractmethod
+    def _is_frozen(self):
+        pass
+
+    @abc.abstractmethod
+    def _freeze(self):
+        pass
diff --git a/bindings/python/bt2/packet.py b/bindings/python/bt2/packet.py
new file mode 100644 (file)
index 0000000..9a9d79b
--- /dev/null
@@ -0,0 +1,107 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.fields
+import bt2.stream
+import copy
+import abc
+import bt2
+
+
+class _Packet(object._Object):
+    @property
+    def stream(self):
+        stream_ptr = native_bt.ctf_packet_get_stream(self._ptr)
+        utils._handle_ptr(stream_ptr, "cannot get packet object's stream object")
+        return bt2.stream._Stream._create_from_ptr(stream_ptr)
+
+    @property
+    def header_field(self):
+        field_ptr = native_bt.ctf_packet_get_header(self._ptr)
+
+        if field_ptr is None:
+            return
+
+        return bt2.fields._create_from_ptr(field_ptr)
+
+    @header_field.setter
+    def header_field(self, header_field):
+        header_field_ptr = None
+
+        if header_field is not None:
+            utils._check_type(header_field, bt2.fields._Field)
+            header_field_ptr = header_field._ptr
+
+        ret = native_bt.ctf_packet_set_header(self._ptr, header_field_ptr)
+        utils._handle_ret(ret, "cannot set packet object's header field")
+
+    @property
+    def context_field(self):
+        field_ptr = native_bt.ctf_packet_get_context(self._ptr)
+
+        if field_ptr is None:
+            return
+
+        return bt2.fields._create_from_ptr(field_ptr)
+
+    @context_field.setter
+    def context_field(self, context_field):
+        context_field_ptr = None
+
+        if context_field is not None:
+            utils._check_type(context_field, bt2.fields._Field)
+            context_field_ptr = context_field._ptr
+
+        ret = native_bt.ctf_packet_set_context(self._ptr, context_field_ptr)
+        utils._handle_ret(ret, "cannot set packet object's context field")
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        self_props = (
+            self.header_field,
+            self.context_field,
+        )
+        other_props = (
+            other.header_field,
+            other.context_field,
+        )
+        return self_props == other_props
+
+    def _copy(self, copy_func):
+        cpy = self.stream.create_packet()
+        cpy.header_field = copy_func(self.header_field)
+        cpy.context_field = copy_func(self.context_field)
+        return cpy
+
+    def __copy__(self):
+        return self._copy(copy.copy)
+
+    def __deepcopy__(self, memo):
+        cpy = self._copy(copy.deepcopy)
+        memo[id(self)] = cpy
+        return cpy
diff --git a/bindings/python/bt2/plugin.py b/bindings/python/bt2/plugin.py
new file mode 100644 (file)
index 0000000..1f71c12
--- /dev/null
@@ -0,0 +1,169 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import collections.abc
+import bt2.component
+import bt2
+
+
+def _plugin_ptrs_to_plugins(plugin_ptrs):
+    plugins = []
+
+    for plugin_ptr in plugin_ptrs:
+        plugin = _Plugin._create_from_ptr(plugin_ptr)
+        plugins.append(plugin)
+
+    return plugins
+
+
+def create_plugins_from_file(path):
+    utils._check_str(path)
+    plugin_ptrs = native_bt.py3_plugin_create_all_from_file(path)
+
+    if plugin_ptrs is None:
+        raise bt2.Error('cannot get plugin objects from file')
+
+    return _plugin_ptrs_to_plugins(plugin_ptrs)
+
+
+def create_plugins_from_dir(path, recurse=True):
+    utils._check_str(path)
+    plugin_ptrs = native_bt.py3_plugin_create_all_from_dir(path, recurse)
+
+    if plugin_ptrs is None:
+        raise bt2.Error('cannot get plugin objects from directory')
+
+    return _plugin_ptrs_to_plugins(plugin_ptrs)
+
+
+class _PluginVersion:
+    def __init__(self, major, minor, patch, extra):
+        self._major = major
+        self._minor = minor
+        self._patch = patch
+        self._extra = extra
+
+    @property
+    def major(self):
+        return self._major
+
+    @property
+    def minor(self):
+        return self._minor
+
+    @property
+    def patch(self):
+        return self._patch
+
+    @property
+    def extra(self):
+        return self._extra
+
+    def __str__(self):
+        extra = ''
+
+        if self._extra is not None:
+            extra = self._extra
+
+        return '{}.{}.{}{}'.format(self._major, self._minor, self._patch, extra)
+
+
+class _Plugin(object._Object, collections.abc.Sequence):
+    @property
+    def name(self):
+        name = native_bt.plugin_get_name(self._ptr)
+        utils._handle_ptr(name, "cannot get plugin object's name")
+        return name
+
+    @property
+    def author(self):
+        return native_bt.plugin_get_author(self._ptr)
+
+    @property
+    def license(self):
+        return native_bt.plugin_get_license(self._ptr)
+
+    @property
+    def description(self):
+        return native_bt.plugin_get_description(self._ptr)
+
+    @property
+    def path(self):
+        return native_bt.plugin_get_path(self._ptr)
+
+    @property
+    def version(self):
+        status, major, minor, patch, extra = native_bt.plugin_get_version(self._ptr)
+
+        if status < 0:
+            return
+
+        return _PluginVersion(major, minor, patch, extra)
+
+    def source_component_class(self, name):
+        utils._check_str(name)
+        cc_ptr = native_bt.plugin_get_component_class_by_name_and_type(self._ptr,
+                                                                       name,
+                                                                       native_bt.COMPONENT_CLASS_TYPE_SOURCE)
+
+        if cc_ptr is None:
+            return
+
+        return bt2.component._create_generic_component_class_from_ptr(cc_ptr)
+
+    def filter_component_class(self, name):
+        utils._check_str(name)
+        cc_ptr = native_bt.plugin_get_component_class_by_name_and_type(self._ptr,
+                                                                       name,
+                                                                       native_bt.COMPONENT_CLASS_TYPE_FILTER)
+
+        if cc_ptr is None:
+            return
+
+        return bt2.component._create_generic_component_class_from_ptr(cc_ptr)
+
+    def sink_component_class(self, name):
+        utils._check_str(name)
+        cc_ptr = native_bt.plugin_get_component_class_by_name_and_type(self._ptr,
+                                                                       name,
+                                                                       native_bt.COMPONENT_CLASS_TYPE_SINK)
+
+        if cc_ptr is None:
+            return
+
+        return bt2.component._create_generic_component_class_from_ptr(cc_ptr)
+
+    def __len__(self):
+        count = native_bt.plugin_get_component_class_count(self._ptr)
+        utils._handle_ret(count, "cannot get plugin object's component class count")
+        return count
+
+    def __getitem__(self, index):
+        utils._check_uint64(index)
+
+        if index >= len(self):
+            raise IndexError
+
+        cc_ptr = native_bt.plugin_get_component_class(self._ptr, index)
+        utils._handle_ptr(cc_ptr, "cannot get plugin object's component class object")
+        return bt2.component._create_generic_component_class_from_ptr(cc_ptr)
diff --git a/bindings/python/bt2/stream.py b/bindings/python/bt2/stream.py
new file mode 100644 (file)
index 0000000..56bd510
--- /dev/null
@@ -0,0 +1,83 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.packet
+import bt2.event
+import abc
+import bt2
+
+
+def _create_from_ptr(stream_ptr):
+    if native_bt.ctf_stream_is_writer(stream_ptr):
+        import ctf_writer
+
+        cls = ctf_writer._CtfWriterStream
+    else:
+        cls = _Stream
+
+    return cls._create_from_ptr(stream_ptr)
+
+
+class _StreamBase(object._Object):
+    @property
+    def stream_class(self):
+        stream_class_ptr = native_bt.ctf_stream_get_class(self._ptr)
+        utils._handle_ptr(stream_class_ptr, "cannot get stream object's stream class object")
+        return bt2.StreamClass._create_from_ptr(stream_class_ptr)
+
+    @property
+    def name(self):
+        return native_bt.ctf_stream_get_name(self._ptr)
+
+    def __eq__(self, other):
+        if self.addr == other.addr:
+            return True
+
+        return self.name == other.name
+
+
+class _Stream(_StreamBase):
+    def create_packet(self):
+        packet_ptr = native_bt.ctf_packet_create(self._ptr)
+
+        if packet_ptr is None:
+            raise bt2.CreationError('cannot create packet object')
+
+        return bt2.packet._Packet._create_from_ptr(packet_ptr)
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            return False
+
+        return _StreamBase.__eq__(self, other)
+
+    def _copy(self):
+        return self.stream_class(self.name)
+
+    def __copy__(self):
+        return self._copy()
+
+    def __deepcopy__(self, memo):
+        cpy = self._copy()
+        memo[id(self)] = cpy
+        return cpy
diff --git a/bindings/python/bt2/stream_class.py b/bindings/python/bt2/stream_class.py
new file mode 100644 (file)
index 0000000..ce03edf
--- /dev/null
@@ -0,0 +1,279 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.field_types
+import collections.abc
+import bt2.ctf_writer
+import bt2.stream
+import copy
+import bt2
+
+
+class _EventClassIterator(collections.abc.Iterator):
+    def __init__(self, stream_class):
+        self._stream_class = stream_class
+        self._at = 0
+
+    def __next__(self):
+        if self._at == len(self._stream_class):
+            raise StopIteration
+
+        ec_ptr = native_bt.ctf_stream_class_get_event_class(self._stream_class._ptr,
+                                                            self._at)
+        utils._handle_ptr(ec_ptr, "cannot get stream class object's event class object")
+        name = native_bt.ctf_event_class_get_name(ec_ptr)
+        native_bt.put(ec_ptr)
+        utils._handle_ptr(name, "cannot get event class object's name")
+        self._at += 1
+        return name
+
+
+class StreamClass(object._Object, collections.abc.Mapping):
+    def __init__(self, name=None, id=None, packet_context_field_type=None,
+                 event_header_field_type=None, event_context_field_type=None,
+                 event_classes=None):
+        ptr = native_bt.ctf_stream_class_create(None)
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create stream class object')
+
+        super().__init__(ptr)
+
+        if name is not None:
+            self.name = name
+
+        if id is not None:
+            self.id = id
+
+        if packet_context_field_type is not None:
+            self.packet_context_field_type = packet_context_field_type
+
+        if event_header_field_type is not None:
+            self.event_header_field_type = event_header_field_type
+
+        if event_context_field_type is not None:
+            self.event_context_field_type = event_context_field_type
+
+        if event_classes is not None:
+            for event_class in event_classes:
+                self.add_event_class(event_class)
+
+    def __getitem__(self, key):
+        utils._check_str(key)
+        ec_ptr = native_bt.ctf_stream_class_get_event_class_by_name(self._ptr,
+                                                                    key)
+
+        if ec_ptr is None:
+            raise KeyError(key)
+
+        return bt2.EventClass._create_from_ptr(ec_ptr)
+
+    def __len__(self):
+        count = native_bt.ctf_stream_class_get_event_class_count(self._ptr)
+        utils._handle_ret(count, "cannot get stream class object's event class count")
+        return count
+
+    def __iter__(self):
+        return _EventClassIterator(self)
+
+    def event_class_with_id(self, id):
+        utils._check_int64(id)
+        ec_ptr = native_bt.ctf_stream_class_get_event_class_by_id(self._ptr, id)
+        utils._handle_ptr(ec_ptr, "cannot get stream class object's event class object")
+        return bt2.EventClass._create_from_ptr(ec_ptr)
+
+    def add_event_class(self, event_class):
+        utils._check_type(event_class, bt2.EventClass)
+        ret = native_bt.ctf_stream_class_add_event_class(self._ptr, event_class._ptr)
+        utils._handle_ret(ret, "cannot add event class object to stream class object's")
+
+    @property
+    def trace(self):
+        tc_ptr = native_bt.ctf_stream_class_get_trace(self._ptr)
+
+        if tc_ptr is not None:
+            return bt2.Trace._create_from_ptr(tc_ptr)
+
+    @property
+    def name(self):
+        return native_bt.ctf_stream_class_get_name(self._ptr)
+
+    @name.setter
+    def name(self, name):
+        utils._check_str(name)
+        ret = native_bt.ctf_stream_class_set_name(self._ptr, name)
+        utils._handle_ret(ret, "cannot set stream class object's name")
+
+    @property
+    def id(self):
+        id = native_bt.ctf_stream_class_get_id(self._ptr)
+
+        if utils._is_m1ull(id):
+            raise bt2.Error("cannot get stream class object's ID")
+
+        return id
+
+    @id.setter
+    def id(self, id):
+        utils._check_int64(id)
+        ret = native_bt.ctf_stream_class_set_id(self._ptr, id)
+        utils._handle_ret(ret, "cannot set stream class object's ID")
+
+    @property
+    def clock(self):
+        clock_ptr = native_bt.ctf_stream_class_get_clock(self._ptr)
+
+        if clock_ptr is None:
+            return
+
+        return bt2.ctf_writer.CtfWriterClock._create_from_ptr(clock_ptr)
+
+    @clock.setter
+    def clock(self, clock):
+        utils._check_type(clock, bt2.ctf_writer.CtfWriterClock)
+        ret = native_bt.ctf_stream_class_set_clock(self._ptr, clock._ptr)
+        utils._handle_ret(ret, "cannot set stream class object's CTF writer clock object")
+
+    @property
+    def packet_context_field_type(self):
+        ft_ptr = native_bt.ctf_stream_class_get_packet_context_type(self._ptr)
+
+        if ft_ptr is None:
+            return
+
+        return bt2.field_types._create_from_ptr(ft_ptr)
+
+    @packet_context_field_type.setter
+    def packet_context_field_type(self, packet_context_field_type):
+        packet_context_field_type_ptr = None
+
+        if packet_context_field_type is not None:
+            utils._check_type(packet_context_field_type, bt2.field_types._FieldType)
+            packet_context_field_type_ptr = packet_context_field_type._ptr
+
+        ret = native_bt.ctf_stream_class_set_packet_context_type(self._ptr,
+                                                                 packet_context_field_type_ptr)
+        utils._handle_ret(ret, "cannot set stream class object's packet context field type")
+
+    @property
+    def event_header_field_type(self):
+        ft_ptr = native_bt.ctf_stream_class_get_event_header_type(self._ptr)
+
+        if ft_ptr is None:
+            return
+
+        return bt2.field_types._create_from_ptr(ft_ptr)
+
+    @event_header_field_type.setter
+    def event_header_field_type(self, event_header_field_type):
+        event_header_field_type_ptr = None
+
+        if event_header_field_type is not None:
+            utils._check_type(event_header_field_type, bt2.field_types._FieldType)
+            event_header_field_type_ptr = event_header_field_type._ptr
+
+        ret = native_bt.ctf_stream_class_set_event_header_type(self._ptr,
+                                                               event_header_field_type_ptr)
+        utils._handle_ret(ret, "cannot set stream class object's event header field type")
+
+    @property
+    def event_context_field_type(self):
+        ft_ptr = native_bt.ctf_stream_class_get_event_context_type(self._ptr)
+
+        if ft_ptr is None:
+            return
+
+        return bt2.field_types._create_from_ptr(ft_ptr)
+
+    @event_context_field_type.setter
+    def event_context_field_type(self, event_context_field_type):
+        event_context_field_type_ptr = None
+
+        if event_context_field_type is not None:
+            utils._check_type(event_context_field_type, bt2.field_types._FieldType)
+            event_context_field_type_ptr = event_context_field_type._ptr
+
+        ret = native_bt.ctf_stream_class_set_event_context_type(self._ptr,
+                                                               event_context_field_type_ptr)
+        utils._handle_ret(ret, "cannot set stream class object's event context field type")
+
+    def __call__(self, name=None):
+        if name is not None:
+            utils._check_str(name)
+
+        stream_ptr = native_bt.ctf_stream_create(self._ptr, name)
+
+        if stream_ptr is None:
+            raise bt2.CreationError('cannot create stream object')
+
+        return bt2.stream._create_from_ptr(stream_ptr)
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        self_event_classes = list(self.values())
+        other_event_classes = list(other.values())
+        self_props = (
+            self_event_classes,
+            self.name,
+            self.id,
+            self.clock,
+            self.packet_context_field_type,
+            self.event_header_field_type,
+            self.event_context_field_type,
+        )
+        other_props = (
+            other_event_classes,
+            other.name,
+            other.id,
+            other.clock,
+            other.packet_context_field_type,
+            other.event_header_field_type,
+            other.event_context_field_type,
+        )
+        return self_props == other_props
+
+    def _copy(self, ft_copy_func, ev_copy_func):
+        cpy = StreamClass()
+        cpy.id = self.id
+        cpy.name = self.name
+        cpy.packet_context_field_type = ft_copy_func(self.packet_context_field_type)
+        cpy.event_header_field_type = ft_copy_func(self.event_header_field_type)
+        cpy.event_context_field_type = ft_copy_func(self.event_context_field_type)
+
+        for event_class in self.values():
+            cpy.add_event_class(ev_copy_func(event_class))
+
+        return cpy
+
+    def __copy__(self):
+        return self._copy(lambda ft: ft, copy.copy)
+
+    def __deepcopy__(self, memo):
+        cpy = self._copy(copy.deepcopy, copy.deepcopy)
+        memo[id(self)] = cpy
+        return cpy
diff --git a/bindings/python/bt2/trace.py b/bindings/python/bt2/trace.py
new file mode 100644 (file)
index 0000000..8ecbb38
--- /dev/null
@@ -0,0 +1,312 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import bt2.field_types
+import collections.abc
+import bt2.values
+import copy
+import bt2
+
+
+class _StreamClassIterator(collections.abc.Iterator):
+    def __init__(self, trace):
+        self._trace = trace
+        self._at = 0
+
+    def __next__(self):
+        if self._at == len(self._trace):
+            raise StopIteration
+
+        sc_ptr = native_bt.ctf_trace_get_stream_class(self._trace._ptr,
+                                                      self._at)
+        utils._handle_ptr(sc_ptr, "cannot get trace class object's stream class object")
+        id = native_bt.ctf_stream_class_get_id(sc_ptr)
+        native_bt.put(sc_ptr)
+
+        if utils._is_m1ull(id):
+            raise bt2.Error("cannot get stream class object's ID")
+
+        self._at += 1
+        return id
+
+
+class _TraceClockClassesIterator(collections.abc.Iterator):
+    def __init__(self, trace_clock_classes):
+        self._trace_clock_classes = trace_clock_classes
+        self._at = 0
+
+    def __next__(self):
+        if self._at == len(self._trace_clock_classes):
+            raise StopIteration
+
+        trace_ptr = self._trace_clock_classes._trace._ptr
+        cc_ptr = native_bt.ctf_trace_get_clock_class(trace_ptr, self._at)
+        utils._handle_ptr(cc_ptr, "cannot get trace class object's clock class")
+        name = native_bt.ctf_clock_class_get_name(cc_ptr)
+        native_bt.put(cc_ptr)
+        utils._handle_ptr(name, "cannot get clock class object's name")
+        self._at += 1
+        return name
+
+
+class _TraceClockClasses(collections.abc.Mapping):
+    def __init__(self, trace):
+        self._trace = trace
+
+    def __getitem__(self, key):
+        utils._check_str(key)
+        cc_ptr = native_bt.ctf_trace_get_clock_class_by_name(self._trace._ptr,
+                                                             key)
+
+        if cc_ptr is None:
+            raise KeyError(key)
+
+        return bt2.ClockClass._create_from_ptr(cc_ptr)
+
+    def __len__(self):
+        count = native_bt.ctf_trace_get_clock_class_count(self._trace._ptr)
+        utils._handle_ret(count, "cannot get trace class object's clock class count")
+        return count
+
+    def __iter__(self):
+        return _TraceClockClassesIterator(self)
+
+
+class _TraceEnvIterator(collections.abc.Iterator):
+    def __init__(self, trace_env):
+        self._trace_env = trace_env
+        self._at = 0
+
+    def __next__(self):
+        if self._at == len(self._trace_env):
+            raise StopIteration
+
+        trace_ptr = self._trace_env._trace._ptr
+        entry_name = native_bt.ctf_trace_get_environment_field_name(trace_ptr,
+                                                                    self._at)
+        utils._handle_ptr(entry_name, "cannot get trace class object's environment entry name")
+        self._at += 1
+        return entry_name
+
+
+class _TraceEnv(collections.abc.MutableMapping):
+    def __init__(self, trace):
+        self._trace = trace
+
+    def __getitem__(self, key):
+        utils._check_str(key)
+        value_ptr = native_bt.ctf_trace_get_environment_field_value_by_name(self._trace._ptr,
+                                                                            key)
+
+        if value_ptr is None:
+            raise KeyError(key)
+
+        return bt2.values._create_from_ptr(value_ptr)
+
+    def __setitem__(self, key, value):
+        utils._check_str(key)
+        value = bt2.create_value(value)
+        ret = native_bt.ctf_trace_set_environment_field(self._trace._ptr,
+                                                        key, value._ptr)
+        utils._handle_ret(ret, "cannot set trace class object's environment entry")
+
+    def __delitem__(self, key):
+        raise NotImplementedError
+
+    def __len__(self):
+        count = native_bt.ctf_trace_get_environment_field_count(self._trace._ptr)
+        utils._handle_ret(count, "cannot get trace class object's environment entry count")
+        return count
+
+    def __iter__(self):
+        return _TraceEnvIterator(self)
+
+
+class Trace(object._Object, collections.abc.Mapping):
+    def __init__(self, name=None, native_byte_order=None, env=None,
+                 packet_header_field_type=None, clock_classes=None,
+                 stream_classes=None):
+        ptr = native_bt.ctf_trace_create()
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create trace class object')
+
+        super().__init__(ptr)
+
+        if name is not None:
+            self.name = name
+
+        if native_byte_order is not None:
+            self.native_byte_order = native_byte_order
+
+        if packet_header_field_type is not None:
+            self.packet_header_field_type = packet_header_field_type
+
+        if env is not None:
+            for key, value in env.items():
+                self.env[key] = value
+
+        if clock_classes is not None:
+            for clock_class in clock_classes:
+                self.add_clock_class(clock_class)
+
+        if stream_classes is not None:
+            for stream_class in stream_classes:
+                self.add_stream_class(stream_class)
+
+    def __getitem__(self, key):
+        utils._check_int64(key)
+        sc_ptr = native_bt.ctf_trace_get_stream_class_by_id(self._ptr, key)
+
+        if sc_ptr is None:
+            raise KeyError(key)
+
+        return bt2.StreamClass._create_from_ptr(sc_ptr)
+
+    def __len__(self):
+        count = native_bt.ctf_trace_get_stream_class_count(self._ptr)
+        utils._handle_ret(count, "cannot get trace class object's stream class count")
+        return count
+
+    def __iter__(self):
+        return _StreamClassIterator(self)
+
+    def add_stream_class(self, stream_class):
+        utils._check_type(stream_class, bt2.StreamClass)
+        ret = native_bt.ctf_trace_add_stream_class(self._ptr, stream_class._ptr)
+        utils._handle_ret(ret, "cannot add stream class object to trace class object")
+
+    @property
+    def name(self):
+        return native_bt.ctf_trace_get_name(self._ptr)
+
+    @name.setter
+    def name(self, name):
+        utils._check_str(name)
+        ret = native_bt.ctf_trace_set_name(self._ptr, name)
+        utils._handle_ret(ret, "cannot set trace class object's name")
+
+    @property
+    def native_byte_order(self):
+        bo = native_bt.ctf_trace_get_byte_order(self._ptr)
+        utils._handle_ret(bo, "cannot get trace class object's native byte order")
+        return bo
+
+    @native_byte_order.setter
+    def native_byte_order(self, native_byte_order):
+        utils._check_int(native_byte_order)
+        ret = native_bt.ctf_trace_set_byte_order(self._ptr, native_byte_order)
+        utils._handle_ret(ret, "cannot set trace class object's native byte order")
+
+    @property
+    def env(self):
+        return _TraceEnv(self)
+
+    @property
+    def clock_classes(self):
+        return _TraceClockClasses(self)
+
+    def add_clock_class(self, clock_class):
+        utils._check_type(clock_class, bt2.ClockClass)
+        ret = native_bt.ctf_trace_add_clock_class(self._ptr, clock_class._ptr)
+        utils._handle_ret(ret, "cannot add clock class object to trace class object")
+
+    @property
+    def packet_header_field_type(self):
+        ft_ptr = native_bt.ctf_trace_get_packet_header_type(self._ptr)
+
+        if ft_ptr is None:
+            return
+
+        return bt2.field_types._create_from_ptr(ft_ptr)
+
+    @packet_header_field_type.setter
+    def packet_header_field_type(self, packet_header_field_type):
+        packet_header_field_type_ptr = None
+
+        if packet_header_field_type is not None:
+            utils._check_type(packet_header_field_type, bt2.field_types._FieldType)
+            packet_header_field_type_ptr = packet_header_field_type._ptr
+
+        ret = native_bt.ctf_trace_set_packet_header_type(self._ptr,
+                                                         packet_header_field_type_ptr)
+        utils._handle_ret(ret, "cannot set trace class object's packet header field type")
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            # not comparing apples to apples
+            return False
+
+        if self.addr == other.addr:
+            return True
+
+        self_stream_classes = list(self.values())
+        self_clock_classes = list(self.clock_classes.values())
+        self_env = {key: val for key, val in self.env.items()}
+        other_stream_classes = list(other.values())
+        other_clock_classes = list(other.clock_classes.values())
+        other_env = {key: val for key, val in other.env.items()}
+        self_props = (
+            self_stream_classes,
+            self_clock_classes,
+            self_env,
+            self.name,
+            self.native_byte_order,
+            self.packet_header_field_type,
+        )
+        other_props = (
+            other_stream_classes,
+            other_clock_classes,
+            other_env,
+            other.name,
+            other.native_byte_order,
+            other.packet_header_field_type,
+        )
+        return self_props == other_props
+
+    def _copy(self, gen_copy_func, sc_copy_func):
+        cpy = Trace()
+
+        if self.name is not None:
+            cpy.name = self.name
+
+        cpy.packet_header_field_type = gen_copy_func(self.packet_header_field_type)
+
+        for key, val in self.env.items():
+            cpy.env[key] = gen_copy_func(val)
+
+        for clock_class in self.clock_classes.values():
+            cpy.add_clock_class(gen_copy_func(clock_class))
+
+        for stream_class in self.values():
+            cpy.add_stream_class(sc_copy_func(stream_class))
+
+        return cpy
+
+    def __copy__(self):
+        return self._copy(lambda obj: obj, copy.copy)
+
+    def __deepcopy__(self, memo):
+        cpy = self._copy(copy.deepcopy, copy.deepcopy)
+        memo[id(self)] = cpy
+        return cpy
diff --git a/bindings/python/bt2/utils.py b/bindings/python/bt2/utils.py
new file mode 100644 (file)
index 0000000..52133a5
--- /dev/null
@@ -0,0 +1,113 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import bt2
+
+
+def _check_bool(o):
+    if not isinstance(o, bool):
+        raise TypeError("'{}' is not a 'bool' object".format(o.__class__.__name__))
+
+
+def _check_int(o):
+    if not isinstance(o, int):
+        raise TypeError("'{}' is not an 'int' object".format(o.__class__.__name__))
+
+
+def _check_float(o):
+    if not isinstance(o, float):
+        raise TypeError("'{}' is not a 'float' object".format(o.__class__.__name__))
+
+
+def _check_str(o):
+    if not isinstance(o, str):
+        raise TypeError("'{}' is not a 'str' object".format(o.__class__.__name__))
+
+
+def _check_type(o, expected_type):
+    if not isinstance(o, expected_type):
+        raise TypeError("'{}' is not a '{}' object".format(o.__class__.__name__,
+                                                           expected_type))
+
+
+def _is_int64(v):
+    _check_int(v)
+    return v >= -(2**63) and v <= (2**63 - 1)
+
+
+def _is_uint64(v):
+    _check_int(v)
+    return v >= 0 and v <= (2**64 - 1)
+
+
+def _check_int64(v, msg=None):
+    if not _is_int64(v):
+        if msg is None:
+            msg = 'expecting a signed 64-bit integral value'
+
+        msg += ' (got {})'.format(v)
+        raise ValueError(msg)
+
+
+def _check_uint64(v, msg=None):
+    if not _is_uint64(v):
+        if msg is None:
+            msg = 'expecting an unsigned 64-bit integral value'
+
+        msg += ' (got {})'.format(v)
+        raise ValueError(msg)
+
+
+def _is_m1ull(v):
+    return v == 18446744073709551615
+
+
+def _is_pow2(v):
+    return v != 0 and ((v & (v - 1)) == 0)
+
+
+def _check_alignment(a):
+    _check_uint64(a)
+
+
+    if not _is_pow2(a):
+        raise ValueError('{} is not a power of two'.format(a))
+
+
+def _handle_ret(ret, msg=None):
+    if int(ret) < 0:
+        if msg is None:
+            error = bt2.Error()
+        else:
+            error = bt2.Error(msg)
+
+        raise error
+
+
+def _handle_ptr(ptr, msg=None):
+    if ptr is None:
+        if msg is None:
+            error = bt2.Error()
+        else:
+            error = bt2.Error(msg)
+
+        raise error
diff --git a/bindings/python/bt2/values.py b/bindings/python/bt2/values.py
new file mode 100644 (file)
index 0000000..6af3d7a
--- /dev/null
@@ -0,0 +1,756 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import collections.abc
+import functools
+import numbers
+import math
+import abc
+import bt2
+
+
+def _handle_status(status, obj_name):
+    if status >= 0:
+        return
+
+    if status == native_bt.VALUE_STATUS_FROZEN:
+        raise bt2.FrozenError('{} value object is frozen'.format(obj_name))
+    elif status == native_bt.VALUE_STATUS_INVAL:
+        # In practice, this should never happen, because arguments
+        # should always be validated in this Python module before
+        # calling the native functions.
+        raise ValueError('unexpected invalid argument')
+    else:
+        # In practice, this should never happen, because arguments
+        # should always be validated in this Python module before
+        # calling the native functions.
+        raise RuntimeError('unexpected error')
+
+
+def _create_from_ptr(ptr):
+    if ptr is None or ptr == native_bt.value_null:
+        return
+
+    typeid = native_bt.value_get_type(ptr)
+    return _TYPE_TO_OBJ[typeid]._create_from_ptr(ptr)
+
+
+def create_value(value):
+    if value is None:
+        # null value object
+        return
+
+    if isinstance(value, _Value):
+        return value
+
+    if isinstance(value, bool):
+        return BoolValue(value)
+
+    if isinstance(value, int):
+        return IntegerValue(value)
+
+    if isinstance(value, float):
+        return FloatValue(value)
+
+    if isinstance(value, str):
+        return StringValue(value)
+
+    try:
+        return MapValue(value)
+    except:
+        pass
+
+    try:
+        return ArrayValue(value)
+    except:
+        pass
+
+    raise TypeError("cannot create value object from '{}' object".format(value.__class__.__name__))
+
+
+class _Value(object._Object, object._Freezable, metaclass=abc.ABCMeta):
+    def __init__(self, ptr):
+        super().__init__(ptr)
+
+    def __eq__(self, other):
+        if other is None:
+            # self is never the null value object
+            return False
+
+        # try type-specific comparison first
+        spec_eq = self._spec_eq(other)
+
+        if spec_eq is not None:
+            return spec_eq
+
+        if not isinstance(other, _Value):
+            # not comparing apples to apples
+            return False
+
+        # fall back to native comparison function
+        return native_bt.value_compare(self._ptr, other._ptr)
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    @abc.abstractmethod
+    def _spec_eq(self, other):
+        pass
+
+    def _handle_status(self, status):
+        _handle_status(status, self._NAME)
+
+    def _check_create_status(self, ptr):
+        if ptr is None:
+            raise bt2.CreationError('cannot create {} value object'.format(self._NAME.lower()))
+
+    def _is_frozen(self):
+        return native_bt.value_is_frozen(self._ptr)
+
+    def _freeze(self):
+        status = native_bt.value_freeze(self._ptr)
+        self._handle_status(status)
+
+
+class _BasicCopy:
+    def __copy__(self):
+        return self.__class__(self.value)
+
+    def __deepcopy__(self, memo):
+        copy = self.__copy__()
+        memo[id(self)] = copy
+        return copy
+
+
+@functools.total_ordering
+class _NumericValue(_Value, _BasicCopy):
+    @staticmethod
+    def _extract_value(other):
+        if isinstance(other, _NumericValue):
+            return other.value
+
+        if other is True or other is False:
+            return other
+
+        if isinstance(other, numbers.Integral):
+            return int(other)
+
+        if isinstance(other, numbers.Real):
+            return float(other)
+
+        if isinstance(other, numbers.Complex):
+            return complex(other)
+
+        raise TypeError("'{}' object is not a number object".format(other.__class__.__name__))
+
+    def __int__(self):
+        return int(self.value)
+
+    def __float__(self):
+        return float(self.value)
+
+    def __str__(self):
+        return str(self.value)
+
+    def __lt__(self, other):
+        if not isinstance(other, numbers.Number):
+            raise TypeError('unorderable types: {}() < {}()'.format(self.__class__.__name__,
+                                                                    other.__class__.__name__))
+
+        return self.value < float(other)
+
+    def __le__(self, other):
+        if not isinstance(other, numbers.Number):
+            raise TypeError('unorderable types: {}() <= {}()'.format(self.__class__.__name__,
+                                                                     other.__class__.__name__))
+
+        return self.value <= float(other)
+
+    def _spec_eq(self, other):
+        return
+
+    def __eq__(self, other):
+        if not isinstance(other, numbers.Number):
+            return False
+
+        return self.value == complex(other)
+
+    def __rmod__(self, other):
+        return self._extract_value(other) % self.value
+
+    def __mod__(self, other):
+        return self.value % self._extract_value(other)
+
+    def __rfloordiv__(self, other):
+        return self._extract_value(other) // self.value
+
+    def __floordiv__(self, other):
+        return self.value // self._extract_value(other)
+
+    def __round__(self, ndigits=None):
+        if ndigits is None:
+            return round(self.value)
+        else:
+            return round(self.value, ndigits)
+
+    def __ceil__(self):
+        return math.ceil(self.value)
+
+    def __floor__(self):
+        return math.floor(self.value)
+
+    def __trunc__(self):
+        return int(self.value)
+
+    def __abs__(self):
+        return abs(self.value)
+
+    def __add__(self, other):
+        return self.value + self._extract_value(other)
+
+    def __radd__(self, other):
+        return self.__add__(other)
+
+    def __neg__(self):
+        return -self.value
+
+    def __pos__(self):
+        return +self.value
+
+    def __mul__(self, other):
+        return self.value * self._extract_value(other)
+
+    def __rmul__(self, other):
+        return self.__mul__(other)
+
+    def __truediv__(self, other):
+        return self.value / self._extract_value(other)
+
+    def __rtruediv__(self, other):
+        return self._extract_value(other) / self.value
+
+    def __pow__(self, exponent):
+        return self.value ** self._extract_value(exponent)
+
+    def __rpow__(self, base):
+        return self._extract_value(base) ** self.value
+
+    def __iadd__(self, other):
+        self.value = self + other
+        return self
+
+    def __isub__(self, other):
+        self.value = self - other
+        return self
+
+    def __imul__(self, other):
+        self.value = self * other
+        return self
+
+    def __itruediv__(self, other):
+        self.value = self / other
+        return self
+
+    def __ifloordiv__(self, other):
+        self.value = self // other
+        return self
+
+    def __imod__(self, other):
+        self.value = self % other
+        return self
+
+    def __ipow__(self, other):
+        self.value = self ** other
+        return self
+
+
+class _IntegralValue(_NumericValue, numbers.Integral):
+    def __lshift__(self, other):
+        return self.value << self._extract_value(other)
+
+    def __rlshift__(self, other):
+        return self._extract_value(other) << self.value
+
+    def __rshift__(self, other):
+        return self.value >> self._extract_value(other)
+
+    def __rrshift__(self, other):
+        return self._extract_value(other) >> self.value
+
+    def __and__(self, other):
+        return self.value & self._extract_value(other)
+
+    def __rand__(self, other):
+        return self._extract_value(other) & self.value
+
+    def __xor__(self, other):
+        return self.value ^ self._extract_value(other)
+
+    def __rxor__(self, other):
+        return self._extract_value(other) ^ self.value
+
+    def __or__(self, other):
+        return self.value | self._extract_value(other)
+
+    def __ror__(self, other):
+        return self._extract_value(other) | self.value
+
+    def __invert__(self):
+        return ~self.value
+
+    def __ilshift__(self, other):
+        self.value = self << other
+        return self
+
+    def __irshift__(self, other):
+        self.value = self >> other
+        return self
+
+    def __iand__(self, other):
+        self.value = self & other
+        return self
+
+    def __ixor__(self, other):
+        self.value = self ^ other
+        return self
+
+    def __ior__(self, other):
+        self.value = self | other
+        return self
+
+
+class _RealValue(_NumericValue, numbers.Real):
+    pass
+
+
+class BoolValue(_Value, _BasicCopy):
+    _NAME = 'Boolean'
+
+    def __init__(self, value=None):
+        if value is None:
+            ptr = native_bt.value_bool_create()
+        else:
+            ptr = native_bt.value_bool_create_init(self._value_to_bool(value))
+
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+    def _spec_eq(self, other):
+        if isinstance(other, numbers.Number):
+            return self.value == bool(other)
+
+    def __bool__(self):
+        return self.value
+
+    def __str__(self):
+        return str(self.value)
+
+    def _value_to_bool(self, value):
+        if isinstance(value, BoolValue):
+            value = value.value
+
+        if not isinstance(value, bool):
+            raise TypeError("'{}' object is not a 'bool' or 'BoolValue' object".format(value.__class__))
+
+        return value
+
+    @property
+    def value(self):
+        status, value = native_bt.value_bool_get(self._ptr)
+        self._handle_status(status)
+        return value
+
+    @value.setter
+    def value(self, value):
+        status = native_bt.value_bool_set(self._ptr, self._value_to_bool(value))
+        self._handle_status(status)
+
+
+class IntegerValue(_IntegralValue):
+    _NAME = 'Integer'
+
+    def __init__(self, value=None):
+        if value is None:
+            ptr = native_bt.value_integer_create()
+        else:
+            ptr = native_bt.value_integer_create_init(self._value_to_int(value))
+
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+    def _value_to_int(self, value):
+        if not isinstance(value, numbers.Real):
+            raise TypeError('expecting a number object')
+
+        value = int(value)
+        utils._check_int64(value)
+        return value
+
+    @property
+    def value(self):
+        status, value = native_bt.value_integer_get(self._ptr)
+        self._handle_status(status)
+        return value
+
+    @value.setter
+    def value(self, value):
+        status = native_bt.value_integer_set(self._ptr, self._value_to_int(value))
+        self._handle_status(status)
+
+
+class FloatValue(_RealValue):
+    _NAME = 'Floating point number'
+
+    def __init__(self, value=None):
+        if value is None:
+            ptr = native_bt.value_float_create()
+        else:
+            value = self._value_to_float(value)
+            ptr = native_bt.value_float_create_init(value)
+
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+    def _value_to_float(self, value):
+        if not isinstance(value, numbers.Real):
+            raise TypeError("expecting a real number object")
+
+        return float(value)
+
+    @property
+    def value(self):
+        status, value = native_bt.value_float_get(self._ptr)
+        self._handle_status(status)
+        return value
+
+    @value.setter
+    def value(self, value):
+        value = self._value_to_float(value)
+        status = native_bt.value_float_set(self._ptr, value)
+        self._handle_status(status)
+
+
+@functools.total_ordering
+class StringValue(_BasicCopy, collections.abc.Sequence, _Value):
+    _NAME = 'String'
+
+    def __init__(self, value=None):
+        if value is None:
+            ptr = native_bt.value_string_create()
+        else:
+            ptr = native_bt.value_string_create_init(self._value_to_str(value))
+
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+    def _value_to_str(self, value):
+        if isinstance(value, self.__class__):
+            value = value.value
+
+        utils._check_str(value)
+        return value
+
+    @property
+    def value(self):
+        status, value = native_bt.value_string_get(self._ptr)
+        self._handle_status(status)
+        return value
+
+    @value.setter
+    def value(self, value):
+        status = native_bt.value_string_set(self._ptr, self._value_to_str(value))
+        self._handle_status(status)
+
+    def _spec_eq(self, other):
+        try:
+            return self.value == self._value_to_str(other)
+        except:
+            return
+
+    def __le__(self, other):
+        return self.value <= self._value_to_str(other)
+
+    def __lt__(self, other):
+        return self.value < self._value_to_str(other)
+
+    def __bool__(self):
+        return bool(self.value)
+
+    def __str__(self):
+        return self.value
+
+    def __getitem__(self, index):
+        return self.value[index]
+
+    def __len__(self):
+        return len(self.value)
+
+    def __iadd__(self, value):
+        curvalue = self.value
+        curvalue += self._value_to_str(value)
+        self.value = curvalue
+        return self
+
+
+class _Container:
+    def __bool__(self):
+        return len(self) != 0
+
+    def __copy__(self):
+        return self.__class__(self)
+
+    def __deepcopy__(self, memo):
+        ptr = native_bt.value_copy(self._ptr)
+
+        if ptr is None:
+            fmt = 'unexpected error: cannot deep-copy {} value object'
+            raise RuntimeError(fmt.format(self._NAME))
+
+        copy = self.__class__._create_from_ptr(ptr)
+        memo[id(self)] = copy
+        return copy
+
+    def __delitem__(self, index):
+        raise NotImplementedError
+
+
+class ArrayValue(_Container, collections.abc.MutableSequence, _Value):
+    _NAME = 'Array'
+
+    def __init__(self, value=None):
+        ptr = native_bt.value_array_create()
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+        # Python will raise a TypeError if there's anything wrong with
+        # the iterable protocol.
+        if value is not None:
+            for elem in value:
+                self.append(elem)
+
+    def _spec_eq(self, other):
+        try:
+            if len(self) != len(other):
+                # early mismatch
+                return False
+
+            for self_elem, other_elem in zip(self, other):
+                if self_elem != other_elem:
+                    return False
+
+            return True
+        except:
+            return
+
+    def __len__(self):
+        size = native_bt.value_array_size(self._ptr)
+        self._handle_status(size)
+        return size
+
+    def _check_index(self, index):
+        # TODO: support slices also
+        if not isinstance(index, numbers.Integral):
+            raise TypeError("'{}' object is not an integral number object: invalid index".format(index.__class__.__name__))
+
+        index = int(index)
+
+        if index < 0 or index >= len(self):
+            raise IndexError('array value object index is out of range')
+
+    def __getitem__(self, index):
+        self._check_index(index)
+        ptr = native_bt.value_array_get(self._ptr, index)
+
+        if ptr is None:
+            raise RuntimeError('unexpected error: cannot get array value object element')
+
+        return _create_from_ptr(ptr)
+
+    def __setitem__(self, index, value):
+        self._check_index(index)
+        value = create_value(value)
+
+        if value is None:
+            ptr = native_bt.value_null
+        else:
+            ptr = value._ptr
+
+        status = native_bt.value_array_set(self._ptr, index, ptr)
+        self._handle_status(status)
+
+    def append(self, value):
+        value = create_value(value)
+
+        if value is None:
+            ptr = native_bt.value_null
+        else:
+            ptr = value._ptr
+
+        status = native_bt.value_array_append(self._ptr, ptr)
+        self._handle_status(status)
+
+    def __iadd__(self, iterable):
+        # Python will raise a TypeError if there's anything wrong with
+        # the iterable protocol.
+        for elem in iterable:
+            self.append(elem)
+
+        return self
+
+    def __str__(self):
+        strings = []
+
+        for elem in self:
+            if isinstance(elem, StringValue):
+                strings.append(repr(elem.value))
+            else:
+                strings.append(str(elem))
+
+        return '[{}]'.format(', '.join(strings))
+
+    def insert(self, value):
+        raise NotImplementedError
+
+
+class _MapValueKeyIterator(collections.abc.Iterator):
+    def __init__(self, map_obj):
+        self._map_obj = map_obj
+        self._at = 0
+        keys_ptr = native_bt.value_map_get_keys_private(map_obj._ptr)
+
+        if keys_ptr is None:
+            raise RuntimeError('unexpected error: cannot get map value object keys')
+
+        self._keys = _create_from_ptr(keys_ptr)
+
+    def __next__(self):
+        if self._at == len(self._map_obj):
+            raise StopIteration
+
+        key = self._keys[self._at]
+        self._at += 1
+        return str(key)
+
+
+class MapValue(_Container, collections.abc.MutableMapping, _Value):
+    _NAME = 'Map'
+
+    def __init__(self, value=None):
+        ptr = native_bt.value_map_create()
+        self._check_create_status(ptr)
+        super().__init__(ptr)
+
+        # Python will raise a TypeError if there's anything wrong with
+        # the iterable/mapping protocol.
+        if value is not None:
+            for key, elem in value.items():
+                self[key] = elem
+
+    def __eq__(self, other):
+        return _Value.__eq__(self, other)
+
+    def __ne__(self, other):
+        return _Value.__ne__(self, other)
+
+    def _spec_eq(self, other):
+        try:
+            if len(self) != len(other):
+                # early mismatch
+                return False
+
+            for self_key in self:
+                if self_key not in other:
+                    return False
+
+                self_value = self[self_key]
+                other_value = other[self_key]
+
+                if self_value != other_value:
+                    return False
+
+            return True
+        except:
+            return
+
+    def __len__(self):
+        size = native_bt.value_map_size(self._ptr)
+        self._handle_status(size)
+        return size
+
+    def __contains__(self, key):
+        self._check_key_type(key)
+        return native_bt.value_map_has_key(self._ptr, key)
+
+    def _check_key_type(self, key):
+        utils._check_str(key)
+
+    def _check_key(self, key):
+        if key not in self:
+            raise KeyError(key)
+
+    def __getitem__(self, key):
+        self._check_key(key)
+        ptr = native_bt.value_map_get(self._ptr, key)
+
+        if ptr is None:
+            raise RuntimeError('unexpected error: cannot get map value object element')
+
+        return _create_from_ptr(ptr)
+
+    def __iter__(self):
+        return _MapValueKeyIterator(self)
+
+    def __setitem__(self, key, value):
+        self._check_key_type(key)
+        value = create_value(value)
+
+        if value is None:
+            ptr = native_bt.value_null
+        else:
+            ptr = value._ptr
+
+        status = native_bt.value_map_insert(self._ptr, key, ptr)
+        self._handle_status(status)
+
+    def __str__(self):
+        strings = []
+
+        for key, elem in self.items():
+            if isinstance(elem, StringValue):
+                value = repr(elem.value)
+            else:
+                value = str(elem)
+
+            strings.append('{}: {}'.format(repr(key), value))
+
+        return '{{{}}}'.format(', '.join(strings))
+
+
+_TYPE_TO_OBJ = {
+    native_bt.VALUE_TYPE_BOOL: BoolValue,
+    native_bt.VALUE_TYPE_INTEGER: IntegerValue,
+    native_bt.VALUE_TYPE_FLOAT: FloatValue,
+    native_bt.VALUE_TYPE_STRING: StringValue,
+    native_bt.VALUE_TYPE_ARRAY: ArrayValue,
+    native_bt.VALUE_TYPE_MAP: MapValue,
+}
index 3bb4a4f99421c0a099869b1ec039b0126df7e9e1..bd6f65b6d6e95d782b8f77bcb2df9a99e8e4bec6 100644 (file)
@@ -415,6 +415,8 @@ AC_CONFIG_FILES([
        bindings/Makefile
        bindings/python/Makefile
        bindings/python/babeltrace/Makefile
+       bindings/python/bt2/Makefile
+       bindings/python/bt2/__init__.py
        tests/Makefile
        tests/bin/Makefile
        tests/bin/intersection/Makefile
This page took 0.130384 seconds and 4 git commands to generate.