ivy package

Submodules

ivy.ivy module

Using an IvyServer

The following code is a typical example of use:

Implementation details

An Ivy client is made of several threads:

  • an IvyServer instance
  • a UDP server, launched by the Ivy server, listening to incoming UDP broadcast messages
  • IvyTimer objects
group Messages types:
 BYE, ADD_REGEXP, MSG, ERROR, DEL_REGEXP, END_REGEXP, END_INIT, START_REGEXP, START_INIT, DIRECT_MSG, DIE
group Separators:
 ARG_START, ARG_END
group Misc. constants:
 DEFAULT_IVYBUS, PROTOCOL_VERSION, IVY_SHOULD_NOT_DIE IvyApplicationConnected, IvyApplicationDisconnected, DEFAULT_TTL
group Objects and functions related to logging:
 ivylogger, debug, log, warn, error, ivy_loghdlr, ivy_logformatter

Copyright (c) 2005-2011 Sebastien Bigaret <sbigaret@users.sourceforge.net>

class ivy.ivy.IvyClient(ip, port, client_socket, agent_id=None, agent_name=None)[source]

Represents a client connected to the bus. Every callback methods registered by an agent receive an object of this type as their first parameter, so that they know which agent on the bus is the cause of the event which triggered the callback.

An IvyClient is responsible for:

  • managing the remote agent’s subscriptions,
  • sending messages to the remote agent.

It is not responsible for receiving messages from the client: another object is in charge of that, namely an IvyHandler object.

The local IvyServer creates one IvyClient per agent on the bus.

See the discussion in regexps.

Group Protocol-related methods:
 

start_init, end_init, send_new_subscription, remove_subscription, wave_bye

Group Manipulating the remote agent’s subscriptions:
 

add_regexp, remove_regexp

Group Sending messages:
 

send_msg, send_direct_message, send_die_message

Variables:
  • regexps – a dictionary mapping subscriptions’ ids (as delivered by add_regexp) to the corresponding regular expressions. Precisely, it maps ids to tuples being (regexp_as_string, compiled_regexp). You shouldn’t directly access or manipulate this variable, use add_regexp and remove_regexp instead; however if you really need/want to, you must acquire the regexp_lock before accessing/modifying it.
  • regexp_lock – a non-reentrant lock protecting the variable regexps. Used by methods add_regexp, remove_regexp and send_msg.
add_regexp(regexp_id, regexp)[source]
Raises IvyIllegalStateError:
 if the client has not been fully initialized yet (see start_init)
end_init()[source]

Should be called when the initialization process ends.

Raises IvyIllegalStateError:
 if the method has already been called (and self.status has already been set to INITIALIZED)
get_regexps()[source]
remove_regexp(regexp_id)[source]

Removes a regexp

Returns:

the regexp that has been removed

Raises:
  • IvyIllegalStateError – if the client has not been fully initialized yet (see start_init)
  • KeyError – if no such subscription exists
remove_subscription(idx)[source]

Notifies the remote agent that we (the local agent) are not interested in a given subscription.

Parameters:
  • idx: the index/id of a subscription previously registered with send_new_subscription.
send_die_message(num_id=0, msg='')[source]

Sends a die message

send_direct_message(num_id, msg)[source]

Sends a direct message

Note: the message will be encoded by encode_message with numerical_id=num_id and params==msg; this means that if msg is not a string but a list or a tuple, the direct message will contain more than one parameter. This is an extension of the original Ivy design, supported by python, but if you want to inter-operate with applications using the standard Ivy API the message you send must be a string. See in particular in ivy.h:

typedef void (*MsgDirectCallback)( IvyClientPtr app, void *user_data, int id, char *msg ) ;
send_error(num_id, msg)[source]

Sends an error message

send_msg(msg)[source]

Sends the message to the client. The client will receive one message for each of its subscriptions (regexps) that the message matches.

Returns:True if the message was actually sent to the client, that is: if there is one or more regexps matching the message in the client’s subscriptions; returns False otherwise.
send_new_subscription(idx, regexp)[source]

Notifies the remote agent that we (the local agent) subscribe to a new type of messages

Parameters:
  • idx: the index/id of the new subscription. It is the responsibility of the local agent to make sure that every subscription gets a unique id.
  • regexp: a regular expression. The subscription consists in receiving messages matching the regexp.
