Add initial Python bindings documentation
authorSimon Marchi <simon.marchi@efficios.com>
Thu, 26 Mar 2020 20:33:13 +0000 (16:33 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Wed, 15 Apr 2020 00:25:09 +0000 (20:25 -0400)
This initial documentation contains a home page, an installation page,
and a few examples to understand how the `bt2` package works.

Still missing: how exactly the bindings wrap libbabeltrace2 (wraping
rules, exceptions, etc.).

Changes:

`README.adoc`:
    Specify that you need Sphinx to build the Python bindings
    documentation.

`configure.ac` and `m4/check_sphinx.m4`:
    Add `--enable-python-bindings-doc` which requires
    `--enable-python-bindings`.

    This is because the Sphinx configuration file actually imports the
    `bt2` package to get the version (and, eventually, for Sphinx's
    autodoc to find docstrings within the `bt2` modules).

`doc/bindings/python/source`:
    The actual documentation's contents and configuration.

`doc/bindings/python/ext/bt2sphinxurl.py`:
    A Sphinx extension to add Babeltrace 2 manual page and other links
    of which the URL includes the project's version.

Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/3278

Backported from ba64dfcccb1f1bd7a259dc5d563ba422b8375582

Modifications:
 configure.ac:
   PPRINT_PROP_BOOL_CUSTOM -> PPRINT_PROP_BOOL
 examples.rst:
   Removed example usage for `in`:
      if 'next_comm' in event:
    This utility was introduced by
    f03b6364aec2d77bbb5ef0625cbaea8de4179f63, not backported,
    and only provides a shortcut to iterate over all `root` fields.
    User can implement their own as needed.
  Updated referenced version of lttng from 2.11 to "latest" for
  lttng-live url.

Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Change-Id: Id0f4636a5f66e98d383548bdcb894f53d31812b4
Reviewed-on: https://review.lttng.org/c/babeltrace/+/3408
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
Tested-by: jenkins <jenkins@lttng.org>
13 files changed:
README.adoc
configure.ac
doc/Makefile.am
doc/bindings/Makefile.am [new file with mode: 0644]
doc/bindings/python/Makefile.am [new file with mode: 0644]
doc/bindings/python/ext/bt2sphinxurl.py [new file with mode: 0644]
doc/bindings/python/source/common.rst [new file with mode: 0644]
doc/bindings/python/source/conf.py [new file with mode: 0644]
doc/bindings/python/source/examples.rst [new file with mode: 0644]
doc/bindings/python/source/images/basic-convert-graph.png [new file with mode: 0644]
doc/bindings/python/source/index.rst [new file with mode: 0644]
doc/bindings/python/source/installation.rst [new file with mode: 0644]
m4/check_sphinx.m4 [new file with mode: 0644]

index ef2b70c765cbd9f7370026d9ea86450228aae9a1..d89390ee0b618a3fecc72fe8ac14304a803e1c25 100644 (file)
@@ -1,7 +1,7 @@
 // Render with Asciidoctor
 
 = Babeltrace
-15 October 2019
+13 April 2020
 :btversion: 2.0
 :bt2: Babeltrace{nbsp}2
 
@@ -98,6 +98,11 @@ _**If you need the {bt2} manual pages**_::
     * https://www.methods.co.nz/asciidoc/[Asciidoc]{nbsp}≥{nbsp}8.6.8
     * https://pagure.io/xmlto[xmlto]{nbsp}≥{nbsp}0.0.25
 
+_**If you need the `bt2` Python bindings documentation**_::
+    * https://www.sphinx-doc.org/[Sphinx]{nbsp}≥{nbsp}1.3 for
+      Python{nbsp}3
+      (Debian/Ubuntu/Fedora: `python3-sphinx`)
+
 
 === Procedure
 
index 4e9ea09084ef2eb568ee12481eb34231a5196b8c..92ae7e8f9ec690687bb1664c7512680347d03398 100644 (file)
@@ -366,6 +366,14 @@ AC_ARG_ENABLE([python-bindings],
   [enable_python_bindings=unspecified]
 )
 
+# Python bindings documentation
+# Disabled by default
+AC_ARG_ENABLE([python-bindings-doc],
+  [AC_HELP_STRING([--enable-python-bindings-doc], [build the Python bindings documentation])],
+  [], dnl AC_ARG_ENABLE will fill enable_python_bindings_doc with the user choice
+  [enable_python_bindings_doc=no]
+)
+
 # Python plugins
 # Disabled by default
 AC_ARG_ENABLE([python-plugins],
@@ -413,6 +421,7 @@ AC_ARG_ENABLE([man-pages],
 
 # Set automake variables for optionnal feature conditionnals in Makefile.am
 AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS], [test "x$enable_python_bindings" = xyes])
+AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS_DOC], [test "x$enable_python_bindings_doc" = xyes])
 AM_CONDITIONAL([ENABLE_PYTHON_PLUGINS], [test "x$enable_python_plugins" = xyes])
 AM_CONDITIONAL([ENABLE_DEBUG_INFO], [test "x$enable_debug_info" = xyes])
 AM_CONDITIONAL([ENABLE_API_DOC], [test "x$enable_api_doc" = xyes])
@@ -543,6 +552,19 @@ AS_IF([test "x$enable_python_bindings" = xyes || test "x$enable_python_plugins"
   ])
 ])
 
