script.module.ipc


This addon module’s only purpose is to make the contained libraries available to other addons. If you are using service.ipcdatastore this is a required module for that to run. However, others may want utilize these directly in their own addon’s module as an import.

The lib folder for this module contains two folders:

  1. pyro4
  2. ipc

If the script.module.ipc directory is in Kodi’s addon directory, the included addon.xml should make the contents available to other addons as long as script.module.ipc is included in the other addon’s addon.xml under the ‘requires’ tag:

<requires>
  <import addon="xbmc.python" version="2.1.0"/>
  ...
  <import addon="script.module.ipc" version="0.1.0" />
</requires>

pyro4 should be imported simply as import pyro4

There are two modules in the ipc directory. These are included to set up a simple server or client using a specified host and port. Pyro4 contains the libraries needed to use a nameserver and random ports, but for simplicity sake these helper classes do not utilize a nameserver. They are meant to be imported as:

from ipc.ipcserver import IPCServer
from ipc.ipcclient import IPCClient

or however you wish. Objects can be shared from the server using the default configuration as simply as: (see below for details of the default configuration)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import os
import sys
import xbmc, xbmcaddon
from ipc.ipcserver import IPCServer

# see the note below about the following 3 lines
path_to_shared_obj = os.path.join(xbmcaddon.Addon('insert addon name here').getAddonInfo('path'),
                     'resources', 'lib')
if path_to_shared_obj not in sys.path:
    sys.path.insert(0, path_to_shared_obj)

import MyObjectToBeShared

obj = MyObjectToBeShared()
myserver = IPCServer(expose_obj=obj)
try:
    myserver.start()
except Exception as e:
    my_error_handler(e)
else:
    while not xbmc.abortRequested:
        xbmc.sleep(1000)
    myserver.stop()

Note

When starting the server, the module that contains the object to be shared is imported. To prevent potential issues with the way the server then accesses that object, I highly recommend that the path to the exposed object be placed in the PYTHONPATH by using sys.path as shown.

Warning

Under rare circumstances, if Kodi exits erroneously without calling IPCServer.stop(), the Kodi process may remain running despite the GUI being gone. If this occurs, Kodi may not be able to restart until the process is killed manually.

The manner in which the start() method is implemented allows exception trapping in the manner shown. The most common cause of exceptions, if not using a nameserver, is if the port is already in use. This raises a socket.error exception.

Once the server is running, the shared object can be used on the client, again using the default configuration as an example:

from ipc.ipcclient import IPCClient

myclient = IPCClient()
obj = myclient.get_exposed_object()
myvalue = obj.mymethod()

As can be seen in the example above, for the client, it not necessary to import the class of the exposed object. However, during initial development, it is recommended that the actual class is imported and instantiated instead of using obj = myclient.get_exposed_object() until you are sure that everything is working correctly with that object and then switching over to using the server version.

The above examples do not provide for any exception handling. For a more detailed example with handling of both client side and server side errors, see the actual python file for: ipcclientx.IPCClientX.


Classes and class methods from script.module.ipc

class ipcserver.IPCServer(expose_obj, add_on_id='', name='kodi-IPC', host='localhost', port=9099, serializer='pickle')

Bases: threading.Thread

Initializes all parameters needed to start the server using a specifically named server and port. (pyro4 allows for the use of a nameserver if desired. Details at: https://pythonhosted.org/Pyro4/index.html) Inherits from threading so that an EXTERNAL event loop can be used to exit gracefully when Kodi is shutting down by calling the stop() method. Note that if you plan to run more than one server, you should specify ‘name’ and ‘port’ to prevent conflicts and errors.

Parameters:
  • expose_obj (object or classic class) – Required. This is the python object whose methods will be exposed to the clients
  • add_on_id (str) – Optional keyword. The id of an addon which has stored server settings in its settings.xml file. This supercedes any explicit keyword assignments for name, host and port.
  • name (str) – Optional keyword. The arbitrary name used by the socket protocol for this datastore.
  • host (str) – Optional keyword. The host that will be used for the server.
  • port (int) – Optional keyword. The port for the socket used.
  • serializer (str) – Optional keyword. The serialization protocol to be used. Options: pickle, serpent, marshall, json
run()

Note that you must call .start() on the class instance to start the server in a separate thread. Do not call run() directly with IPCServer.run() or it will run in the same thread as the caller and lock when it hits daemon.requestLoop(). If port unavailable, retries 5 times with a 10ms delay between tries.

stop()

Stops the server. If the exposed object has a method called ‘close’, this is called before the server stops. There is a 50msec delays to allow any pending data to propagate.

static test_pickle(test_obj)

A convenience function that tests whether an object or instance can be pickled (serializable for default server sharing protocol).

Parameters:test_obj (object) – Required. The object to be tested
Returns:True if pickleable, False if not
Return type:bool
start()

Overrides base start() and then calls it via super. Main purpose is to provide a way to monitor for exceptions during startup from the inside loop that otherwise could not be easily raised and caught.

class ipcclient.IPCClient(add_on_id='', name='kodi-IPC', host='localhost', port=9099, datatype='pickle')

Initializes the client to use a named proxy for data communication with the server. The method ‘get_data_object’ should be invoked just before running a server based method and then destroyed promptly to prevent running out of data sockets on the server and preventing dropped connections. This can be done by dropping out of context and need not be done explicitly. See pyro4 docs at https://pythonhosted.org/Pyro4/index.html for details.

Parameters:
  • add_on_id (str) – Optional keyword. The id of an addon which has stored server settings in its settings.xml file. This supercedes any explicit eyword assignments for name, host and port.
  • name (str) – Optional keyword. Arbitrary name for the object being used, must match the name used by server
  • host (str) – Optional keyword. The resolvable name or IP address where the server is running
  • port (int) – Optional keyword. Port matching server port
  • datatype (str) – Optional keyword. Type of data transport being used options: pickle, serpent, json, marshall. Must match server.
get_exposed_object()
Returns:Retrieves a reference to the object being shared by the server via proxy as pyro4 remote object
Return type:object
server_available()

Checks to see if a connection can be made to the shared object

Returns:Return True if connection is successful, False if not
Return type:bool
static get_traceback()

Useful for exceptions that occur within the logic of the shared object. The normal system traceback will not be informative for these types of errors.

Returns:A detailed traceback of the last error
Return type:str