bt2: make it work for Python 3.4
authorSimon Marchi <simon.marchi@efficios.com>
Mon, 23 Sep 2019 19:21:41 +0000 (15:21 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Wed, 25 Sep 2019 15:22:58 +0000 (11:22 -0400)
The import system from Python 3.4 has some significant differences
compared to the one from Python 3.5 and later.  Notably, support for
circular references (which we use) was added in the later versions.

For example, trace.py and stream.py both import each other.  Trace
objects need Stream objects when listing streams in a trace, and Stream
objects need Trace objects when getting the containing trace of a
stream.

To make it work on Python 3.4, I think we need to break any circular
import chain that happen when "import bt2" is done.  It is fine to
import things later, once all the modules have been initialized.

To achieve this, I changed some

  from bt2 import foo as bt2_foo

to

  def _get_bt2_foo():
      from bt2 import foo as bt2_foo
      return bt2_foo

The users of bt2_foo in that file now need to use _get_bt2_foo() instead
of bt2_foo.  it is important that _get_bt2_foo doesn't get called during
the import of the module, otherwise, we come back to the initial
problem.  So in some cases, I have wrapped some calls to _get_bt2_foo in
properties, as was already done to break circular dependencies between
classes.

This is not enough though, as there is another behavior difference
between 3.4 and 3.5+, which I believe is also a consequence of the
circular import changes [1].  It is also related to the fact that we
delete submodule attributes from the bt2 module in our __init__.py.

With 3.5, it is still possible to do:

    from bt2 import value

after we've deleted the `value` attribute from bt2.  With Python 3.4, it
doesn't work.  I believe it is related to [1] in that Python 3.5+, when
processing a "from" import, searches in sys.modules if a simple getattr
on the module failed.

This is problematic for us, since we need the 'from bt2 import "foo"'
lines to work after the bt2 attributes have been deleted (to support the
changes described above).  The solution I've found is to not delete the
bt2 submodule attributes when using Python 3.4.

We delete those attributes simply to clean up the bt2 namespace and to
avoid exposing the internal organisation of the code.  I think it's
an acceptable trade-off to not do it when using Python 3.4, if it means
that it will actually work.

[1] https://bugs.python.org/issue17636

Change-Id: Ia6810972492a058f60c21e6f22afd43962f4f7a2
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/2082
Tested-by: jenkins <jenkins@lttng.org>
src/bindings/python/bt2/bt2/__init__.py
src/bindings/python/bt2/bt2/event_class.py
src/bindings/python/bt2/bt2/packet.py
src/bindings/python/bt2/bt2/port.py
src/bindings/python/bt2/bt2/query_executor.py
src/bindings/python/bt2/bt2/stream.py
src/bindings/python/bt2/bt2/stream_class.py
src/bindings/python/bt2/bt2/trace.py

index dfa71a87032b5c4e40063f3a400696e346b19f3b..0e4342aa03537c6d57a850d2226f67017b5a7f97 100644 (file)
@@ -20,6 +20,8 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # THE SOFTWARE.
 
+import sys
+
 # import all public names
 from bt2.clock_class import ClockClassOffset
 from bt2.clock_snapshot import _ClockSnapshotConst
@@ -175,48 +177,52 @@ from bt2.value import _ArrayValueConst
 from bt2.value import _MapValueConst
 from bt2.version import __version__
 
+if (sys.version_info.major, sys.version_info.minor) != (3, 4):
+
+    def _del_global_name(name):
+        if name in globals():
+            del globals()[name]
 
-def _del_global_name(name):
-    if name in globals():
-        del globals()[name]
+    # remove private module names from the package
+    _del_global_name('_native_bt')
+    _del_global_name('clock_class')
+    _del_global_name('clock_snapshot')
+    _del_global_name('component')
+    _del_global_name('connection')
+    _del_global_name('error')
+    _del_global_name('event')
+    _del_global_name('event_class')
+    _del_global_name('field')
+    _del_global_name('field_class')
+    _del_global_name('field_path')
+    _del_global_name('graph')
+    _del_global_name('integer_range_set')
+    _del_global_name('interrupter')
+    _del_global_name('logging')
+    _del_global_name('message')
+    _del_global_name('message_iterator')
+    _del_global_name('native_bt')
+    _del_global_name('object')
+    _del_global_name('packet')
+    _del_global_name('plugin')
+    _del_global_name('port')
+    _del_global_name('py_plugin')
+    _del_global_name('query_executor')
+    _del_global_name('stream')
+    _del_global_name('stream_class')
+    _del_global_name('trace')
+    _del_global_name('trace_class')
+    _del_global_name('trace_collection_message_iterator')
+    _del_global_name('utils')
+    _del_global_name('value')
+    _del_global_name('version')
 
+    # remove private `_del_global_name` name from the package
+    del _del_global_name
 
-# remove private module names from the package
-_del_global_name('_native_bt')
-_del_global_name('clock_class')
-_del_global_name('clock_snapshot')
-_del_global_name('component')
-_del_global_name('connection')
-_del_global_name('error')
-_del_global_name('event')
-_del_global_name('event_class')
-_del_global_name('field')
-_del_global_name('field_class')
-_del_global_name('field_path')
-_del_global_name('graph')
-_del_global_name('integer_range_set')
-_del_global_name('interrupter')
-_del_global_name('logging')
-_del_global_name('message')
-_del_global_name('message_iterator')
-_del_global_name('native_bt')
-_del_global_name('object')
-_del_global_name('packet')
-_del_global_name('plugin')
-_del_global_name('port')
-_del_global_name('py_plugin')
-_del_global_name('query_executor')
-_del_global_name('stream')
-_del_global_name('stream_class')
-_del_global_name('trace')
-_del_global_name('trace_class')
-_del_global_name('trace_collection_message_iterator')
-_del_global_name('utils')
-_del_global_name('value')
-_del_global_name('version')
 
-# remove private `_del_global_name` name from the package
-del _del_global_name
+# remove sys module name from the package
+del sys
 
 
 class _MemoryError(_Error):
index b981d1e7cafbc76b02648dbf970c410fcb8befaf..b165d3211ec16d655f36a791c1fc7d34d80a5d8a 100644 (file)
 from bt2 import native_bt, object, utils
 from bt2 import field_class as bt2_field_class
 from bt2 import value as bt2_value
-from bt2 import stream_class as bt2_stream_class
+
+
+def _bt2_stream_class():
+    from bt2 import stream_class as bt2_stream_class
+
+    return bt2_stream_class
 
 
 class EventClassLogLevel:
@@ -65,7 +70,7 @@ class _EventClassConst(object._SharedObject):
     _create_value_from_ptr_and_get_ref = staticmethod(
         bt2_value._create_from_const_ptr_and_get_ref
     )
-    _stream_class_pycls = property(lambda s: bt2_stream_class._StreamClassConst)
+    _stream_class_pycls = property(lambda s: _bt2_stream_class()._StreamClassConst)
 
     @property
     def stream_class(self):
@@ -138,7 +143,7 @@ class _EventClass(_EventClassConst):
     _create_value_from_ptr_and_get_ref = staticmethod(
         bt2_value._create_from_ptr_and_get_ref
     )
-    _stream_class_pycls = property(lambda s: bt2_stream_class._StreamClass)
+    _stream_class_pycls = property(lambda s: _bt2_stream_class()._StreamClass)
 
     def _user_attributes(self, user_attributes):
         value = bt2_value.create_value(user_attributes)
index 04ec688889a8c6541c45cf338109c5b696197908..9dfff9eb54d6ba8358ac5f9033f816e55e18daf5 100644 (file)
 
 from bt2 import native_bt, object
 from bt2 import field as bt2_field
-from bt2 import stream as bt2_stream
+
+
+def _bt2_stream():
+    from bt2 import stream as bt2_stream
+
+    return bt2_stream
 
 
 class _PacketConst(object._SharedObject):
@@ -32,7 +37,7 @@ class _PacketConst(object._SharedObject):
     _borrow_context_field_ptr = staticmethod(
         native_bt.packet_borrow_context_field_const
     )
-    _stream_pycls = property(lambda _: bt2_stream._StreamConst)
+    _stream_pycls = property(lambda _: _bt2_stream()._StreamConst)
     _create_field_from_ptr = staticmethod(bt2_field._create_field_from_const_ptr)
 
     @property
@@ -56,5 +61,5 @@ class _PacketConst(object._SharedObject):
 class _Packet(_PacketConst):
     _borrow_stream_ptr = staticmethod(native_bt.packet_borrow_stream)
     _borrow_context_field_ptr = staticmethod(native_bt.packet_borrow_context_field)
-    _stream_pycls = property(lambda _: bt2_stream._Stream)
+    _stream_pycls = property(lambda _: _bt2_stream()._Stream)
     _create_field_from_ptr = staticmethod(bt2_field._create_field_from_ptr)
index 94738d850bd9d576036dee7e929858421b174845..a62c3091cb7ff62eafac95fe352464c0d3d27dfe 100644 (file)
 # THE SOFTWARE.
 
 from bt2 import native_bt, object
-from bt2 import connection as bt2_connection
+
+
+def _bt2_connection():
+    from bt2 import connection as bt2_connection
+
+    return bt2_connection
 
 
 def _create_from_const_ptr_and_get_ref(ptr, port_type):
@@ -68,7 +73,7 @@ class _PortConst(object._SharedObject):
         if conn_ptr is None:
             return
 
-        return bt2_connection._ConnectionConst._create_from_ptr_and_get_ref(conn_ptr)
+        return _bt2_connection()._ConnectionConst._create_from_ptr_and_get_ref(conn_ptr)
 
     @property
     def is_connected(self):
@@ -97,7 +102,7 @@ class _UserComponentPort(_PortConst):
         if conn_ptr is None:
             return
 
-        return bt2_connection._ConnectionConst._create_from_ptr_and_get_ref(conn_ptr)
+        return _bt2_connection()._ConnectionConst._create_from_ptr_and_get_ref(conn_ptr)
 
     @property
     def user_data(self):
index 36edb014e500a0a7573b200752a82db785ff7e2b..2f82d16b4d3baba67281c2e1d5b6830cedbf8e02 100644 (file)
 
 from bt2 import native_bt, object, utils
 from bt2 import interrupter as bt2_interrupter
-from bt2 import component as bt2_component
 from bt2 import value as bt2_value
 import bt2
 
 
+def _bt2_component():
+    from bt2 import component as bt2_component
+
+    return bt2_component
+
+
 class _QueryExecutorCommon:
     @property
     def _common_ptr(self):
@@ -50,11 +55,11 @@ class QueryExecutor(object._SharedObject, _QueryExecutorCommon):
         return self._ptr
 
     def __init__(self, component_class, object, params=None, method_obj=None):
-        if not isinstance(component_class, bt2_component._ComponentClassConst):
+        if not isinstance(component_class, _bt2_component()._ComponentClassConst):
             err = False
 
             try:
-                if not issubclass(component_class, bt2_component._UserComponent):
+                if not issubclass(component_class, _bt2_component()._UserComponent):
                     err = True
             except TypeError:
                 err = True
index fe2af8621a0421487a47de84e5f19d1ebf8081d1..334b2fa56b918c400471b30d134de42ab5d68853 100644 (file)
 from bt2 import native_bt, utils
 from bt2 import object as bt2_object
 from bt2 import packet as bt2_packet
-from bt2 import trace as bt2_trace
 from bt2 import stream_class as bt2_stream_class
 from bt2 import value as bt2_value
 import bt2
 
 
+def _bt2_trace():
+    from bt2 import trace as bt2_trace
+
+    return bt2_trace
+
+
 class _StreamConst(bt2_object._SharedObject):
     _get_ref = staticmethod(native_bt.stream_get_ref)
     _put_ref = staticmethod(native_bt.stream_put_ref)
@@ -41,7 +46,7 @@ class _StreamConst(bt2_object._SharedObject):
     )
     _borrow_trace_ptr = staticmethod(native_bt.stream_borrow_trace_const)
     _stream_class_pycls = bt2_stream_class._StreamClassConst