start_init(agent_name)[source]

Finalizes the initialization process by setting the client’s agent_name. This is a Ivy protocol requirement that an application sends its agent-name only once during the initial handshake (beginning with message of type START_INIT and ending with a type END_INIT). After this method is called, we expect to receive the initial subscriptions for that client (or none); the initialization process completes after end_init is called.

Raises IvyIllegalStateError:
 if the method has already been called once
wave_bye(num_id=0)[source]

Notifies the remote agent that we are about to quit

class ivy.ivy.IvyHandler(request, client_address, server)[source]

Bases: SocketServer.StreamRequestHandler

An IvyHandler is associated to one IvyClient connected to our server.

It runs into a dedicate thread as long as the remote client is connected to us.

It is in charge of examining all messages that are received and to take any appropriate actions.

Implementation note: the IvyServer is accessible in self.server

handle()[source]
process_ivymessage(msg, client)[source]

Examines the message (after passing it through the decode_msg() filter) and takes the appropriate actions depending on the message types. Please refer to the document The Ivy Architecture and Protocol and to the python code for further details.

Parameters:
  • msg: (should not include a newline at end)
Returns:

False if the connection should be terminated, True otherwise

exception ivy.ivy.IvyIllegalStateError[source]

Bases: exceptions.RuntimeError

exception ivy.ivy.IvyMalformedMessage[source]

Bases: exceptions.Exception

exception ivy.ivy.IvyProtocolError[source]

Bases: exceptions.Exception

class ivy.ivy.IvyServer(agent_name, ready_msg='', app_callback=<function void_function at 0x3a14938>, die_callback=<function void_function at 0x3a14938>, usesDaemons=False)[source]

Bases: SocketServer.ThreadingTCPServer

An Ivy server is responsible for receiving and handling the messages that other clients send on an Ivy bus to a given agent.

An IvyServer has two important attributes: usesDaemons and server_termination.

Variables:
  • usesDaemons – whether the threads are daemonic or not. Daemonic threads do not prevent python from exiting when the main thread stop, while non-daemonic ones do. Default is False. This attribute should be set through at __init__() time and should not be modified afterwards.
  • server_termination – a threading.Event object that is set on server shutdown. It can be used either to test whether the server has been stopped (server_termination.isSet()) or to wait until it is stopped (server_termination.wait()). Application code should not try to set the Event directly, rather it will call stop() to terminate the server.
  • port – tells on which port the TCP server awaits connection

All public methods (not starting with an underscore _) are MT-safe

Group Communication on the ivybus:
 start, send_msg, send_direct_message, send_ready_message, handle_msg, stop
Group Inspecting the ivybus:
 get_clients, _get_client, get_client_with_name
Group Our own subscriptions:
 get_subscriptions, bind_msg, unbind_msg, _add_subscription, _remove_subscription, _get_fct_for_subscription
bind_direct_msg(on_direct_msg_fct)[source]
bind_msg(on_msg_fct, regexp)[source]

Registers a new subscriptions, by binding a regexp to a function, so that this function is called whenever a message matching the regexp is received.

Parameters:
  • on_msg_fct: a function accepting as many parameters as there is groups in the regexp. For example:
    • the regexp '^hello .*' corresponds to a function called w/ no parameter,
    • '^hello (.*)': one parameter,
    • '^hello=([^ ]*) from=([^ ]*)': two parameters
  • regexp: (string) a regular expression
Returns:

the binding’s id, which can be used to unregister the binding with unbind_msg()

bind_regexp_change(on_regexp_change_callback)[source]
get_client_with_name(name)[source]

Returns the list of the clients registered with a given agent-name

See:get_clients
get_clients()[source]

Returns the list of the agent names of all connected clients

See:get_client_with_name
get_subscriptions()[source]
handle_die_message(msg_id, from_client=None)[source]
handle_direct_msg(client, num_id, msg)[source]
Parameters:
  • client
  • num_id
  • msg
Returns:

handle_msg(client, idx, *params)[source]

Simply call the function bound to the subscription id idx with the supplied parameters.

handle_new_client(client)[source]

Completes connection with the client TODO: maybe add a flag (while connecting) on the client, that would prevent sending msg. etc.. as CNX. not confirmed

