Migrations between different Versions ************************************* .. contents:: :local: :depth: 3 Towards ln 2.x ============== Standard Message Definitions ---------------------------- ln/pyservice ^^^^^^^^^^^^ ``ln/pyservice`` was changed and renamed to ``ln2/pyservice``. see ``ln/pyobject`` below. ln/pyobject ^^^^^^^^^^^ ``ln/pyobject`` was changed and renamed to ``ln2/pyobject``. the ``data`` field changed its data-type from ``char*`` to ``uint8_t*`` as it is often used to transport a binary python pickle of some object (if ``is_pickle == 1``). in case ``is_pickle == 0`` the ``data`` field will transport an utf-8 encoded string of a python-repr of that object. To address interoperability issues between different versions of LN and pickled pyobjects, you can set the ``LN_PYOBJECT_PICKLE_PROTOCOL`` environment variable. Supported pickle protocols range from ``0`` to ``5``, with the default being protocol ``4`` (introduced in Python 3.4). Protocols ``>3`` are incompatible with earlier versions. To use the highest supported protocol version, specify a negative protocol value. (from ``help(pickle)`` for py3.9) ln/file_services/{write,read_from}_file ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ These message definitions used ``char*`` fields to transport possibly binary data. they were fixed and renamed to ``ln/file_services2/{write,read_from}_file`` respectively. Python Binding -------------- The python binding can now be used with python3. see :ref:`python3_migration-label`. Deprecated config-directives ---------------------------- Instance flag ``use_old_arch_names`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This flag was not used for anything. Deprecated API that was removed ------------------------------- .. contents:: :local: ``ln_wait_for_service_requests()`` and ``ln_handle_service_requests()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: C // old, removed API: while(keep_running) { if(ln_wait_for_service_requests(clnt, 1)) ln_handle_service_requests(clnt); // handles all pending service requests } // new: while(keep_running) ln_wait_and_handle_service_group_requests(clnt, NULL, 1); // every second we check `keep_running` C++ binding: .. code-block:: C++ // old, removed API: while(true) if(clnt.wait_for_service_requests()) clnt.handle_service_requests(); // new: while(true) clnt.wait_and_handle_service_group_requests(std::nullptr); Python binding: .. code-block:: python # old, removed API: while True: if self.clnt.wait_for_service_requests(): # (block until request) self.clnt.handle_service_requests() # new: while True: self.clnt.wait_and_handle_service_group_requests() # or: self.clnt.wait_and_handle_service_group_requests(None) # or: self.clnt.wait_and_handle_service_group_requests(None, -1) ``ln_enable_async_service_handling()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (C++ binding: ``ln::client::enable_async_service_handling()``) This removed API enabled service request handling from the LN client's internal async thread. Do not use this service-handling model in new code. Use explicit direct handling with ``ln_wait_and_handle_service_group_requests()`` when requests should be handled serially by the caller, or use ``ln_handle_service_group_in_thread_pool()`` when handlers should run in a service-group thread pool. .. code-block:: C // old, removed API: ln_enable_async_service_handling(clnt, 1); // new: ln_handle_service_group_in_thread_pool(clnt, NULL, "main thread pool"); ``ln::client::unregister_service_provider()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (python binding: ``ln.client.unregister_service_provider()``) .. code-block:: C++ // old, removed API: clnt.unregister_service_provider(time_service); // new: clnt.release_service(time_service); The old function was used in ln_generate-generated header files. But it was always discoraged to distribute those generated headers. Users should generate those headers with their buildsystem for whatever messages they want to use. ``WITH_DEPRECATED_GET_TIME`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ User of ``ln.h`` could define that macro to get a ``get_time()`` function. Instead use `ln_get_time()` for wall time or `ln_get_monotonic_time()` for a monotonic time source. Towards LN 1.3.3 ================ API changes ----------- ``lnrpc_{client, server}`` & ``gtk_multi_waiter`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ These are submodules from the ``links_and_nodes``-python package which were designed to work with PyGTK (old gtk2 wrapper). They are still available, but no longer unconditionally imported into the `links_and_nodes` namespace. .. code-block:: python # old, removed API: import links_and_nodes as ln ... multi_waiter = ln.gtk_multi_waiter(...) my_lnrpc_client = ln.lnrpc_client(...) # new: import links_and_nodes as ln from links_and_nodes.gtk_multi_waiter import * from links_and_nodes.lnrpc_client import * ... multi_waiter = gtk_multi_waiter(...) my_lnrpc_client = lnrpc_client(...) To ease transition for this backward-incompatible change, you can instead set the ``LN_WANT_PYGTK`` environment variable to ``1`` before doing ``import links_and_nodes`` to get the old behavior (which will no longer work with LN versions >= 2). Example in an ln-config file: .. code-block:: lnc process old, pygtk using program add environment: LN_WANT_PYGTK=1 command: python2 ... add flags: forward_x11 ... Example in the conanfile of your program: .. code-block:: python from conans import ConanFile class my_program(ConanFile): ... requires = [ "links_and_nodes_python/[~1.3]@common/stable", ... ] ... def package_info(self): self.env_info.LN_WANT_PYGTK = "1" Instead of this PyGTK implementation, for new code you should try to use a combination of ``links_and_nodes.MainloopMultiWaiter`` and some implementation of ``links_and_nodes_base.MainloopInterface`` -- when using Gtk3 you could use ``links_and_nodes_base.GLibMainloop`` as shown here: .. code-block:: python import sys import gi gi.require_version('GLib', '2.0') from gi.repository import GLib import links_and_nodes as ln class glib_subscriber_test_app: def __init__(self, mainloop): self.clnt = ln.client("glib_subscribe", sys.argv) self.port1 = self.clnt.subscribe("topic1", "uint32_t") self.port2 = self.clnt.subscribe("topic2", "double") self.multi_waiter = ln.MainloopMultiWaiter(self.clnt, mainloop) self.multi_waiter.add_port(self.port1, self.on_port1) self.multi_waiter.add_port(self.port2, self.on_port2, "test arg1", 42) def on_port1(self, port): print("port1 did publish %r" % port.packet.data) return True # return True to keep receiving updates def on_port2(self, port, arg1, arg2): print("port2 did publish %r" % port.packet.value) return True if __name__ == "__main__": mainloop = ln.GLibMainloop() # implements ln.MainloopInterface for GLib app = glib_subscriber_test_app(mainloop) print("ready") mainloop.run() or, if you don't want to use Gtk at all, you could use ``links_and_nodes_base.SelectMainloop`` or create your own ``MainloopInterface`` implementation. Why this breaking Change? """"""""""""""""""""""""" Because the version jump to LN 1.3 was intended to enable use of the new Gtk3 (via gobject-introspection, like ``import gi; from gi.repository import Gtk``). The fact that ``import gobject`` is done when importing ``links_and_nodes`` represents a bug introduced when LN version 1.3.0 was released. The problem is that it is not allowed to import old PyGTK modules and the new Gtk3 modules into the same python interpreter:: >>> import gtk >>> import gi; from gi.repository import Gtk Traceback (most recent call last): File "", line 1, in File "/opt/python/osl153-x86_64/python2/stable/1.4.4/lib/python2.7/site-packages/gi/__init__.py", line 39, in raise ImportError(_static_binding_error) ImportError: When using gi.repository you must not import static modules like "gobject". Please change all occurrences of "import gobject" to "from gi.repository import GObject". See: https://bugzilla.gnome.org/show_bug.cgi?id=709183 old, PyGtk modules include ``gtk``, ``gdk``, ``gobject``, ``glib`` -- see `Porting from PyGTK 2 to PyGI GTK 3 `_ for more information on how to port your program. Towards LN 1.2.3 ================ API Changes ----------- flat-md ^^^^^^^ there is a new ``ln_get_message_definition_v21()`` which provides the "flat-md"-representation that is used by ``links_and_nodes_simulink/ln_publish_subscribe_sfun``. C++ programs can simply use the new member-overload ``ln::client::get_message_definition()`` with 5 arguments. python ``ln.client.get_message_definition()`` now returns the 4-tuple ``md, msg_size, hash, flat_md``.