Getting started

This sections intends to be a crash course of pysig library, describing basic usage scenarios. For more information please refer to the rest of documentation. It will provide you all the necessary details to understand all the features supported by pysig.

The basics

This example will show how you can create a simple router for managing the flow of events between a listener and a sender. First, lets create a sender.

import sig

# create router object
router = sig.Router()

# create a sender
sender = router.addSender("my_sender", [ "my_event_1", "my_event_2"])

It’s simple enough to understand, that we have created a sender object called my_sender that may trigger two events in the near future.

Let’s trigger first event, to see how this is done.

# create a trigger for my_event_1
sig_ev1 = sender.getSignal("my_event_1")

# trigger this event
data = { "info1" : "something", "info2" : 2 }

# do trigger
sig_ev1.trigger(data)

For the sake of simplicity, we can write this in only one line:

router.getSender("my_sender").getSignal("my_event_1").trigger({"info1":"somedata", "info2":2})

Now, what we have done, is to trigger an event with additional data attached, even though no listeners are registered to it. That is no problem for pysig, but is somehow useless.

Lets define our callback function.

# this is your callback function
def listen_to_events(info, data):
        event = info.get("event")
        sender = info.get("sender")
        print "Received event '%s' from sender '%s' having data '%s'" % (event, sender, data)

Now, let’s register it to the sender.

# let's register our listener callback
router.addListener(listen_to_events, "my_sender", "my_event_1")

It’s done, we are listening to the first event of our sender. You may notice, we used the router object for registering the listener. We may also use the sender object with the same effect.

sender.addListener(listen_to_events, "my_event1")

The router object simplifies the registration. But be aware, when using the router, the sender and event parameter are purely optional. That is for a very good reason, the listener may register to all sender events or even all router events, using special broadcast events that will be described later on in this documentation.

Note!

Please note that sender_identifier and event_identifier are the exact same objects passed by the caller to the Router.addSender function and not just strings. The only restriction regarding these parameters is that they need to be JSON-serializable and hashable. Otherwise said, they can be str objects as well as int, float objects.

For example, this code is perfectly valid for pysig, but it also may be confusing:

[...]
sender = router.addSender( 20, [ 1, 2.0, "2.0"])
sig_ev1 = sender.getSignal(1)
sig_ev2 = sender.getSignal(2.0)
sig_ev3 = sender.getSignal("2.0")
[...]

You can also use dictionaries as long as they are encoded:

import json
[...]
sender_name = { "id" : 123, "source" : "http://www.something.com" }
sender_name = json.dumps(sender_name)
sender = router.addSender(senderName, [1,2,3] )
[...]

Example

Lets see how it looks, in the proper order.

import sig

# this is your callback function
def listen_to_events(info, data):
        event = info.get("event")
        sender = info.get("sender")
        print "Received event '%s' from sender '%s' having data '%s'" % (event, sender, data)

# create router
router = sig.Router()

# create sender
sender = router.addSender("my_sender", ["my_event_1", "my_event_2"])
sig_ev1 = sender.getSignal("my_event_1")

# register listener
router.addListener(listen_to_events, "my_sender", "my_event_1")

# trigger first event
sig_ev1.trigger({"info1":"something", "info2":2})

The output will be, of course:

Received event 'my_event_1' from sender 'my_sender' having data '{'info1': 'something', 'info2': 2}'

Network

The above example is useful for dispatching events intra-process, otherwise said, inside the same application. For distributing these events over a network, we have to use the server and client implementation of pysig.

Carrier

The communication between a pysig server and its client are assured by the Carrier class. This class defines the abstraction layer necessary for pysig to communicate. We can implement your own carrier, by deriving from this class.

Currently pysig supports only built-in TCP Client and TCP Server carriers.

Server

In order to receive commands remotely, you need to use instead of the Router class the ServerRouter class. The difference of this two, is that the second one requires a carrier for instantiation.

Client

In order to send commands remotely, you need to use the ClientRouter class instead of the Router class.

Also, instead of addSender, addListener, removeSender, removeListener methods, you are supposed to use the corresponding addRemoteSender, addRemoteListener, removeRemoteSender and removeRemoteListener methods.