handle_regexp_change(client, event, num_id, regexp)[source]
isAlive()[source]
remove_client(ip, port, trigger_application_callback=True)[source]

Removes a registered client

This method is responsible for calling server.app_callback

Returns:the removed client, or None if no such client was found

Note

NO NETWORK CLEANUP IS DONE

send_direct_message(agent_name, num_id, msg, stop_on_first=True)[source]

Sends a direct message to the agent named agent_name. If there is more than one agent with that name on the bus, parameter stop_on_first determines the behaviour.

Parameters:
  • agent_name: the name of the agent(s) to which the direct message should be sent.
  • num_id: a numerical identifier attached to the direct message
  • msg: the direct message itself
  • stop_on_first: if True, the message to all agents having the same name will receive the message; if False the method exits after the first message has been sent.
Returns:

True if at least one direct message was sent

send_msg(message)[source]

Examine the message and choose to send a message to the clients that subscribed to such a msg

Returns:the number of clients to which the message was sent
send_ready_message(client)[source]
serve_forever()[source]

Handle requests (calling handle_request()) until doomsday... or until stop() is called.

This method is registered as the target method for the thread. It is also responsible for launching the UDP server in a separate thread, see UDP_init_and_listen for details.

You should not need to call this method, use start instead.

start(ivybus=None)[source]

Binds the server to the ivybus. The server remains connected until stop is called, or until it receives and accepts a ‘die’ message.

Raises IvyIllegalStateError:
 if the server has already been started
stop()[source]

Disconnects the server from the ivybus. It also sets the server_termination event.

Raises IvyIllegalStateError:
 if the server is not running started
unbind_msg(num_id)[source]

Unbinds a subscription

Parameters:num_id – the binding’s id, as returned by bind_msg()
Returns:the regexp corresponding to the unsubscribed binding
Raises KeyError:
 if no such subscription can be found
class ivy.ivy.IvyTimer(server, nbticks, delay, callback)[source]

Bases: threading.Thread

An IvyTimer object is responsible for calling a function regularly. It is bound to an IvyServer and stops when its server stops.

  • Each timer gets a unique id, stored in the attribute id. Note that a dead timer’s id can be reassigned to another one (a dead timer is a timer that has been stopped)
  • To start a timer, simply call its method start()
  • To modify a timer’s delay: simply assign timer.delay, the modification will be taken into account after the next tick. The delay should be given in milliseconds.
  • to stop a timer, assign timer.abort to True, the timer will stop at the next tick (the callback won’t be called)

Please note: start() starts a new thread; if the same function is given as the callback to different timers, that function should be prepared to be called concurrently. Specifically, if the callback accesses shared variables, they should be protected against concurrency problems (using locks e.g.).

run()[source]
ivy.ivy.UDP_init_and_listen(broadcast_addr, port, socket_server)[source]

Called by an IvyServer at startup; the method is responsible for:

  • sending the initial UDP broadcast message,
  • waiting for incoming UDP broadcast messages being sent by new clients connecting on the bus. When it receives such a message, a connection is established to that client and that connection (a socket) is then passed to the IvyServer instance.
Parameters:
  • broadcast_addr: the broadcast address used on the Ivy bus
  • port: the port dedicated to the Ivy bus
  • socket_server: instance of an IvyServer handling communications for our client.
ivy.ivy.decode_ivybus(ivybus=None)[source]

Transforms the supplied string into the corresponding broadcast address and port

Parameters:ivybus – if None or empty, defaults to environment variable IVYBUS
Returns:a tuple made of (broadcast address, port number). For example:
>>> print decode_ivybus('192.168.12:2010')
('192.168.12.255', 2010)
ivy.ivy.decode_msg(msg)[source]
Returns:msg_id, numerical_id, parameters
Raises IvyMalformedMessage:
 
ivy.ivy.encode_message(msg_type, numerical_id, params='')[source]

params is string -> added as-is params is list -> concatenated, separated by ARG_END

ivy.ivy.is_multicast(ip)[source]

Tells whether the specified ip is a multicast address or not

Parameters:ip – an IPv4 address in dotted-quad string format, for example 192.168.2.3
ivy.ivy.trace(*args, **kw)
ivy.ivy.void_function(*arg, **kw)[source]

A function that accepts any number of parameters and does nothing

ivy.std_api module

Implements the standard Ivy API.