+AS_IF([test "x$enable_python_bindings_doc" = xyes],
+  [
+    AM_CHECK_PYTHON_SPHINX([PYTHON])
+    AS_IF([test "x$PYTHON_SPHINX_EXISTS" = xno], [
+      AC_MSG_ERROR([The Sphinx package for Python 3 is required to build the Python bindings documentation])
+    ])
+
+    AS_IF([test "x$enable_python_bindings" != xyes], [
+      AC_MSG_ERROR([The Python bindings are required to build their documentation])
+    ])
+  ]
+)
+
 AS_IF([test "x$enable_debug_info" = xyes],
   [
     # Check if libelf and libdw are present
@@ -730,6 +752,8 @@ AC_CONFIG_FILES([
        doc/api/Makefile
        doc/api/libbabeltrace2/Doxyfile
        doc/api/libbabeltrace2/Makefile
+       doc/bindings/Makefile
+       doc/bindings/python/Makefile
        doc/contributing-images/Makefile
        doc/Makefile
        doc/man/asciidoc-attrs.conf
@@ -915,6 +939,8 @@ m4_popdef([build_man_pages_msg])
 
 test "x$enable_api_doc" = "xyes" && value=1 || value=0
 PPRINT_PROP_BOOL([HTML API documentation], $value)
+test "x$enable_python_bindings_doc" = "xyes" && value=1 || value=0
+PPRINT_PROP_BOOL([Python bindings documentation], $value)
 
 AS_ECHO
 PPRINT_SUBTITLE([Logging])
index c0aca530b8047d74b6ed0cd8b81b64f563a9c4dd..7047f0e5f24e644cac98c77e4922b50802808203 100644 (file)
@@ -1,4 +1,6 @@
-SUBDIRS = contributing-images man
+# SPDX-License-Identifier: MIT
+
+SUBDIRS = contributing-images man bindings
 
 if ENABLE_API_DOC
 SUBDIRS += api
diff --git a/doc/bindings/Makefile.am b/doc/bindings/Makefile.am
new file mode 100644 (file)
index 0000000..a9d7ab1
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: MIT
+
+if ENABLE_PYTHON_BINDINGS_DOC
+SUBDIRS = python
+endif
diff --git a/doc/bindings/python/Makefile.am b/doc/bindings/python/Makefile.am
new file mode 100644 (file)
index 0000000..f5d8490
--- /dev/null
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: MIT
+
+SPHINX_SOURCE_DIR = $(srcdir)/source
+SPHINX_EXT_DIR = $(srcdir)/ext
+SPHINX_BUILD_DIR = $(builddir)/build
+SPHINX_BUILD_HTML_DIR = $(SPHINX_BUILD_DIR)/html
+SPHINX_HTML_TARGET = $(SPHINX_BUILD_HTML_DIR)/index.html
+
+EXTRA_DIST = $(SPHINX_SOURCE_DIR) $(SPHINX_EXT_DIR)
+
+all-local: $(SPHINX_HTML_TARGET)
+
+PYTHON_BT2_BUILD_LIB_DIR = $(abs_top_builddir)/src/bindings/python/bt2/build/build_lib
+PP = $(PYTHON_BT2_BUILD_LIB_DIR)
+LLP = $(abs_top_builddir)/src/lib/.libs
+
+# `PATH` is used as a replacement for `LD_LIBRARY_PATH` on Windows
+# builds (Cygwin, MinGW).
+#
+# `DYLD_LIBRARY_PATH` is used a replacement for `LD_LIBRARY_PATH` on
+# macOS builds.
+SPHINXBUILD = PATH="$(LLP):$$PATH" PYTHONPATH="$(PP):$(SPHINX_EXT_DIR)" LD_LIBRARY_PATH="$(LLP)" DYLD_LIBRARY_PATH="$(LLP)" $(PYTHON) -m sphinx
+SPHINX_SRC = \
+       $(SPHINX_SOURCE_DIR)/common.rst \
+       $(SPHINX_SOURCE_DIR)/index.rst \
+       $(SPHINX_SOURCE_DIR)/installation.rst \
+       $(SPHINX_SOURCE_DIR)/examples.rst
+
+$(SPHINX_HTML_TARGET): $(SPHINX_SRC)
+       $(SPHINXBUILD) -b html -E $(SPHINX_SOURCE_DIR) $(SPHINX_BUILD_HTML_DIR)
+
+clean-local:
+       rm -rf $(SPHINX_BUILD_DIR)
diff --git a/doc/bindings/python/ext/bt2sphinxurl.py b/doc/bindings/python/ext/bt2sphinxurl.py
new file mode 100644 (file)
index 0000000..b3e2608
--- /dev/null
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
+#
+# This file is a Sphinx extension which adds the following roles:
+#
+# `bt2man`:
+#     A typical manual page reference, like `grep(1)`.
+#
+#     Example:
+#
+#         :bt2man:`grep(1)`
+#
+#     This role creates a simple inline literal node with the role's
+#     text if it's not a Babeltrace 2 manual page reference, or an
+#     external link to the corresponding online manual page (on
+#     `babeltrace.org`) with the appropriate project's version
+#     (`version` configuration entry) otherwise.
+#
+# `bt2link`:
+#     An external link with an URL in which a specific placeholder is
+#     replaced with the project's version.
+#
+#     The role's text follows the typical external link format, for
+#     example:
+#
+#         Link text <https://example.com/>
+#
+#     Any `@ver@` in the URL is replaced with the project's version
+#     (`version` configuration entry).
+#
+#     Example:
+#
+#         :bt2link:`libbabeltrace2 <https://babeltrace.org/docs/v@ver@/libbabeltrace2/>`
+
+import docutils
+import docutils.utils
+import docutils.nodes
+import re
+import functools
+
+
+def _bt2man_role(
+    bt2_version, name, rawtext, text, lineno, inliner, options=None, content=None
+):
+    # match a manual page reference
+    m = re.match(r'^([a-zA-Z0-9_.:-]+)\(([a-zA-Z0-9]+)\)$', text)
+
+    if not m:
+        msg = 'Cannot parse manual page reference `{}`'.format(text)
+        inliner.reporter.severe(msg, line=lineno)
+        return [inliner.problematic(rawtext, rawtext, msg)], [msg]
+
+    # matched manual page and volume
+    page = m.group(1)
+    vol = m.group(2)
+
+    # create nodes: `ret_node` is the node to return
+    page_node = docutils.nodes.strong(rawtext, page)
+    vol_node = docutils.nodes.inline(rawtext, '({})'.format(vol))
+    man_node = docutils.nodes.inline(rawtext, '', page_node, vol_node)
+    ret_node = docutils.nodes.literal(rawtext, '', man_node)
+
+    if page.startswith('babeltrace2'):
+        # Babeltrace 2 manual page: wrap `ret_node` with an external
+        # link node
+        url_tmpl = 'https://babeltrace.org/docs/v{ver}/man{vol}/{page}.{vol}/'
+        url = url_tmpl.format(ver=bt2_version, vol=vol, page=page)
+        ret_node = docutils.nodes.reference(
+            rawtext, '', ret_node, internal=False, refuri=url
+        )
+
+    return [ret_node], []
+
+
+def _bt2link_role(
+    bt2_version, name, rawtext, text, lineno, inliner, options=None, content=None
+):
+    # match link text and URL
+    m = re.match(r'^([^<]+) <([^>]+)>$', text)
+
+    if not m:
+        msg = 'Cannot parse link template `{}`'.format(text)
+        inliner.reporter.severe(msg, line=lineno)
+        return [inliner.problematic(rawtext, rawtext, msg)], [msg]
+
+    link_text = m.group(1)
+
+    # replace `@ver@` with the project's version
+    url = m.group(2).replace('@ver@', bt2_version)
+
+    # create and return an external link node
+    node = docutils.nodes.reference(rawtext, link_text, internal=False, refuri=url)
+    return [node], []
+
+
+def _add_roles(app):
+    # add the extension's roles; the role functions above expect the
+    # project's version as their first parameter
+    app.add_role('bt2man', functools.partial(_bt2man_role, app.config.version))
+    app.add_role('bt2link', functools.partial(_bt2link_role, app.config.version))
+
+
+def setup(app):
+    app.connect('builder-inited', _add_roles)
+    return {
+        'version': app.config.version,
+        'parallel_read_safe': True,
+    }
diff --git a/doc/bindings/python/source/common.rst b/doc/bindings/python/source/common.rst
new file mode 100644 (file)
index 0000000..812127a
--- /dev/null
@@ -0,0 +1,5 @@
+.. non-breaking space substitution
+   (see <https://docutils.sourceforge.io/docs/user/rst/quickref.html#substitution-references-and-definitions>):
+
+.. |~| unicode:: U+00A0
+   :trim:
diff --git a/doc/bindings/python/source/conf.py b/doc/bindings/python/source/conf.py
new file mode 100644 (file)
index 0000000..859ae57
--- /dev/null
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
+
+import bt2
+import re
+
+# project
+project = 'Babeltrace 2 Python bindings'
+copyright = '2020, EfficiOS, Inc'
+author = 'EfficiOS, Inc'
+release = bt2.__version__
+version = re.match(r'^\d+\.\d+', release).group(0)
+
+# index
+master_doc = 'index'
+
+# extensions
+extensions = ['bt2sphinxurl']
+
+# theme
+html_theme = 'alabaster'
diff --git a/doc/bindings/python/source/examples.rst b/doc/bindings/python/source/examples.rst
new file mode 100644 (file)
index 0000000..ec57e06
--- /dev/null
@@ -0,0 +1,902 @@
+.. include:: common.rst
+
+.. _examples:
+
+Examples
+========
+This section contains a few short and straightforward examples which
+show how to use the Babeltrace |~| 2 Python bindings.
+
+The :mod:`bt2` package provides the Babeltrace |~| 2 Python bindings.
+Note that the :mod:`babeltrace` package is part of the Babeltrace |~| 1
+project: it's somewhat out-of-date and not compatible with the
+:mod:`bt2` package.
+
+Assume that all the examples below are named :file:`example.py`.
+
+.. _examples_tcmi:
+
+Iterate trace events
+--------------------
+The most convenient and high-level way to iterate the events of one or
+more traces is with a :class:`bt2.TraceCollectionMessageIterator`
+object.
+
+A :class:`bt2.TraceCollectionMessageIterator` object roughly offers the
+same features as the ``convert`` command of the :command:`babeltrace2`
+command-line program (see the :bt2man:`babeltrace2-convert(1)` manual
+page), but in a programmatic, Pythonic way.
+
+As of Babeltrace |~| |version|, the trace collection message iterator
+class is a Python bindings-only feature: the Python code uses
+libbabeltrace2 internally, but the latter does not offer this utility as
+such.
+
+The :class:`bt2.TraceCollectionMessageIterator` interface features:
+
+* **Automatic source component (trace format) discovery**.
+
+  ``convert`` command equivalent example:
+
+  .. code-block:: text
+
+     $ babeltrace2 /path/to/my/trace
+
+* **Explicit component class instantiation**.
+
+  ``convert`` command equivalent example:
+
+  .. code-block:: text
+
+     $ babeltrace2 --component=source.my.format
+
+* **Passing initialization parameters to both auto-discovered and
+  explicitly created components**.
+
+  ``convert`` command equivalent example:
+
+  .. code-block:: text
+
+     $ babeltrace2 /path/to/my/trace --params=detailed=no \
+                   --component=source.ctf.fs \
+                   --params='inputs=["/path/to/my/trace"]'
+
+* **Trace event muxing**.
+
+  The message iterator muxes (combines) the events from multiple
+  compatible streams into a single, time-sorted sequence of events.
+
+  .. code-block:: text
+
+     $ babeltrace2 /path/to/trace1 /path/to/trace2 /path/to/trace3
+
+* **Stream intersection mode**.
+
+  ``convert`` command equivalent example:
+
+  .. code-block:: text
+
+     $ babeltrace2 /path/to/my/trace --stream-intersection
+
+* **Stream trimming with beginning and/or end times**.
+
+  ``convert`` command equivalent example:
+
+  .. code-block:: text
+
+     $ babeltrace2 /path/to/my/trace --begin=22:14:38 --end=22:15:07
+
+While the :command:`babeltrace2 convert` command creates a ``sink.text.pretty``
+component class (by default) to pretty-print events as plain text lines,
+a :class:`bt2.TraceCollectionMessageIterator` object is a Python
+iterator which makes its user a message consumer (there's no sink
+component)::
+
+    import bt2
+
+    for msg in bt2.TraceCollectionMessageIterator('/path/to/trace'):
+        if type(msg) is bt2._EventMessageConst:
+            print(msg.event.name)
+
+.. _examples_tcmi_autodisc:
+
+Discover traces
+~~~~~~~~~~~~~~~
+Pass one or more file paths, directory paths, or other strings when you
+build a :class:`bt2.TraceCollectionMessageIterator` object to let it
+automatically determine which source components to create for you.
+
+If you pass a directory path, the message iterator traverses the
+directory recursively to find traces, automatically selecting the
+appropriate source component classes to instantiate.
+
+The :class:`bt2.TraceCollectionMessageIterator` object and the
+:command:`babeltrace2 convert` CLI command share the same automatic
+component discovery algorithm. See the
+:bt2link:`Create implicit components from non-option arguments <https://babeltrace.org/docs/v@ver@/man1/babeltrace2-convert.1/#doc-comp-create-impl-non-opt>`
+section of the :bt2man:`babeltrace2-convert(1)` manual page for more
+details.
+
+The following example shows how to use a
+:class:`bt2.TraceCollectionMessageIterator` object to automatically
+discover one or more traces from a single path (file or directory). For
+each trace event, the example prints its name::
+
+    import bt2
+    import sys
+
+    # Get the trace path from the first command-line argument.
+    path = sys.argv[1]
+
+    # Create a trace collection message iterator with this path.
+    msg_it = bt2.TraceCollectionMessageIterator(path)
+
+    # Iterate the trace messages.
+    for msg in msg_it:
+        # `bt2._EventMessageConst` is the Python type of an event message.
+        if type(msg) is bt2._EventMessageConst:
+            # An event message holds a trace event.
+            event = msg.event
+
+            # Print event's name.
+            print(event.name)
+
+Run this example:
+
+.. code-block:: text
+
+   $ python3 example.py /path/to/one/or/more/traces
+
+Output example:
+
+.. code-block:: text
+
+    kmem_kmalloc
+    kmem_kfree
+    kmem_cache_alloc_node
+    block_getrq
+    kmem_kmalloc
+    block_plug
+    kmem_kfree
+    block_rq_insert
+    kmem_kmalloc
+    kmem_kfree
+    kmem_kmalloc
+    kmem_kfree
+
+The example above is simplistic; it does not catch the exceptions that
+some statements can raise:
+
+* ``bt2.TraceCollectionMessageIterator(path)`` raises an exception if
+  it cannot find any trace.
+
+* Each iteration of the loop, or, more precisely, the
+  :meth:`bt2.TraceCollectionMessageIterator.__next__` method, raises
+  an exception if there's any error during the iteration process.
+
+  For example, an internal source component message iterator can fail
+  when trying to decode a malformed trace file.
+
+.. _examples_tcmi_expl:
+
+Create explicit source components
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If `automatic source component discovery <#examples-tcmi-autodisc>`_
+doesn't work for you (for example, because the source component class
+you actually need to instantiate doesn't offer the
+``babeltrace.support-info`` query object), create explicit source
+components when you build a :class:`bt2.TraceCollectionMessageIterator`
+object.
+
+The following example builds a trace collection message iterator to
+explicitly instantiate a ``source.ctf.fs`` component class (found in the
+``ctf`` plugin). Again, for each trace event, the example prints its
+name::
+
+    import bt2
+    import sys
+
+    # Find the `ctf` plugin (shipped with Babeltrace 2).
+    ctf_plugin = bt2.find_plugin('ctf')
+
+    # Get the `source.ctf.fs` component class from the plugin.
+    fs_cc = ctf_plugin.source_component_classes['fs']
+
+    # Create a trace collection message iterator, instantiating a single
+    # `source.ctf.fs` component class with the `inputs` initialization
+    # parameter set to open a single CTF trace.
+    msg_it = bt2.TraceCollectionMessageIterator(bt2.ComponentSpec(fs_cc, {
+        # Get the CTF trace path from the first command-line argument.
+        'inputs': [sys.argv[1]],
+    }))
+
+    # Iterate the trace messages.
+    for msg in msg_it:
+        # `bt2._EventMessageConst` is the Python type of an event message.
+        if type(msg) is bt2._EventMessageConst:
+            # Print event's name.
+            print(msg.event.name)
+
+Run this example:
+
+.. code-block:: text
+
+   $ python3 example.py /path/to/ctf/trace
+
+Output example:
+
+.. code-block:: text
+
+    kmem_kmalloc
+    kmem_kfree
+    kmem_cache_alloc_node
+    block_getrq
+    kmem_kmalloc
+    block_plug
+    kmem_kfree
+    block_rq_insert
+    kmem_kmalloc
+    kmem_kfree
+    kmem_kmalloc
+    kmem_kfree
+
+The example above looks similar to the previous one using
+`automatic source component discovery <#examples-tcmi-autodisc>`_,
+but there are notable differences:
+
+* A ``source.ctf.fs`` component expects to receive the path to a
+  *single* `CTF <https://diamon.org/ctf/>`_ trace (a directory
+  containing a file named ``metadata``).
+
+  Unlike the previous example, you must pass the exact
+  :abbr:`CTF (Common Trace Format)` trace directory path, *not* a
+  parent directory path.
+
+* Unlike the previous example, the example above can only read a single
+  trace.
+
+  If you want to read multiple :abbr:`CTF (Common Trace Format)` traces
+  using explicit component class instantiation with a single trace
+  collection message iterator, you must create one ``source.ctf.fs``
+  component per trace.
+
+Note that the :class:`bt2.ComponentSpec` class offers the
+:meth:`from_named_plugin_and_component_class` convenience static method
+which finds the plugin and component class for you. You could therefore
+rewrite the trace collection message iterator creation part of the
+example above as::
+
+    # Create a trace collection message iterator, instantiating a single
+    # `source.ctf.fs` component class with the `inputs` initialization
+    # parameter set to open a single CTF trace.
+    msg_it = bt2.TraceCollectionMessageIterator(
+        bt2.ComponentSpec.from_named_plugin_and_component_class('ctf', 'fs', {
+            # Get the CTF trace path from the first command-line argument.
+            'inputs': [sys.argv[1]],
+        })
+    )
+
+.. _examples_tcmi_ev_field:
+
+Get a specific event field's value
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The :ref:`examples_tcmi_autodisc` and :ref:`examples_tcmi_expl` examples
+show that a :class:`bt2.TraceCollectionMessageIterator` iterates the
+time-sorted *messages* of one or more traces.
+
+One specific type of message is :class:`bt2._EventMessageConst`, which
+holds a trace event object.
+
+.. note::
+
+   Everything you can find in the :mod:`bt2` package is publicly
+   accessible.
+
+   Names which start with ``_`` (underscore), like
+   :class:`bt2._EventMessageConst`, indicate that you can't
+   *instantiate* such a class (you cannot call the class). However, the
+   type itself remains public so that you can use its name to check an
+   object's type:
+
+   .. code-block:: python
+
+       if type(msg) is bt2._EventMessageConst:
+           # ...
+
+   .. code-block:: python
+
+       if isinstance(field, bt2._IntegerFieldConst):
+           # ...
+
+Access an event object's field by using the event as a simple mapping
+(like a read-only :class:`dict`), where keys are field names. The field
+can belong to any part of the event (contexts or payload) and to its
+packet's context, if any::
+
+    import bt2
+    import sys
+
+    # Create a trace collection message iterator from the first
+    # command-line argument.
+    msg_it = bt2.TraceCollectionMessageIterator(sys.argv[1])
+
+    # Iterate the trace messages.
+    for msg in msg_it:
+        # `bt2._EventMessageConst` is the Python type of an event message.
+        # Only keep such messages.
+        if type(msg) is not bt2._EventMessageConst:
+            continue
+
+        # An event message holds a trace event.
+        event = msg.event
+
+        # Only check `sched_switch` events.
+        if event.name != 'sched_switch':
+            continue
+
+        # In an LTTng trace, the `cpu_id` field is a packet context field.
+        # The mapping interface of `event` can still find it.
+        cpu_id = event['cpu_id']
+
+        # Previous and next process short names are found in the event's
+        # `prev_comm` and `next_comm` fields.
+        prev_comm = event['prev_comm']
+        next_comm = event['next_comm']
+
+        # Print line, using field values.
+        msg = 'CPU {}: Switching process `{}` → `{}`'
+        print(msg.format(cpu_id, prev_comm, next_comm))
+
+The example above assumes that the traces to open are
+`LTTng <https://lttng.org/>`_ Linux kernel traces.
+
+Run this example:
+
+.. code-block:: text
+
+   $ python3 example.py /path/to/one/or/more/lttng/traces
+
+Output example:
+
+.. code-block:: text
+
+    CPU 2: Switching process `Timer` → `swapper/2`
+    CPU 0: Switching process `swapper/0` → `firefox`
+    CPU 0: Switching process `firefox` → `swapper/0`
+    CPU 0: Switching process `swapper/0` → `rcu_preempt`
+    CPU 0: Switching process `rcu_preempt` → `swapper/0`
+    CPU 3: Switching process `swapper/3` → `alsa-sink-ALC26`
+    CPU 2: Switching process `swapper/2` → `Timer`
+    CPU 2: Switching process `Timer` → `swapper/2`
+    CPU 2: Switching process `swapper/2` → `pulseaudio`
+    CPU 0: Switching process `swapper/0` → `firefox`
+    CPU 1: Switching process `swapper/1` → `threaded-ml`
+    CPU 2: Switching process `pulseaudio` → `Timer`
+
+If you need to access a specific field, use:
+
+Event payload
+  :attr:`bt2._EventConst.payload_field` property.
+
+Event specific context
+  :attr:`bt2._EventConst.specific_context_field` property.
+
+Event common context
+  :attr:`bt2._EventConst.common_context_field` property.
+
+Packet context
+  :attr:`bt2._PacketConst.context_field` property.
+
+Use Python's ``in`` operator to verify if a specific "root" field (in the list
+above) contains a given field by name::
+
+    if 'next_comm' in event.payload_field:
+           # ...
+
+The following example iterates the events of a given trace, printing the
+value of the ``fd`` payload field if it's available::
+
+    import bt2
+    import sys
+
+    # Create a trace collection message iterator from the first command-line
+    # argument.
+    msg_it = bt2.TraceCollectionMessageIterator(sys.argv[1])
+
+    # Iterate the trace messages.
+    for msg in msg_it:
+        # `bt2._EventMessageConst` is the Python type of an event message.
+        if type(msg) is bt2._EventMessageConst:
+            # Check if the `fd` event payload field exists.
+            if 'fd' in msg.event.payload_field:
+                # Print the `fd` event payload field's value.
+                print(msg.event.payload_field['fd'])
+
+Output example:
+
+.. code-block:: text
+
+    14
+    15
+    16
+    19
+    30
+    31
+    33
+    42
+    0
+    1
+    2
+    3
+
+.. _examples_tcmi_ev_time:
+
+Get an event's time
+~~~~~~~~~~~~~~~~~~~
+The time, or timestamp, of an event object belongs to its message as
+a *default clock snapshot*.
+
+An event's clock snapshot is a *snapshot* (an immutable value) of the
+value of the event's stream's clock when the event occurred. As of
+Babeltrace |~| |version|, a stream can only have one clock: its default
+clock.
+
+Use the :attr:`default_clock_snapshot` property of an event message
+to get its default clock snapshot. A clock snapshot object offers,
+amongst other things, the following properties:
+
+:attr:`value` (:class:`int`)
+  Value of the clock snapshot in clock cycles.
+
+  A stream clock can have any frequency (Hz).
+
+:attr:`ns_from_origin` (:class:`int`)
+  Number of nanoseconds from the stream clock's origin (often the Unix
+  epoch).
+
+The following example prints, for each event, its name, its date/time,
+and the difference, in seconds, since the previous event's time (if
+any)::
+
+    import bt2
+    import sys
+    import datetime
+
+    # Create a trace collection message iterator from the first command-line
+    # argument.
+    msg_it = bt2.TraceCollectionMessageIterator(sys.argv[1])
+
+    # Last event's time (ns from origin).
+    last_event_ns_from_origin = None
+
+    # Iterate the trace messages.
+    for msg in msg_it:
+        # `bt2._EventMessageConst` is the Python type of an event message.
+        if type(msg) is bt2._EventMessageConst:
+            # Get event message's default clock snapshot's ns from origin
+            # value.
+            ns_from_origin = msg.default_clock_snapshot.ns_from_origin
+
+            # Compute the time difference since the last event message.
+            diff_s = 0
+
+            if last_event_ns_from_origin is not None:
+                diff_s = (ns_from_origin - last_event_ns_from_origin) / 1e9
+
+            # Create a `datetime.datetime` object from `ns_from_origin` for
+            # presentation. Note that such an object is less accurate than
+            # `ns_from_origin` as it holds microseconds, not nanoseconds.
+            dt = datetime.datetime.fromtimestamp(ns_from_origin / 1e9)
+
+            # Print line.
+            fmt = '{} (+{:.6f} s): {}'
+            print(fmt.format(dt, diff_s, msg.event.name))
+
+            # Update last event's time.
+            last_event_ns_from_origin = ns_from_origin
+
+Run this example:
+
+.. code-block:: text
+
+   $ python3 example.py /path/to/one/or/more/traces
+
+Output example:
+
+.. code-block:: text
+
+    2015-09-09 22:40:41.551451 (+0.000004 s): lttng_ust_statedump:end
+    2015-09-09 22:40:43.003397 (+1.451946 s): lttng_ust_dl:dlopen
+    2015-09-09 22:40:43.003412 (+0.000015 s): lttng_ust_dl:build_id
+    2015-09-09 22:40:43.003861 (+0.000449 s): lttng_ust_dl:dlopen
+    2015-09-09 22:40:43.003865 (+0.000004 s): lttng_ust_dl:build_id
+    2015-09-09 22:40:43.003879 (+0.000014 s): my_provider:my_first_tracepoint
+    2015-09-09 22:40:43.003895 (+0.000016 s): my_provider:my_first_tracepoint
+    2015-09-09 22:40:43.003898 (+0.000003 s): my_provider:my_other_tracepoint
+    2015-09-09 22:40:43.003922 (+0.000023 s): lttng_ust_dl:dlclose
+
+Bonus: Print top 5 running processes using LTTng
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+As :ref:`examples_tcmi_ev_field` shows, a
+:class:`bt2.TraceCollectionMessageIterator` can read
+`LTTng <https://lttng.org/>`_ traces.
+
+The following example is similar to :ref:`examples_tcmi_ev_time`: it
+reads a whole LTTng Linux kernel trace, but instead of printing the time
+difference for each event, it accumulates them to print the short names
+of the top |~| 5 running processes on CPU |~| 0 during the whole trace.
+
+.. code-block:: python
+
+    import bt2
+    import sys
+    import collections
+
+    # Create a trace collection message iterator from the first command-line
+    # argument.
+    msg_it = bt2.TraceCollectionMessageIterator(sys.argv[1])
+
+    # This counter dictionary will hold execution times:
+    #
+    #     Task command name -> Total execution time (ns)
+    exec_times = collections.Counter()
+
+    # This holds the last `sched_switch` event time.
+    last_ns_from_origin = None
+
+    for msg in msg_it:
+        # `bt2._EventMessageConst` is the Python type of an event message.
+        # Only keep such messages.
+        if type(msg) is not bt2._EventMessageConst:
+            continue
+
+        # An event message holds a trace event.
+        event = msg.event
+
+        # Only check `sched_switch` events.
+        if event.name != 'sched_switch':
+            continue
+
+        # Keep only events which occurred on CPU 0.
+        if event['cpu_id'] != 0:
+            continue
+
+        # Get event message's default clock snapshot's ns from origin value.
+        ns_from_origin = msg.default_clock_snapshot.ns_from_origin
+
+        if last_ns_from_origin is None:
+            # We start here.
+            last_ns_from_origin = ns_from_origin
+
+        # Previous process's short name.
+        prev_comm = str(event['prev_comm'])
+
+        # Initialize an entry in our dictionary if not done yet.
+        if prev_comm not in exec_times:
+            exec_times[prev_comm] = 0
+
+        # Compute previous process's execution time.
+        diff_ns = ns_from_origin - last_ns_from_origin
+
+        # Update execution time of this command.
+        exec_times[prev_comm] += diff_ns
+
+        # Update last event's time.
+        last_ns_from_origin = ns_from_origin
+
+    # Print top 5.
+    for comm, ns in exec_times.most_common(5):
+        print('{:20}{} s'.format(comm, ns / 1e9))
+
+Run this example:
+
+.. code-block:: text
+
+   $ python3 example.py /path/to/lttng/trace
+
+Output example:
+
+.. code-block:: text
+
+    swapper/0           326.294314471 s
+    chromium            2.500456202 s
+    Xorg.bin            0.546656895 s
+    threaded-ml         0.545098185 s
+    pulseaudio          0.53677713 s
+
+Note that ``swapper/0`` is the "idle" process of CPU |~| 0 on Linux;
+since we weren't using the CPU that much when tracing, its first
+position in the list makes sense.
+
+Inspect event classes
+~~~~~~~~~~~~~~~~~~~~~
+Each event stream is a *stream class* instance.
+
+A stream class contains *event classes*. A stream class's event classes
+describe all the possible events you can find in its instances. Stream
+classes and event classes form the *metadata* of streams and events.
+
+The following example shows how to list all the event classes of a
+stream class. For each event class, the example also prints the names of
+its payload field class's first-level members.
+
+.. note::
+
+   As of Babeltrace |~| |version|, there's no way to access a stream class
+   without consuming at least one message for one of its instances
+   (streams).
+
+   A source component can add new event classes to existing stream
+   classes during the trace processing task. Therefore, this example
+   only lists the initial stream class's event classes.
+
+.. code-block:: python
+
+    import bt2
+    import sys
+
+    # Create a trace collection message iterator from the first command-line
+    # argument.
+    msg_it = bt2.TraceCollectionMessageIterator(sys.argv[1])
+
+    # Get the message iterator's first stream beginning message.
+    for msg in msg_it:
+        # `bt2._StreamBeginningMessageConst` is the Python type of a stream
+        # beginning message.
+        if type(msg) is bt2._StreamBeginningMessageConst:
+            break
+
+    # A stream beginning message holds a stream.
+    stream = msg.stream
+
+    # Get the stream's class.
+    stream_class = stream.cls
+
+    # The stream class object offers a mapping interface (like a read-only
+    # `dict`), where keys are event class IDs and values are
+    # `bt2._EventClassConst` objects.
+    for event_class in stream_class.values():
+        print('{}:'.format(event_class.name))
+
+        # The `payload_field_class` property of an event class returns a
+        # `bt2._StructureFieldClassConst` object. This object offers a
+        # mapping interface, where keys are member names and values are
+        # `bt2._StructureFieldClassMemberConst` objects.
+        for member in event_class.payload_field_class.values():
+            fmt = '  {}: `{}.{}`'
+            print(fmt.format(member.name, bt2.__name__,
+                             member.field_class.__class__.__name__))
+
+Run this example:
+
+.. code-block:: text
+
+   $ python3 example.py /path/to/trace
+
+Output example:
+
+.. code-block:: text
+
+    sched_migrate_task:
+      comm: `bt2._StringFieldClassConst`
+      tid: `bt2._SignedIntegerFieldClassConst`
+      prio: `bt2._SignedIntegerFieldClassConst`
+      orig_cpu: `bt2._SignedIntegerFieldClassConst`
+      dest_cpu: `bt2._SignedIntegerFieldClassConst`
+    sched_switch:
+      prev_comm: `bt2._StringFieldClassConst`
+      prev_tid: `bt2._SignedIntegerFieldClassConst`
+      prev_prio: `bt2._SignedIntegerFieldClassConst`
+      prev_state: `bt2._SignedIntegerFieldClassConst`
+      next_comm: `bt2._StringFieldClassConst`
+      next_tid: `bt2._SignedIntegerFieldClassConst`
+      next_prio: `bt2._SignedIntegerFieldClassConst`
+    sched_wakeup_new:
+      comm: `bt2._StringFieldClassConst`
+      tid: `bt2._SignedIntegerFieldClassConst`
+      prio: `bt2._SignedIntegerFieldClassConst`
+      target_cpu: `bt2._SignedIntegerFieldClassConst`
+
+.. _examples_graph:
+
+Build and run a trace processing graph
+--------------------------------------
+Internally, a :class:`bt2.TraceCollectionMessageIterator` object (see
+:ref:`examples_tcmi`) builds a *trace processing graph*, just like the
+:bt2man:`babeltrace2-convert(1)` CLI command, and then offers a
+Python iterator interface on top of it.
+
+See the :bt2man:`babeltrace2-intro(7)` manual page to learn more about
+the Babeltrace |~| 2 project and its core concepts.
+
+The following examples shows how to manually build and then run a trace
+processing graph yourself (like the :bt2man:`babeltrace2-run(1)` CLI
+command does). The general steps to do so are:
+
+#. Create an empty graph.
+
+#. Add components to the graph.
+
+   This process is also known as *instantiating a component class*
+   because the graph must first create the component from its class
+   before adding it.
+
+   A viable graph contains at least one source component and one sink
+   component.
+
+#. Connect component ports.
+
+   On initialization, components add input and output ports, depending
+   on their type.
+
+   You can connect component output ports to input ports within a graph.
+
+#. Run the graph.
+
+   This is a blocking operation which makes each sink component consume
+   some messages in a round robin fashion until there are no more.
+
+.. code-block:: python
+
+    import bt2
+    import sys
+
+    # Create an empty graph.
+    graph = bt2.Graph()
+
+    # Add a `source.text.dmesg` component.
+    #
+    # graph.add_component() returns the created and added component.
+    #
+    # Such a component reads Linux kernel ring buffer messages (see
+    # `dmesg(1)`) from the standard input and creates corresponding event
+    # messages. See `babeltrace2-source.text.dmesg(7)`.
+    #
+    # `my source` is the unique name of this component within `graph`.
+    comp_cls = bt2.find_plugin('text').source_component_classes['dmesg']
+    src_comp = graph.add_component(comp_cls, 'my source')
+
+    # Add a `sink.text.pretty` component.
+    #
+    # Such a component pretty-prints event messages on the standard output
+    # (one message per line). See `babeltrace2-sink.text.pretty(7)`.
+    #
+    # The `babeltrace2 convert` CLI command uses a `sink.text.pretty`
+    # sink component by default.
+    comp_cls = bt2.find_plugin('text').sink_component_classes['pretty']
+    sink_comp = graph.add_component(comp_cls, 'my sink')
+
+    # Connect the `out` output port of the `source.text.dmesg` component
+    # to the `in` input port of the `sink.text.pretty` component.
+    graph.connect_ports(src_comp.output_ports['out'],
+                        sink_comp.input_ports['in'])
+
+    # Run the trace processing graph.
+    graph.run()
+
+Run this example:
+
+.. code-block:: text
+
+   $ dmesg -t | python3 example.py
+
+Output example:
+
+.. code-block:: text
+
+    string: { str = "ata1.00: NCQ Send/Recv Log not supported" }
+    string: { str = "ata1.00: ACPI cmd ef/02:00:00:00:00:a0 (SET FEATURES) succeeded" }
+    string: { str = "ata1.00: ACPI cmd f5/00:00:00:00:00:a0 (SECURITY FREEZE LOCK) filtered out" }
+    string: { str = "ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out" }
+    string: { str = "ata1.00: NCQ Send/Recv Log not supported" }
+    string: { str = "ata1.00: configured for UDMA/133" }
+    string: { str = "ata1.00: Enabling discard_zeroes_data" }
+    string: { str = "OOM killer enabled." }
+    string: { str = "Restarting tasks ... done." }
+    string: { str = "PM: suspend exit" }
+
+Query a component class
+-----------------------
+Component classes, provided by plugins, can implement a method to
+support *query operations*.
+
+A query operation is similar to a function call: the caller makes a
+request (a query) with parameters and the component class's query
+method returns a result object.
+
+The query operation feature exists so that you can benefit from a
+component class's implementation to get information about a trace, a
+stream, a distant server, and so on. For example, the
+``source.ctf.lttng-live`` component class (see
+:bt2man:`babeltrace2-source.ctf.lttng-live(7)`) offers the ``sessions``
+object to list the available
+`LTTng live <https://lttng.org/docs/#doc-lttng-live>`_ tracing
+session names and other properties.
+
+The semantics of the query parameters and the returned object are
+completely defined by the component class implementation: the library
+and its Python bindings don't enforce or suggest any layout.
+The best way to know which objects you can query from a component class,
+what are the expected and optional parameters, and what the returned
+object contains is to read this component class's documentation.
+
+The following example queries the "standard" ``babeltrace.support-info``
+query object (see
+:bt2man:`babeltrace2-query-babeltrace.support-info(7)`) from the
+``source.ctf.fs`` component class
+(see :bt2man:`babeltrace2-source.ctf.fs(7)`) and
+pretty-prints the result. The ``babeltrace.support-info`` query object
+indicates whether or not a given path locates a
+:abbr:`CTF (Common Trace Format)` trace directory::
+
+    import bt2
+    import sys
+
+    # Get the `source.ctf.fs` component class from the `ctf` plugin.
+    comp_cls = bt2.find_plugin('ctf').source_component_classes['fs']
+
+    # The `babeltrace.support-info` query operation expects a `type`
+    # parameter (set to `directory` here) and an `input` parameter (the
+    # actual path or string to check, in this case the first command-line
+    # argument).
+    #
+    # See `babeltrace2-query-babeltrace.support-info(7)`.
+    params = {
+        'type': 'directory',
+        'input': sys.argv[1],
+    }
+
+    # Create a query executor.
+    #
+    # This is the environment in which query operations happens. The
+    # queried component class has access to this executor, for example to
+    # retrieve the query operation's logging level.
+    query_exec = bt2.QueryExecutor(comp_cls, 'babeltrace.support-info',
+                                   params)
+
+    # Query the component class through the query executor.
+    #
+    # This method returns the result.
+    result = query_exec.query()
+
+    # Print the result.
+    print(result)
+
+As you can see, no trace processing graph is involved (like in
+:ref:`examples_tcmi` and :ref:`examples_graph`): a query operation
+is *not* a sequential trace processing task, but a simple, atomic
+procedure call.
+
+Run this example:
+
+.. code-block:: text
+
+   $ python3 example.py /path/to/ctf/trace
+
+Output example:
+
+.. code-block:: text
+
+    {'group': '21c63a42-40bc-4c08-9761-3815ae01f43d', 'weight': 0.75}
+
+This result indicates that the component class is 75 |~| % confident that
+:file:`/path/to/ctf/trace` is a CTF trace directory path. It also shows
+that this specific CTF trace belongs to the
+``21c63a42-40bc-4c08-9761-3815ae01f43d`` group; a single component can
+handle multiple traces which belong to the same group.
+
+Let's try the sample example with a path that doesn't locate a CTF
+trace:
+
+.. code-block:: text
+
+   $ python3 example.py /etc
+
+Output:
+
+.. code-block:: text
+
+    {'weight': 0.0}
+
+As expected, the zero weight indicates that ``/etc`` isn't a CTF trace
+directory path.
diff --git a/doc/bindings/python/source/images/basic-convert-graph.png b/doc/bindings/python/source/images/basic-convert-graph.png
new file mode 100644 (file)
index 0000000..a97dfca
Binary files /dev/null and b/doc/bindings/python/source/images/basic-convert-graph.png differ
diff --git a/doc/bindings/python/source/index.rst b/doc/bindings/python/source/index.rst
new file mode 100644 (file)
index 0000000..2a2c8b1
--- /dev/null
@@ -0,0 +1,104 @@
+.. include:: common.rst
+
+Welcome!
+========
+Welcome to the `Babeltrace 2 <https://babeltrace.org/>`_ Python |~| 3
+bindings documentation (version |version|).
+
+.. note::
+
+   This documentation (text and illustrations) is licensed under a
+   `Creative Commons Attribution-ShareAlike 4.0 International
+   <https://creativecommons.org/licenses/by-sa/4.0/legalcode>`_ license.
+
+.. attention::
+   This documentation is **incomplete**.
+
+   In the meantime:
+
+   * See :ref:`examples` to learn how to accomplish common tasks.
+
+   * Have a look at the
+     :bt2link:`Babeltrace 2 C API documentation <https://babeltrace.org/docs/v@ver@/libbabeltrace2/>`.
+
+     The Babeltrace |~| 2 Python bindings wrap all the functionalities
+     of libbabeltrace2 in a reasonably systematic manner.
+
+**Contents**:
+
+.. toctree::
+   :maxdepth: 2
+
+   installation
+   examples
+
+What's Babeltrace 2?
+--------------------
+Babeltrace 2 is an open-source software project by
+`EfficiOS <https://www.efficios.com/>`_; its purpose
+is to process or convert
+`traces <https://en.wikipedia.org/wiki/Tracing_(software)>`_.
+
+The Babeltrace |~| 2 project contains:
+
+* A **library**,
+  :bt2link:`libbabeltrace2 <https://babeltrace.org/docs/v@ver@/libbabeltrace2/>`,
+  which all the other parts rely on.
+
+  libbabeltrace2 offers a C99 interface.
+
+* A **command-line program**, :bt2man:`babeltrace2(1)`, which can
+  convert and manipulate traces.
+
+* **Python 3 bindings** which offer a Pythonic interface of
+  libbabeltrace2.
+
+  This documentation is about those bindings.
+
+* "Standard" **plugins** which ship with the project.
+
+  `Common Trace Format <https://diamon.org/ctf/>`_ (CTF) input and
+  output,
+  :bt2link:`plain text <https://babeltrace.org/docs/v@ver@/man7/babeltrace2-plugin-text.7/>`
+  input and output, and various
+  :bt2link:`utilities <https://babeltrace.org/docs/v@ver@/man7/babeltrace2-plugin-utils.7/>`
+  are part of those plugins.
+
+With the Babeltrace |~| 2 Python bindings, you can write programs to
+do everything you can do, and more, with libbabeltrace2, that is:
+
+* Write custom source, filter, and sink *component classes* which
+  you can package as Python *plugins*.
+
+  Component classes are instantiated as components within a *trace
+  processing graph* and are assembled to accomplish a trace manipulation
+  or conversion job.
+
+* Load plugins (compiled shared object or Python modules), instantiate
+  their component classes within a trace processing graph, connect the
+  components as needed, and run the graph to accomplish a trace
+  manipulation or conversion job.
+
+  This is what the :command:`babeltrace2` CLI tool's ``convert`` and
+  ``run`` commands do, for example.
+
+A trace processing graph contains connected components. The specific
+component topology determines the trace processing task to realize.
+
+.. figure:: images/basic-convert-graph.png
+
+   A *conversion graph*, a specific trace processing graph.
+
+Between the components of a trace processing graph, *messages* flow from
+output ports to input ports following the configured connections through
+*message iterators*. There are many types of messages, chief amongst
+which is the *event message*.
+
+With the Babeltrace |~| 2 Python bindings, you can also query some
+specific object from a component class (for example, the available LTTng
+live sessions of an `LTTng <https://lttng.org/>`_ relay daemon). This is
+what the :command:`babeltrace2` CLI tool's ``query`` command does, for example.
+
+Make sure to read the :bt2man:`babeltrace2-intro(7)`
+manual page to learn even more about the Babeltrace |~| 2 project and
+its core concepts.
diff --git a/doc/bindings/python/source/installation.rst b/doc/bindings/python/source/installation.rst
new file mode 100644 (file)
index 0000000..5b0412d
--- /dev/null
@@ -0,0 +1,35 @@
+.. include:: common.rst
+
+Installation
+============
+Linux distributions
+-------------------
+Linux distributions typically provide the Babeltrace |~| 2 Python
+bindings as the ``python3-bt2`` package.
+
+Build and install from source
+-----------------------------
+When you
+`build Babeltrace 2 from source <https://babeltrace.org/#bt2-build-from-src>`_,
+specify the ``--enable-python-bindings`` option at configuration time,
+for example:
+
+.. code-block:: text
+
+   $ ./configure --enable-python-bindings
+
+If you also need the Babeltrace |~| 2 library (libbabeltrace2) to
+load Python plugins, specify the ``--enable-python-plugins`` option
+at configuration time:
+
+.. code-block:: text
+
+   $ ./configure --enable-python-bindings --enable-python-plugins
+
+See the project's
+:bt2link:`README <https://github.com/efficios/babeltrace/blob/stable-@ver@/README.adoc>`
+for build-time requirements and detailed build instructions.
+
+.. note::
+
+   The Babeltrace |~| 2 Python bindings only work with Python |~| 3.
diff --git a/m4/check_sphinx.m4 b/m4/check_sphinx.m4
new file mode 100644 (file)
index 0000000..5e9dcbc
--- /dev/null
@@ -0,0 +1,32 @@
+# check_sphinx.m4 -- check for Sphinx Python package
+#
+# Copyright (C) 2015 - Philippe Proulx <pproulx@efficios.com>
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# Sphinx ships with a script named "sphinx-build", which is usually
+# installed in "/usr/bin". Unfortunately, this script uses
+# "/usr/bin/python" as its interpreter. Since "/usr/bin/python" can
+# be either Python 2 or Python 3, depending on the distribution, and
+# since we absolutely need the Python 3 Sphinx package for Babeltrace
+# because it needs to import our bindings for autodocumentation,
+# there's no way to tell if "sphinx-build" is actually using Python 2
+# or Python 3.
+#
+# This macro checks if the Sphinx package ("sphinx") is installed
+# and visible from the interpreter designated by the PYTHON variable.
+# It sets PYTHON_SPHINX_EXISTS to "yes" if Sphinx is found for the
+# given Python interpreter, otherwise "no".
+
+# AM_CHECK_PYTHON_SPHINX(PYTHON)
+# ---------------------------------------------------------------------------
+AC_DEFUN([AM_CHECK_PYTHON_SPHINX],
+    [prog="
+try:
+    import sphinx
+    print('yes')
+except ImportError:
+    print('no')"
+    PYTHON_SPHINX_EXISTS=`${$1} -c "$prog"`])
This page took 0.041531 seconds and 4 git commands to generate.