The first set of methods will register senders and listeners only locally, as explained later in this documentation.

Examples

In the root of pysig repository we can find the examples folder. In this folder we can find several ready to use examples, that shows network functionality support. We will enumerate some of them.

generic_server.py

This example starts a basic pysig router server that also registers a sender which periodically fires a given event. The name of the sender and the event fired can be customized the the caller from stdin.

Log level   (default: info):
Server bind (default: 0.0.0.0):
Server port (default: 3000):
Sender      (default: timer):
Event       (default: tick):
Event data  (default: None):server data
Timer delay (default: 5):
[sig][  36][                info][    info]-[TCP_SRV] Starting to listen on 0.0.0.0:3000
[sig][  79][            <module>][    info]-[timer] Triggering event tick with data 'server data'

generic_client_sender.py

This example shows how to create a router client that connects to a given server. It uses the client to register a custom sender that triggers an event periodically. The address of router, the name of the sender and the name of the event are read from stdin.

Server IP   (default: 127.0.0.1):
Server port (default: 3000):
Sender      (default: timer_client):
Event       (default: tick):
Event data  (default: None):client data
Timer delay (default: 5):
[sig][  36][                info][    info]-[TCP_CL] Connecting to 127.0.0.1:3000
[sig][  36][                info][    info]-[TCP_CL] Started receive thread for 127.0.0.1:3000
[sig][  60][            <module>][    info]-[timer_client] Triggering event tick with data 'client data'

generic_client_listener.py

This example shows how to create a router client that connects to a given server. It uses the client to register a listener to a specific event, a broadcast event or a channel event. It reads the address of the router from stding and also the name of the sender and event.

The output below, shows how we ran the example and instructed it to listen for the tick event issued by the server called timer.

Server IP   (default: 127.0.0.1):
Server port (default: 3000):
Sender      (default: None):timer
Event       (default: None):tick
[sig][  36][                info][    info]-[TCP_CL] Connecting to 127.0.0.1:3000
[sig][  36][                info][    info]-[TCP_CL] Started receive thread for 127.0.0.1:3000
[sig][  41][            listener][    info]-[timer] Event tick received with data '"test"'

We can also instruct it to listen to the tick event issued by the generic_client_sender.py as long as this example runs.

Server IP   (default: 127.0.0.1):
Server port (default: 3000):
Sender      (default: None):timer_client
Event       (default: None):tick
[sig][  36][                info][    info]-[TCP_CL] Connecting to 127.0.0.1:3000
[sig][  36][                info][    info]-[TCP_CL] Started receive thread for 127.0.0.1:3000
[sig][  41][            listener][    info]-[timer_client] Event tick received with data '"client data"'

When generic_client_sender.py is restarted, the example should output the following:

[sig][  34][    listener_connect][    info]-Sender timer_client is now disconnected
[sig][  34][    listener_connect][    info]-Sender timer_client is now connected

That is because the generic_client_listener.py automatically registers for the built-in events sig.EVENT_CONNECT and sig.EVENT_DISCONNECT, for the indicated sender as this code shows:

client.addRemoteListener(listener_connect, sender, sig.EVENT_CONNECT)
client.addRemoteListener(listener_connect, sender, sig.EVENT_DISCONNECT)
client.addRemoteListener(listener, sender, event)

Note!

Using the examples, mentioned above, you can easily experiment the concept of broadcast events, broadcast senders and channel events, by simply modiifying the parameters passed from stdin.

generic_client_request.py

This example shows how to issue a request to a sender that supports request handling. In order to test it easily, we have added a request method called get_ticks to the sender registered by generic_server.py and to the sender registered by generic_client_sender.py.

Therefore, when generic_server.py is run, we may test generic_client_request.py with the following input:

Server IP   (default: 127.0.0.1):
Server port (default: 3000):
Sender      (default: timer):
Request     (default: get_ticks):
Params      (default: None):
Every       (default: 5):

As seen from input, this example will issue the request get_ticks to the sener timer every 5 seconds. The output of generic_client_request.py should look like this:

[sig][  36][                info][    info]-[TCP_CL] Connecting to 127.0.0.1:3000
[sig][  36][                info][    info]-[TCP_CL] Started receive thread for 127.0.0.1:3000
[sig][  70][            <module>][    info]-Requesting get_ticks from timer (params: None)
[sig][  73][            <module>][    info]-Response = 62

In the same time generic_server.py will output the following:

[sig][  52][           get_ticks][    info]-[REQUEST] get_ticks (returned: 62)

In case we configure it to ask for a request that doesn’t exist, because the sender registered by generic_server.py supports a default handler, the output will be the following:

[sig][  36][                info][    info]-[TCP_CL] Connecting to 127.0.0.1:3000
[sig][  36][                info][    info]-[TCP_CL] Started receive thread for 127.0.0.1:3000
[sig][  70][            <module>][    info]-Requesting something from timer (params: some_params)
[sig][  73][            <module>][    info]-Response = Unknown method something (params: some_params)

alert_listener.py

This example implements a listener connected remotely to a pysig server, to a list of events from a plural of senders, that triggers a Desktop notification for Linux users, whenever an event was triggered.

web_events_logger.py

This example implements a web application that logs all the received events. You need to install web.py library in order to make it work.

You can install web.py using pip:

pip install web.py

Just like alert_listener.py this example implements a listener connected remotely to a pysig server.

From stdin you can configure it to listen to a list of specific events, from a list of specific senders or to the broadcast event of specific senders or to channel events. After is configured you can modifiy the list of subscriptions by using a RESTful API.

The log of events are printed out either in HTML or PLAIN text format.

The following is an example input of a possible configuration of web_events_logger.py example, instructing it to:

  • connect to the pysig server at 192.168.9.20:3000
  • limit the number of events to 200 entries, after which the least-recent entry will be deleted
  • listen to all events from ifttt sender (registering to iffttt broadcast event)
  • listen to the specific ir event from keros sender
  • listen to the #notify channel from all senders
Server IP   (default: 127.0.0.1):
Server port (default: 3000):
Queue limit (default: 20):200
Sender 1:ifttt
Event  1:None
Event  2:
Sender 2:keros
Event  1:ir
Event  2:
Sender 3:None
Event  1:#notify
Event  2:
Sender 4:

After configuring the web application via stdin the following output should be seen:

[sig][  36][                info][    info]-[TCP_CL] Connecting to 192.168.9.20:3000
[sig][  36][                info][    info]-[TCP_CL] Started receive thread for 192.168.9.20:3000
[sig][  79][perform_registration][    info]-Registering to ifttt:None
[sig][  90][perform_registration][    info]-done
[sig][  79][perform_registration][    info]-Registering to keros:ir
[sig][  90][perform_registration][    info]-done
[sig][  79][perform_registration][    info]-Registering to None:#notify
[sig][  90][perform_registration][    info]-done
http://0.0.0.0:8080/

The RESTful API that you can use with this web application is:

/

The index page will return the recorded log entries. An optional parameter is the fmt parameter that can be passed as plain or html. Default is html.

Another set of optional arguments is the sender and event parameter, that once passed to the GET request it will filter out the log for the given sender and/or event.

Ex: http://localhost:8080/?fmt=plain

/regs or /registrations

Returns the list of subscriptions this application is currently registered at. Same as for index / page, the optional fmt parameter is used to select format.

Ex: http://localhost:8080/regs?fmt=plain

/clear or /flush

Resets the list of logged events.

Ex: http://localhost:8080/clear

/config or /cfg

Receives the optional argument limit that is used to modify the limit of events that logs and then outputs a JSON containing the current configuration.

Ex: http://localhost:8080/config?limit=100

/add

Registers to the given subscription and receives a sender and event parameter. If you omit one of them, it will be defaulted to None.

Ex: http://localhost:8080/add?sender=timer&event=tick

/remove or /rem

Removes a certain registration by using the given sender and event parameters. If you omit one of them, it will be defaulted to None.

Ex: http://localhost:8080/remove?sender=timer&event=tick