You can refer to the example code pyhello.py for an example of use.

All methods in this module are frontends to a ivy.IvyServer instance, stored in the module’s attributes _IvyServer.

group Connecting to/disconnecting from the Ivy bus:
 IvyInit, IvyStart,IvyMainLoop, IvyStop
group Bindings:IvyBindMsg, IvyUnBindMsg, IvyBindDirectMsg
group Inspecting the Ivy bus:
 IvyGetApplicationList, IvyGetApplication, IvyGetApplicationName, IvyGetApplicationHost
group Interacting with other Ivy clients:
 IvySendMsg, IvySendDieMsg, IvySendDirectMsg
group Timers:IvyTimerRepeatAfter, IvyTimerModify, IvyTimerRemove

Copyright (c) 2005-2011 Sebastien Bigaret <sbigaret@users.sourceforge.net>

ivy.std_api.IvyBindDirectMsg(on_msg_fct)[source]

Registers a method that should be called each time someone sends us a direct message

ivy.std_api.IvyBindMsg(on_msg_fct, regexp)[source]

Registers a method that should be called each time a message matching regexps is sent on the Ivy bus.

Returns:an id identifying the binding, that can be used to unregister it (see IvyUnBindMsg())
ivy.std_api.IvyBindRegexpChange(regexp_change_callback)[source]
ivy.std_api.IvyGetApplication(name)[source]

Returns the Ivy client registered on the bus under the given name.

Warning

if multiple applications are registered w/ the same name only one is returned

Returns:an ivy.IvyClient object
ivy.std_api.IvyGetApplicationHost(client)[source]

Equivalent to client.fqdn. IP address is stored under client.ip, and port number under client.port

Parameters:client – an ivy.IvyClient object, as returned by IvyGetApplication()
ivy.std_api.IvyGetApplicationList()[source]

Returns the names of the applications that are currently connected

ivy.std_api.IvyGetApplicationMessages(client)[source]

Returns all subscriptions for that client

Parameters:client – an ivy.IvyClient object, as returned by IvyGetApplication()
Returns:list of tuples (idx, regexp)
ivy.std_api.IvyGetApplicationName(client)[source]

Equivalent to client.agent_name

Parameters:client – an ivy.IvyClient object, as returned by IvyGetApplication()
ivy.std_api.IvyInit(agent_name, ready_msg=None, main_loop_type_ignored=0, on_cnx_fct=<function void_function at 0x3a14938>, on_die_fct=<function void_function at 0x3a14938>)[source]

Initializes the module. This method should be called exactly once before any other method is called.

ivy.std_api.IvyMainLoop()[source]

Simulates the original main loop: simply waits for the server termination.

Note that while the original API requires this to be called, this module does NOT rely in any way on this method. In particular, a client is fully functional and begins to receive messages as soon as the IvyStart() method is called.

ivy.std_api.IvySendDieMsg(client)[source]

Sends a “die” message to client, instructing him to terminate.

Parameters:client – an ivy.IvyClient object, as returned by IvyGetApplication()
ivy.std_api.IvySendDirectMsg(client, num_id, msg)[source]

Sends a message directly to an other Ivy client, with the supplied numerical id and message.

Parameters:
  • client: an ivy.IvyClient object, as returned by IvyGetApplication()
  • num_id: an additional integer to use. It may, or may not, be meaningful, this only depends on the usage your application makes of it, the Ivy protocol itself does not care and simply transmits it along with the message.
  • msg: the message to send
ivy.std_api.IvySendError(client, num_id, error_msg)[source]

Sends an “error” message to client

Parameters:
  • client: an ivy.IvyClient object, as returned by IvyGetApplication()
  • num_id: an additional integer to use. It may, or may not, be meaningful, this only depends on the usage your application makes of it, the Ivy protocol itself does not care and simply transmits it along with the message.
  • error_msg: the message to send
ivy.std_api.IvySendMsg(msg)[source]

Sends a message on the bus

ivy.std_api.IvyStart(ivybus=None)[source]

Starts the Ivy server and fully activate the client. Please refer to the discussion in IvyMainLoop() ‘s documentation.

ivy.std_api.IvyStop()[source]
ivy.std_api.IvyTimerModify(timer_id, delay)[source]

Modifies a timer’s delay. Note that the modification will happen after the next tick.

