pyimb is a Python client to the IMB framework

This text assumes you are familiar with TNO’s IMB framework.

imb module

Client to IMB hub

The module is written in Python 3.4 and only tested with Python 3.4. Backporting to Python 2.7 should be easy. Just be careful with unicode strings and byte arrays.

class imb.Client(host, port, owner_id=None, owner_name=None, federation=None)

A client that can connect to an IMB hub.

The client tries to open a socket immediately as the object is created. The socket read/write logic is implemented with the python asynchat module, and the asyncore.loop() call is made in a separate thread. That thread will not finish untill Client.disconnect() has been called.

Examples

>>> import imb
>>> host = 'localhost'
>>> port = 4000
>>> owner_id = 123
>>> owner_name = 'my name'
>>> federation = 'my federation'
>>>
>>> c = imb.Client(host, port, owner_id, owner_name, federation) # Connect to a hub
>>> # Example 1: Send a string in the payload of a NormalEvent
>>> e = c.publish('my event') # Now we can send signals on the event
>>> e.signal_event(imb.ekNormalEvent, imb.encode_string('This string is sent'))
>>> e.signal_string('This is easier but equivalent.')
>>>
>>> # Example 2: Receive a string in the payload of a NormalEvent
>>> def string_handler(payload):
...     print('Received string {}'.format(imb.decode_string(payload)))
...
>>> # Handle all ekNormalEvent signals on this event with the string_handler
>>> e.add_handler(imb.ekNormalEvent, string_handler)
>>> e.subscribe() # start listening for signals on the event
>>>
>>> # Example 3: Send a file as a stream
>>> e.signal_stream('stream name', open('test.txt', 'rb')) # Empty the file stream
>>> e.unpublish()
>>>
>>> # Example 4: Receive a stream
>>> def create_stream(stream_id, stream_name):
...     filename = str(stream_id) + '_' + stream_name
...     return open(filename, 'wb+')
...
>>> def end_stream(stream):
...     pass # do something here, just before automatic stream.close()
...
>>> e.create_stream_callback = create_stream
>>> e.end_stream_callback = end_stream # this is optional, really
>>> # Now just wait for a stream to come flying on the event
>>> # (we are already subscribed since Example 2 above!)
>>> e.unsubscribe() # Unsubscribe when you are done
>>> c.disconnect() # And finally always disconnect
collect_incoming_data(data)

Called by async_chat for incoming data on the TCP socket.

See docs for async_chat.

disconnect()

Disconnect the underlying socket.

found_terminator()

Called by async_chat when a terminator sequence is received.

See docs for async_chat.

get_event(event_name, prefix=True, create=True)

Get an EventDefinition object that can be used for communication.

The EventDefinition object returned will not be subscribed or published to anything.

Parameters:
  • event_name (str) – The name of the event.
  • prefix (bool, optional) – If True, the event name will be prefixed with the client federation + ., so you get something like ` my_federation.my_event_name`.
  • create (bool, optional) – If True (the default), the event will be created if it doesn’t exist yet. If False, and the event doesn’t exist yet, this function will return None.
Returns:

An EventDefinition object or None.

Example

>>> c = imb.Client(host, port, owner_id, owner_name, federation) # Connect to a hub
>>> e = c.get_event('my event')
>>> e.publish() # Now we can send signals on the event
handle_connect()

Called by async_chat class when the client has connected.

See docs for async_chat.

publish(event_name, prefix=True)

Get an EventDefinition object and ensure it is subscribed.

Parameters:
Returns:

A published EventDefinition object.

Example

>>> # Two equivalent ways of getting a published EventDefinition object
>>> e1 = client.get_event('my_event')
>>> e1.publish()
>>> e2 = c.publish('my_event') # This gets a reference to the same object
>>> e1 is e2
True
signal_normal_event(event_id, event_kind, event_payload)

Send an icEvent command.

You probably want to use e.g. Event.signal_event() instead. It will at least simplify things by supplying the event_id for you.

