10.5. Migrations between different Versions
10.5.1. Towards ln 2.x
10.5.1.1. Standard Message Definitions
10.5.1.1.1. ln/pyservice
ln/pyservice was changed and renamed to ln2/pyservice. see ln/pyobject below.
10.5.1.1.2. 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)
10.5.1.1.3. 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.
10.5.1.2. Python Binding
The python binding can now be used with python3. see Python3 Migration.
10.5.1.3. Deprecated config-directives
10.5.1.3.1. Instance flag use_old_arch_names
This flag was not used for anything.
10.5.1.4. Deprecated API that was removed
10.5.1.4.1. ln_wait_for_service_requests() and ln_handle_service_requests()
// 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:
// 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:
# 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)
10.5.1.4.2. 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.
// old, removed API:
ln_enable_async_service_handling(clnt, 1);
// new:
ln_handle_service_group_in_thread_pool(clnt, NULL, "main thread pool");
10.5.1.4.3. ln::client::unregister_service_provider()
(python binding: ln.client.unregister_service_provider())
// 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.
10.5.1.4.4. 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.
10.5.2. Towards LN 1.3.3
10.5.2.1. API changes
10.5.2.1.1. 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.
# 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:
process old, pygtk using program
add environment: LN_WANT_PYGTK=1
command: python2 ...
add flags: forward_x11
...
Example in the conanfile of your program:
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:
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.
10.5.2.1.1.1. 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 "<stdin>", line 1, in <module>
File "/opt/python/osl153-x86_64/python2/stable/1.4.4/lib/python2.7/site-packages/gi/__init__.py", line 39, in <module>
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.
10.5.3. Towards LN 1.2.3
10.5.3.1. API Changes
10.5.3.1.1. 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.