-    _trace_pycls = bt2_trace._TraceConst
+    _trace_pycls = property(lambda _: _bt2_trace()._TraceConst)
 
     @property
     def cls(self):
@@ -79,7 +84,7 @@ class _Stream(_StreamConst):
     )
     _borrow_trace_ptr = staticmethod(native_bt.stream_borrow_trace)
     _stream_class_pycls = bt2_stream_class._StreamClass
-    _trace_pycls = bt2_trace._Trace
+    _trace_pycls = property(lambda _: _bt2_trace()._Trace)
 
     def create_packet(self):
         if not self.cls.supports_packets:
index 609fe3b29dbb8223589c1d96ae41515eea0f1017..1be62059fef638e882545efcdd16398ca952c78c 100644 (file)
 from bt2 import native_bt, object, utils
 from bt2 import field_class as bt2_field_class
 from bt2 import event_class as bt2_event_class
-from bt2 import trace_class as bt2_trace_class
 from bt2 import clock_class as bt2_clock_class
 from bt2 import value as bt2_value
 import collections.abc
 
 
+def _bt2_trace_class():
+    from bt2 import trace_class as bt2_trace_class
+
+    return bt2_trace_class
+
+
 class _StreamClassConst(object._SharedObject, collections.abc.Mapping):
     _get_ref = staticmethod(native_bt.stream_class_get_ref)
     _put_ref = staticmethod(native_bt.stream_class_put_ref)
