Package: qam
Introductionqamis a framework for remote-procedure-calls. It uses thecarrotmessaging framework. The RPC specific code is based onPython XML-RPC.Noteqamis not actively maintened anymore by the qamteam. The successor ofqamiscallme<http://pypi.python.org/pypi/callme>.callmeuses internally a more simpler approach asqamdue to the improvements the AMQP standard has made since QAM was released. Therefore we made a complete re-design and wrotecallmewhich has less overhead and is much faster thanqam.Key Features:uses AMQP as Transport Protocolsupports synchronous and asynchrouns remote method callssupports timeouts in synchronous and asynchronous modeJSON marshaling for high interoperatbility (seeSerialization: JSON vs. Picklefor details)Pickle marshaling for Object Transport SupportSupports Remote Exception Transfer in JSON/Pickle/Synchronous/Asynchrounus modeFully ThreadedEasy to UseOpenSource BSD-licensedThe AMQP messaging system manages the remote-procedure-calls for a client and a server. The client sends a amqp message to the server, where the method is specified which should be called on server.
After executing the function the result is packed into a amqp message and sent back to the client.The aim ofqamis to offer a simple RPC framework, which is reliable and secure.You have to install a AMQP message broker. The most popular are:RabbitMQZeroMQApache ActiveMQPersonally we have used RabbitMQ, it is easy to install and to configure.Before you start using theqamframework you should know a little bit about AMQP.
Therefor we advice you to readRabbits and warrensa very good article which introduces the basic ideas of AMQP.
Further you can look at thecarrotdocumentation. There you can get also good overview of AMQP.DocumentationThe full Documentation including Reference can be found atpypi documentationMailing listJoin the QAM Users mailing list:QAM-UsersIf you are developing inside QAM join:QAM-DevelopersBug Tracker:If you find any issues please report them onhttp://bitbucket.org/qamteam/qam/issues/Getting QAMYou can get the python package on thePython Package IndexThe Mercurial Repository is available atbitbucket.org qamInstallationqamcan be installed via the Python Package Index of from source.Usingeasy_installto installqam:$ easy_install qamIf you have downloaded a source tarball you can install it
by doing the following:$ python setup.py build
# python setup.py install # as rootTutorialStarting the QAMServerFirst it is necessary, to tell the QAMServer which functions are available. Therefore you have to register the functions on the QAMServer.
After registering the functions, the QAMServer waits for method-calls from the QAMProxy.Here is how you register a function on QAMServer and switch into the serving mode:>>> from qam.qam_server import QAMServer
>>> qam_server = QAMServer(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver')
...
>>> def adder_function(x, y):
...    return x + y
...
>>> qam_server.register_function(adder_function, 'add')
...
... # it is also possible to register the adder_function as follows:
... # qam_server.register_function(adder_function)
... # the method-name for registering in this case is adder_function.__name__
...
>>> qam_server.serve()It is also possible to register whole classes on QAMServer. Therefore you only have to register the class instance on the QAMServer.
It’s not necessary to register all functions of the class, it’s enough when you register the class instance.IMPORTANT:When you register an instance you must specify a name as second argument in theregister_class()method which selects the instance when calling it.
In the example below you would call the remote methodproxy.my_instance.adder_function(1,2)Here is how you register a class instance on QAMServer and switch into the serving mode:>>> from qam.qam_server import QAMServer
>>> qam_server = QAMServer(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver')
...
>>> class TestClass():
...    def __init__(self):
...        pass
...    def adder_function(self,a,b):
...        return a+b
...
>>> instance = TestClass()
>>> qam_server.register_class(instance,'my_instance')
>>> qam_server.serve()Managing RPC with QAMProxyThe QAMProxy sends the RPC-requests to the QAMServer and receives the result. It acts like a client which can call Server Methods and receive their
results.There are two different ways to receive the result:synchronous call: the QAMProxy blocks until a result arrivesasynchronous call: it is possible to register a callback-function which will be called when the result arrives.
In the meantime the QAMProxy can execute other functions or you can go in with your programm to execute.Synchronous RPCThis example shows you how to call a simple method registered on the QAMServer. You have to wait for the result, because no callback-function is registered. So it is a synchronous RPC.>>> from qam.qam_proxy import QAMProxy, QAMMethodNotFoundException, QAMException
... # create a new QAMProxy-instance
>>> qam_proxy = QAMProxy(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver',
...                    client_id='qamproxy')
...
>>> result = qam_proxy.add(2,3) # call method on QAMServer and wait for a result
... # close all open AMQP connections and cleanup
>>> qam_proxy.close()In case you have registered a class instance on the QAMServer and you want to call a method from this instance you can simple call this instance on QAMProxy.
You can call the instance with the name you specified withqam.qam_server.QAMServer.register_class(instance,name).
In this example it is a synchronous RPC again. You have to wait for the result.>>> from qam.qam_proxy import QAMProxy, QAMMethodNotFoundException, QAMException
>>> qam_proxy = QAMProxy(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver',
...                    client_id='qamproxy')
...
>>> result = qam_proxy.my_instance.adder_function(2,4)
>>> qam_proxy.close()Asynchronous RPCIf you don’t want to wait for the result it is possible to register a callback-function.
You can do this by callingQAMProxy.callback(callback_function,error_function).method_to_call_on_server(params).
The callback-function takes two parameters as arguments. The first is the callback-function.
The second is optional and is only called if an error occourd on QAMServer.SIDENOTE:It is highly recomended that you alway set a error_function as well, as
you can never know if the remote method will succeed or will throw an exception or if an internal exception will happen. Especially in asynchronous
calls the only way you will be notified in case of an error WITHOUT an error_function is the qam logging.After receiving the result from QAMServer the callback-function or the error-function is executed, with the result as parameter.
You can monitor the state of the callback withqam.qam_proxy.get_callback_state(uid). Possible States are:0: waiting on result1: processing (result arrived and callback/error function is currently executing)2: callback finished (callback/error function have finished executing)In the following example you can see how to use callback-functions. In this example a simple method-call on the Server should be executed.
No class instance is registered on the QAMServer, only the adder_function is registered.>>> from qam.qam_proxy import QAMProxy, QAMMethodNotFoundException, QAMException
>>> qam_proxy = QAMProxy(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver',
...                    client_id='qamproxy')
...
... # defining functions for callback
>>> def success(arg):
...    print arg
>>> def error(arg):
...    # if an error occours on QAMServer
...    print arg
>>> uid = qam_proxy.callback(success, error).add(2,4)
>>> while True:
...    state = qam_proxy.get_callback_state(uid)
...    if state == 2 :
...        # execution of callback finished
...        break
...
>>> qam_proxy.close()The function success and error are registered as callback and error function. If everything succeeds the success-function will be called.
If an error occoured on the QAMServer the error-function will be called.
If no error-function is defined and an error occourd a log-message is written into the logging system.It is also possible to execute asynchronous class-instance-method calls on the QAMServer. In the following example you can see how you can manage that.
You can call the instance with the name you specified withqam.qam_server.QAMServer.register_class(instance,name).>>> from qam.qam_proxy import QAMProxy, QAMMethodNotFoundException, QAMException
>>> qam_proxy = QAMProxy(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver',
...                    client_id='qamproxy')
...
... # defining functions for callback
>>> def success(arg):
...    print arg
>>> def error(arg):
...    # if an error occours on QAMServer
...    print arg
>>> uid = qam_proxy.callback(success, error).my_instance.adder_function(2,4)
>>> while True:
...    state = qam_proxy.get_callback_state(uid)
...    if state == 2 :
...        # execution of callback finished
...        break
...
>>> qam_proxy.close()TimeoutsIt is also possible to set timeouts for remote functions. E.g. you might use timeouts if you don’t want to wait longer than for example 10 seconds
for a function to return because after 10 seconds the result isn’t important for you anymore.A simple client side synchronous code would look like this:>>> from qam.qam_proxy import QAMProxy, QAMMethodNotFoundException, QAMException, QAMTimeoutException
... # create a new QAMProxy-instance
>>> qam_proxy = QAMProxy(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver',
...                    client_id='qamproxy')
...
>>> try:
>>>     result = qam_proxy.set_timeout(10).add(2,3) # call method on QAMServer and wait for a result
>>> except QAMTimeoutException:
>>>     print 'Remote function is too slow, timeout occoured.'
...# close all open AMQP connections and cleanup
>>> qam_proxy.close()But we also can set timeouts in asynchronous mode. The Callback/Error function will then only get called if it gets executed before the
timeout occours. If the timeout occours before the callback/error function gets executed, the error function gets called with aqam.qam_proxy.QAMTimeoutExceptionas argument. Let’s have a look at some sample code, again we assume that after 10 seconds result is
not anymore important to us.A simple client side asynchronous code would look like this:>>> from qam.qam_proxy import QAMProxy, QAMMethodNotFoundException, QAMException, QAMTimeoutException
>>> qam_proxy = QAMProxy(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver',
...                    client_id='qamproxy')
...
... # defining functions for callback
>>> def success(arg):
...    print arg
>>> def error(arg):
...    if isinstance(arg, QAMTimeoutException):
...      #timeout occoured
...      print 'Timeout occoured'
...    else:
...      print 'Other error happened'
>>> uid = qam_proxy.callback(success, error).set_timeout(10).add(2,4)
>>> while True:
...    state = qam_proxy.get_callback_state(uid)
...    if state == 2 :
...        # execution of callback finished
...        break
...
>>> qam_proxy.close()Internally, if an timeout occours it doesn’t matter if the actual function will return some day or if it will never return.
In case the remote function returns after the timeout exception has occoured the message will be correctly processed (so that everything stays
clean in the AMQP Subsystem) but the result will be thrown away.Serialization: JSON vs. PickleWhen working with QAM all the remote methods and results will get serialized in the background for you that you don’t have to bother about that.
But for flexibility you can choose between two serializer: JSON and Pickle. Both have their benefits and drawbacks. In most cases you will do fine
with the default Pickle serializer. But if you have special requirements you might choose the JSON serializer.IMPORTANT: Anyway which serializer you choose, you must specify the same serializer on the QAMServer and on the QAMProxy, otherwise
they can’t communicate correctly.SIDENOTE:The default serializer isPickle.To set the serializer on the proxy, here e.g. json:>>> from qam.qam_proxy import QAMProxy
... # create a new QAMProxy-instance
>>> qam_proxy = QAMProxy(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver',
...                    serializer='json')To set the serializer on the server, again json:>>> from qam.qam_server import QAMServer
>>> qam_server = QAMServer(hostname="localhost",
...                    port=5672,
...                    username='guest',
...                    password='guest',
...                    vhost='/',
...                    server_id='qamserver',
...                    serializer='json')To get an slight overview which serializer fits your need best here is a small comparison.
This comparision is specially trimmed for the use in QAM. In other environments there might be other things to be aware of.PropertyPickleJSONCompressiongoodno compressionComplex Object Transportyesonly json encodable
objects are supported
(dict, list, scalar)Support for Custom Exception Inheritanceyesno, but you can use
Exceptions as well. The
only drawback is that you
cannot create custom Exceptions
with inheritanceInteroperability with other languagesworsegood, as our transport format
is quite easy to implement in
other languages with json supportNeeds Complex Type Definitions on
both sides (Proxy and Server)yes, because proxy and
the server need to know
which type they will get.
You have to import your
custom Argument Classes or
custom Exceptions you want
to raise on both proxy side
and server side.no, because we can only transfer dict
list, and scalars we don’t need to
define them seperately.ArchitectureSupported byWingware - The Python IDE (http://wingware.com)ContributingWe are welcome everyone who wants to contribute to QAM. Development of QAM happens athttp://bitbucket.org/qamteam/qam/LicenseQAM is released under the BSD License. The full license text is in the root folder of the QAM Package.