Parameters:
  • event_id (int) – The event_id to send.
  • event_kind (int) – One of the event kind constants, e.g. ekNormalEvent or ekChangeObjectEvent.
  • event_payload (bytes or similar) – The event payload to send along.
signal_publish(event_id, event_entry_type, event_name)

Publish an event.

You probably want to use Client.publish() or EventDefinition.publish() instead.

signal_subscribe(event_id, event_entry_type, event_name)

Subscribe to an event.

You probably want to use Client.subscribe() or EventDefinition.subscribe() instead.

signal_unpublish(event_name)

Unpublish an event.

You probably want to use Client.unpublish() or EventDefinition.unpublish() instead.

signal_unsubscribe(event_name)

Unsubscribe from an event.

You probably want to use Client.unsubscribe() or EventDefinition.unsubscribe() instead.

subscribe(event_name, prefix=True)

Get an EventDefinition object and ensure it is subscribed.

Parameters:
Returns:

A subscribed EventDefinition object.

Example

>>> e1 = client.get_event('my_event')
>>> e1.subscribe()
>>> e2 = c.subscribe('my_event') # This gets a reference to the same object
>>> e1 is e2
True
unpublish(event_name, prefix=True)

Ensure that the client is not publishing an event.

Parameters:
Returns:

The EventDefinition object, if it already existed. Otherwise None.

unsubscribe(event_name, prefix=True)

Ensure that the client is not subscribed to an event.

Parameters:
Returns:

The EventDefinition object, if it already existed. Otherwise None.

class imb.ClientStates

Possible states of a Client.

Used internally by Client.

class imb.Command(command_code=None, payload=None)

Represents a command that can be sent by a Client

Commands are things like “subscribe to event”, “publish event”, “signal event”, etc.

You don’t need to use this class directly. It is used only by the Client class.

class imb.EventDefinition(event_id, name, client)

Represents an event in the IMB framework

The EventDefinition object represents an event (a named communication channel) in the IMB framework. It is created by a Client instance and can then be used to

  • subscribe/unsubscribe to the event,
  • publish/unpublish the event,
  • send signals, and
  • setup handlers for incoming signals.

Note

You should not create EventDefinition instances yourself. Instead, call Client.get_event(), Client.subscribe() or Client.publish().

add_handler(event_kind, handler)

Add a handler for received events of a certain kind.

The handler is a callable object which is called after an event has arrived. Your handler is expected to have the right arguments signature for the event kind. Check examples below, or the source of _decode_event_payload() if you are unsure.

Parameters:
  • event_kind (int) – One of the event kind constants, e.g. ekNormalEvent or ekChangeObjectEvent.
  • handler (callable) – The handler.
Raises:

RuntimeError – If you try to add handlers for stream events.

Example

>>> c = imb.Client(url, port, owner_id, owner_name, federation)
>>> e = c.subscribe('event name')
>>> def my_change_object_handler(action, object_id, short_event_name, attr_name):
...     print('ChangeObjectEvent:', action, object_id, short_event_name, attr_name)
>>> e.add_handler(imb.ekChangeObjectEvent, my_change_object_handler)
>>> def my_normal_event_handler(payload):
...     print('NormalEvent:', payload)
>>> e.add_handler(imb.ekNormalEvent, my_normal_event_handler)
create_stream_callback

Property – Callback function for stream creation.

This property should be either None or a callable to be called when a stream header arrives on the event. The callback function should take two arguments: (stream_id, stream_name), and return a stream object to save the incoming stream in.

If no stream object is returned from the stream callback function, the incoming stream will not be saved.

Note

There is also a property end_stream_callback() which is called at the end of the stream, just before it is closed.

Example:

>>> def create_stream(stream_id, stream_name):
...     filename = str(stream_id) + '_' + stream_name
...     return open(filename, 'wb+')
...
>>> def end_stream(stream):
...     pass # do something here, just before automatic stream.close()
...
>>> e = client.subscribe('my-stream')
>>> e.create_stream_callback = create_stream
>>> e.end_stream_callback = end_stream # this is optional, really
end_stream_callback