@@ -55,7 +60,7 @@ class _StreamClassConst(object._SharedObject, collections.abc.Mapping):
     )
 
     _event_class_cls = property(lambda _: bt2_event_class._EventClassConst)
-    _trace_class_cls = property(lambda _: bt2_trace_class._TraceClassConst)
+    _trace_class_cls = property(lambda _: _bt2_trace_class()._TraceClassConst)
     _clock_class_cls = property(lambda _: bt2_clock_class._ClockClassConst)
 
     def __getitem__(self, key):
@@ -201,7 +206,7 @@ class _StreamClass(_StreamClassConst):
     )
 
     _event_class_cls = property(lambda s: bt2_event_class._EventClass)
-    _trace_class_cls = property(lambda s: bt2_trace_class._TraceClass)
+    _trace_class_cls = property(lambda s: _bt2_trace_class()._TraceClass)
     _clock_class_cls = property(lambda s: bt2_clock_class._ClockClass)
 
     def create_event_class(
index 23e5419ca18612fb28067d2f3d94a70f9534cacd..b25c8b822a2b1e7a88a1e619a5e68fd2f585f0b2 100644 (file)
@@ -24,13 +24,18 @@ from bt2 import native_bt, object, utils
 import collections.abc
 from bt2 import value as bt2_value
 from bt2 import stream as bt2_stream
-from bt2 import trace_class as bt2_trace_class
 from bt2 import stream_class as bt2_stream_class
 import bt2
 import functools
 import uuid as uuidp
 
 
+def _bt2_trace_class():
+    from bt2 import trace_class as bt2_trace_class
+
+    return bt2_trace_class
+
+
 class _TraceEnvironmentConst(collections.abc.Mapping):
     _create_value_from_ptr_and_get_ref = staticmethod(
         bt2_value._create_from_const_ptr_and_get_ref
@@ -100,7 +105,7 @@ class _TraceConst(object._SharedObject, collections.abc.Mapping):
         bt2_value._create_from_const_ptr_and_get_ref
     )
     _stream_pycls = property(lambda _: bt2_stream._StreamConst)
-    _trace_class_pycls = property(lambda _: bt2_trace_class._TraceClassConst)
+    _trace_class_pycls = property(lambda _: _bt2_trace_class()._TraceClassConst)
     _trace_env_pycls = property(lambda _: _TraceEnvironmentConst)
 
     def __len__(self):
@@ -183,7 +188,7 @@ class _Trace(_TraceConst):
         bt2_value._create_from_ptr_and_get_ref
     )
     _stream_pycls = property(lambda _: bt2_stream._Stream)
-    _trace_class_pycls = property(lambda _: bt2_trace_class._TraceClass)
+    _trace_class_pycls = property(lambda _: _bt2_trace_class()._TraceClass)
     _trace_env_pycls = property(lambda _: _TraceEnvironment)
 
     def _name(self, name):
This page took 0.030978 seconds and 4 git commands to generate.