|
|
|
|
|
CLIX is a Python plugin framework. "Clix" is a pun on the word 'clique' which means an association or a gathering. In this case, CLIX gather components and or classes into the same executable space, and creates a databus for them to use in communicating with one another. CLIX is not (by itself) a strategy for building loosely coupled systems, although it may participate in such systems. It is a framework for plugging in components and objects into the same namespace conveniently and in a standard way.
When an instance of CLIX starts up via:
Python clixManager.py
it builds a databus and loads all the plugins you add to a folder called clixlets that you position relative to the startup folder for CLIX's switchboard and plugin manager located in clixManager.py. Once loaded, CLIXLETS communicate with all other CLIXLETS in the same namespace via input and output queues. A CLIXLET may have both input and output queues, or only one of the other (for example, a GUI CLIXLET might only have an output queue if it is only issuing commands to other CLIXLETS). After loading CLIXLETS, it then acts as a switchboard to route messages between CLIXLETS. Currently, a shutdown message addressed to the ClixManager itself will shut down the system.
CLIXLET messages
must contain a sender's name, a recipient's name, and a payload. Optionally, they may contain a type, which should be either CONVERSATION, PRESENCE, or INFOQUERY. CONVERSATION messages normally carry argument or result payloads
between CLIXLETS. PRESENCE messages may advertise
the availability of a service to a peer in the same namespace. INFOQUERY messages generally are directed to the ClixManager
itself. If a queue is included in a message, the message is automatically
enqueued on the given queue. On the other hand, you may put the message on a
particular queue yourself. Generally (and for convenience and simplicity sake,)
all messages can be routed through the ClixManager acting as a switchboard, but
nothing prevents an arbitrary pair of conversing CLIXLETS from exchanging their queues (as messages) and
conversing directly. It is left as an exercise for the implementer to parse
message payloads into expected arguments and package them into results
Here are
some examples:
self.managerQ.put( ClixMessage(sender=ME,receiver=self.sender, payload="http:''slashdot.org") ) # CONVERSATION Message
ClixMessage(sender=ME, receiver='ClixManager', queue=self.managerQ , payload="thisClixlet IN SERVICE", type=INFOQUERY) # INFOQUERY for the manager
What motivates CLIX is an interest in having a common and well-defined component management framework.
For example, let's say that you have a simple module such
as this:
# adder.py
def add(a,b):
return a+b
One way to use this defined function might be simply to
import it into the context of some python program:
# tester.py
from adder import *
x = add(31,11)
z = add('foo','bar')
Suppose however you have an arbitrarily large set of
components (methods and classes) you want to integrate, and furthermore:
In this case, CLIX provides a simple but elegant solution,
with relatively low overhead.
What's needed to extend an ordinary function into a CLIXLET is
for it to 1). add a method called registerClixlet() so that the module
can join into the clique, becoming a CLIXLET, 2). converse through queues, and 3). deal
with the incoming and outgoing payloads
So our trivial example module now does becomes this:
1:from clixMessage import ClixMessage
2:from clixMessageTypes import *
3:import Queue
4:import os
5:def add():
6: while True:
7: msg = gInputQueue.get()
8: args = msg.getPayload()
9: result = args[0] + args[1]
10: gClixManagerQueue.put(ClixMessage(sender=gClixletRegistry['name'],receiver=msg.getSender(), payload=result))
11:def registerClixlet(clixletRegistry=None, clixManagerQueue=None):
12: global gClixManagerQueue
13: global gInputQueue
14: global gClixletRegistry
15: gClixManagerQueue = clixManagerQueue
16: clixletRegistry['name'] = "adder"
17: clixletRegistry['function'] = add
18: clixletRegistry['params'] = ()
19:
20: # if this clixlet is going to talk across the databus, then give it input and possibly output queues:
21: gInputQueue = Queue.Queue()
22: clixletRegistry['inputQueue'] = gInputQueue
23: clixletRegistry['outputQueue'] = gClixManagerQueue
24: gClixletRegistry = clixletRegistry.copy()
This may seem like a bit of overhead for such a trivial
function; bear in mind that it is arbitrarily trivial for the sake of illustration.
Real applications (we would hope) are rather more complex.