Parameters:
  • timer_id: id of the timer to modify, as returned by IvyTimerRepeatAfter()
  • delay: the delay, in milliseconds, between ticks
ivy.std_api.IvyTimerRemove(timer_id)[source]

Stops and removes a given timer.

Parameters:timer_id – id of the timer, as returned by IvyTimerRepeatAfter
ivy.std_api.IvyTimerRepeatAfter(count, delay, callback)[source]

Creates and activates a new timer

Parameters:
  • count: nb of time to repeat the loop, 0 (zero) for an endless loop
  • delay: the delay between ticks, in milliseconds
  • callback: the function to call on every tick. That function is called without any parameters.
Returns:

the timer’s id

ivy.std_api.IvyUnBindMsg(binding_id)[source]

Unregisters a binding

Parameters:binding_id – the binding’s id, as previously returned by IvyBindMsg().
Returns:the regexp corresponding to the unsubscribed binding
Raises KeyError:
 if no such subscription can be found

Module contents

Ivy is a lightweight software bus for quick-prototyping protocols. It allows applications to broadcast information through text messages, with a subscription mechanism based on regular expressions.

If you’re used to the standard Ivy API, you probably want to look at the std_api module.

Note

Version 2.0 broke backward compatibility. If you are upgrading from ivy-python v1.x, be sure to read the v2.0 compatibility notes, below.

Introduction

The Ivy software bus was designed at the French Centre d’Etudes de la Navigation Aerienne (CENA). The original work: software, documentation, papers, credits can be found on the Ivy Home Page; it contains all the necessary materials needed to understand Ivy.

This package is the Python library; Ivy libraries are also available for numerous languages, among which: C, C#, C++, Java, Perl –see the Ivy Download Page for details.

This python library is a full rewrite of the original software, and it is written in pure python. Note that another python implementation is available at the Ivy downloads page, which is built by SWIG on top of the Ivy C library.

Understanding the package

This Ivy package is made of two modules: ivy and std_api.

In order to understand the way it works, we highly suggest that you read the materials available from the Ivy Home Page. Within the documentation of this python package we suppose that you are already familiar with the way an ivy bus works, and with how the corresponding framework is organized.

ivy.std_api

Once familiar with the ivy framework, you’ll find in the std_api module the exact API you’re expecting (see for example the The Ivy C library).

An example of use, directly taken from the original swig-base python release, is included with the package, see examples/pyhello.py.

Important

One big difference with the original implementation is that there is nothing like a “main loop”: the server is activated as soon as the method ivy.std_api.IvyStart is called, and the ivy.std_api.IvyMainLoop method simply waits for the server to terminate (the server is in fact

ivy.ivy

It’s where the “magic” goes: the module std_api is built on top of it.

You’ll be interested in using this package if for example, you want to manage more than one ivy-bus in an application. We won’t give here much hint on how to use it, since the code of the ivy module itself serves as a perfect example of use!

Logging

The module issues messages through python’s standard logging module:

  • logger’s name: 'Ivy'
  • default level: logging.INFO
  • default handler: a logging.StreamHandler logging messages to the standard error stream.

For example, if you need to see all messages issued by the package, including debug messages, use the following code excerpt:

Further details can be found in the Python standard documentation.

v2.0 compatibility notes

Version 2.0 broke the backward compatibility with former versions 1.x: every callback methods now receives an ivy.IvyClient object as its first parameter.

This makes it possible for the receiving code to know which agent on the bus sends the corresponding event. Unfortunately, this breaks backward compatibility and callback methods must be adapted to the new scheme.

For example, suppose you’re binding a regexp to the callback on_msg:

In ivy-python 1.x, you used to declare the callback as follows

or:

In version 2.0, your callbacks also get the agent triggering the event as the 1st parameter; the previous callbacks should be rewritten this way:

or:

Misc. notes

  • direct msg: to app_name == the last one registered w/ that name (for example register ivyprobe 3 times and send a direct msg to IVYPROBE from one of them)
  • regexps order and ids: ivyprobe e.g. subscribes regexp in reverse order of ids. For each match, send the appropriate message. documented.

License

This software is distributed under the “new” BSD license,

(please refer the file LICENSE for full legal details)

Copyright (c) 2005-2011 Sebastien Bigaret <sbigaret@users.sourceforge.net>