Property – Callback function for handling end of stream.

Similar to create_stream_callback(), this property should either be None or a callable to be called when a stream tail has arrived on the event. The callback function should take one argument stream: it will be passed the same stream object that was created in create_stream_callback().

Note

The stream is automatically closed right after the end_stream_callback function is called, so you don’t have to do this manually.

Example

See create_stream_callback() for an example.

handle_event(event_kind, event_payload)

Call all handlers registered for the event_kind.

You never have to call this function directly. It is called by the owning Client object.

See also EventDefinition.add_handler().

publish()

Publish this event with the owning Client

Equivalent to client.signal_publish(...).

This function is provided only for convenience.

signal_change_object(action, object_id, attribute)

Send a ChangeObject event.

Parameters:
  • action (int) – One of the action constants.
  • object_id (int) – Id of object to change.
  • attribute (string) –
signal_event(event_kind, event_payload)

Send a message on the event.

Parameters:
  • event_kind (int) – One of the event kind constants, e.g. ekNormalEvent or ekChangeObjectEvent.
  • event_payload (bytes or similar) – The payload to send.
signal_stream(name, stream, chunk_size=16384)

Send a stream on the event.

Parameters:
  • name (str) – A name for the stream, unique for the Client.
  • stream (stream object) – The stream to be sent. Must be implemented as a standard Python stream, e.g. a file object obtained through open(filename, ‘b’).
  • chunk_size (int, optional) – Number of bytes to send in each body chunk.
signal_string(value)

Send a NormalEvent event with only a string in the payload.

This is provided for convenience. Equivalent to self.signal_event(ekNormalEvent, encode_string(value)).

Parameters:value (str) – The string to send.
subscribe()

Subscribe the owning Client to this event

Equivalent to client.signal_subscribe(...).

This function is provided only for convenience.

unpublish()

Unpublish this event with the owning Client

Equivalent to client.signal_unpublish(...).

This function is provided only for convenience.

unsubscribe()

Unsubscribe the owning Client from this event

Equivalent to client.signal_unsubscribe(...).

This function is provided only for convenience.

imb.decode_int(buf)

Decode a signed integer from bytes, using the default byte order.

Parameters:buf (bytes or similar) – The bytes to decode.
Returns:The decoded integer.
Return type:int
imb.decode_string(buf, start=0, nextpos=False)

Decode a string prefixed with length.

This function is the reverse of encode_string().

Parameters:
  • buf (bytes or similar) – The bytes to decode.
  • start (int, optional) – The index to start decoding from in the bytes.
  • nextpos (bool, optional) – If True, the function will return a tuple (decoded_string, nextpos), where nextpos is equal to one plus the index of the last byte of the string. (See example below.)

Examples

>>> encode_string('foo')
b'foo'
>>> decode_string(encode_string('bar'))
'bar'
>>> buf = b''.join([encode_string('foo'), encode_string('bar')])
>>> s1, nextpos = decode_string(buf, nextpos=True)
>>> s1
'foo'
>>> nextpos
7
>>> s2 = decode_string(buf, start=nextpos)
>>> s2
'bar'
imb.decode_uint(buf)

Decode an unsigned integer from bytes, using the default byte order.

Parameters:buf (bytes or similar) – The bytes to decode.
Returns:The decoded integer.
Return type:int
imb.encode_int32(value)

Encode a signed 32-bit integer, using the default byte order.

Parameters:value (int) – The integer to encode.
Returns:A bytes object representing the integer.
Return type:bytes
imb.encode_string(value)

Encode a string (prefixed with string length).

Strings are encoded as follows. First comes a signed 32-bit integer equal to the number of bytes in the encoded string. Then comes the string, encoded with the default encoding.

Parameters:value (str) – The string to encode.
Returns:A bytes object representing the integer.
Return type:bytes
imb.encode_uint32(value)

Encode an unsigned 32-bit integer in bytes using the default byte order.

Parameters:value (int) – The integer to encode.
Returns:A bytes object representing the integer.
Return type:bytes

Indices and tables