From: Simon Marchi Date: Mon, 23 Sep 2019 19:21:41 +0000 (-0400) Subject: bt2: make it work for Python 3.4 X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=7993562851b443afb3801e5bf2b88d674734808b bt2: make it work for Python 3.4 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 Reviewed-on: https://review.lttng.org/c/babeltrace/+/2082 Tested-by: jenkins --- diff --git a/src/bindings/python/bt2/bt2/__init__.py b/src/bindings/python/bt2/bt2/__init__.py index dfa71a87..0e4342aa 100644 --- a/src/bindings/python/bt2/bt2/__init__.py +++ b/src/bindings/python/bt2/bt2/__init__.py @@ -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): diff --git a/src/bindings/python/bt2/bt2/event_class.py b/src/bindings/python/bt2/bt2/event_class.py index b981d1e7..b165d321 100644 --- a/src/bindings/python/bt2/bt2/event_class.py +++ b/src/bindings/python/bt2/bt2/event_class.py @@ -23,7 +23,12 @@ 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) diff --git a/src/bindings/python/bt2/bt2/packet.py b/src/bindings/python/bt2/bt2/packet.py index 04ec6888..9dfff9eb 100644 --- a/src/bindings/python/bt2/bt2/packet.py +++ b/src/bindings/python/bt2/bt2/packet.py @@ -22,7 +22,12 @@ 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) diff --git a/src/bindings/python/bt2/bt2/port.py b/src/bindings/python/bt2/bt2/port.py index 94738d85..a62c3091 100644 --- a/src/bindings/python/bt2/bt2/port.py +++ b/src/bindings/python/bt2/bt2/port.py @@ -21,7 +21,12 @@ # 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): diff --git a/src/bindings/python/bt2/bt2/query_executor.py b/src/bindings/python/bt2/bt2/query_executor.py index 36edb014..2f82d16b 100644 --- a/src/bindings/python/bt2/bt2/query_executor.py +++ b/src/bindings/python/bt2/bt2/query_executor.py @@ -22,11 +22,16 @@ 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 diff --git a/src/bindings/python/bt2/bt2/stream.py b/src/bindings/python/bt2/bt2/stream.py index fe2af862..334b2fa5 100644 --- a/src/bindings/python/bt2/bt2/stream.py +++ b/src/bindings/python/bt2/bt2/stream.py @@ -23,12 +23,17 @@ 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: diff --git a/src/bindings/python/bt2/bt2/stream_class.py b/src/bindings/python/bt2/bt2/stream_class.py index 609fe3b2..1be62059 100644 --- a/src/bindings/python/bt2/bt2/stream_class.py +++ b/src/bindings/python/bt2/bt2/stream_class.py @@ -23,12 +23,17 @@ 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( diff --git a/src/bindings/python/bt2/bt2/trace.py b/src/bindings/python/bt2/bt2/trace.py index 23e5419c..b25c8b82 100644 --- a/src/bindings/python/bt2/bt2/trace.py +++ b/src/bindings/python/bt2/bt2/trace.py @@ -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):