Python as an Erlang Distributed Node

Erlang is a superb option for developers for exploiting multiple processors or cores(OpenSouce for You, December 2013). It is easy to create processes in Erlang which communicate with each other using asynchronous communication. Erlang provides libraries for C and Java for communicating with Erlang as if the C or Java application were a distributed node in the Erlang universe. Of course, it is more likely that you already have a C or a Java application and would like to exploit the advantages of Erlang for enhancing the functionality of your existing application.

In this article, you will explore how you can use py_interface (http://www.lysator.liu.se/~tab/erlang/py_interface) to integrate a Python application with an Erlang application. As an illustration, you will use the example of concurrent programming in http://www.erlang.org/doc/getting_started/conc_prog.html and create a Python client which communicates with the Erlang messenger server.

The Erlang server receives a message for a logon, logoff or a send message request. The Erlang code would be as follows where the User_List contains the names of the signed in users and their addresses:

server(User_List) ->

  receive

   {From, logon, Name} ->

    New_User_List = server_logon(From, Name, User_List),

    server(New_User_List);

   {From, logoff} ->

    New_User_List = server_logoff(From, User_List),

    server(New_User_List);

   {From, message_to, To, Message} ->

    server_transfer(From, To, Message, User_List),

    server(User_List)

   end.

The above code calls various Erlang functions to do the work. Since the code is available on the Erlang website and the focus is on Python integrating with Erlang, minimal Erlang code is included here.

Your Python code will first have to become an Erlang node and listen for any incoming events. The comments in the code explain the steps needed.

import types

from py_interface import erl_common

from py_interface import erl_term

from py_interface import erl_node

from py_interface import erl_opts

from py_interface import erl_eventhandler

def main():

  global mb

  hostName = "netbook"

  ownNodeName = "pyclient"

  cookie = "erlang.cookie"

  #Create Erl node and Publish it

  n = erl_node.ErlNode(ownNodeName, erl_opts.ErlNodeOpts(cookie=cookie))

  n.Publish()

  # Create a mailbox for receiving messages

  mb = n.CreateMBox(MBoxCallback)

  # Register mbox with a symbolic name - not used in this example

  mb.RegisterName("Py")

  # Create and start an eventhandler loop

  evhand = erl_eventhandler.GetEventHandler()

  timeout = 1

  # Add a timer event for the logon message to be sent

  evhand.AddTimerEvent(timeout, logon_callback)

  evhand.Loop()


The above code includes a timeout event after 1 sec which will call the function to send the logon request. It would be preferable to create two separate threads/processes for incoming and outgoing messages. However, the idea here is to focus on the py_interface functions only. A simple timeout illustrates the py_interface functions just as well.

The logon message will be a tuple (not a list) with three terms –

  • Sender's id – the process id or the tuple of registration name and the node name

  • Logon Erlang token

  • Name – Erlang token for consistency

Since there is no corresponding data type in Python for an Erlang token, py_interface contains a function to convert a string to an Erlang token. In the code below, Python client signs on with the name 'python'. The 'mb.Self()' function returns the process id. The Erlang server is running on the node 'messenger@netbook' with the registered name 'messenger'.

def logon_callback():

  global mb

  msg=(mb.Self(), erl_term.ErlAtom('logon'), erl_term.ErlAtom('python'))

  mb.Send(('messenger','messenger@netbook'), msg)


A message you send to the messenger for another user will be a tuple with 4 elements -

  • Sender's id

  • Erlang token 'message_to'

  • Name of the person for whom the message is intended

  • Content of the message

However, the format of the message you receive will be different as you can see from the Erlang code. Server transfer validates the sender, then it validates the recipient and sends a message to the recipient and a confirmation message to the sender.

server_transfer(From, To, Message, User_List) ->

  case lists:keysearch(From, 1, User_List) of

   false ->

    From ! {messenger, stop, you_are_not_logged_on};

   {value, {From, Name}} ->

    server_transfer(From, Name, To, Message, User_List)

   end.


server_transfer(From, Name, To, Message, User_List) ->

  case lists:keysearch(To, 2, User_List) of

   false ->

    From ! {messenger, receiver_not_found};

   {value, {ToPid, To}} ->

    ToPid ! {message_from, Name, Message},

    From ! {messenger, sent}

   end.


The message you receive will be a tuple with 3 elements -

  • Sender's id

  • Sender's name

  • The content of the message.

The message will be received by the MBoxCallback. In the simple example below, you receive the message and send a canned message back to the sender.

def MBoxCallback(msg, *k, **kw):

  global mb

  if len(msg) > 2:

  # send back a vacation message to the sender

    m = (mb.Self(), erl_term.ErlAtom('message_to'), msg[1], "on vacation")

    mb.Send(('messenger','messenger@netbook'), m)

  else:

    print "Incoming msg=%s (k=%s, kw=%s)" % (`msg`, `k`, `kw`)

You can learn more by looking at the examples included with the py_interface distribution. The usual doc string documentation is available in the py_interface modules.

So, in case, a part of your application could make use of parallel processing, you may integrate your existing Python (or C or Java) application with Erlang. It is well worth your time to explore this option.

Jan 14

Comments