Pyro Manual
version 0.6 (august 18, 1999)
Pyro is an acronym for PYthon Remote Objects. It is a basic Distributed
Object Technology system written entirely in Python, and for use in Python only. With
this, it closely resembles Java's Remote Method Invocation (RMI). It has less similarity to
CORBA - which is a system- and language independent
Distributed Object Technology and has much more to offer than Pyro or RMI. But Pyro is small,
simple, fun and free!
Contents
Introduction
Installation
Pieces of Pyro
The Rules of the Game
Example
For a short overview of what Pyro is, see the Pyro page online - available through the
resources section of my AmigaPython page (http://www.bigfoot.com/~irmen/python.html).
You can also download the package from there.
Copyright
This software is copyright © by Irmen de Jong. It is subject to change without notice. It
is completely without warranty, and provided "as is". Use it at your own risk.
In no event shall Irmen de Jong be liable for any damage resulting from errors in or
improper use of it. Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided that the
copyright notice appears in all copies and that both the copyright notice and the
permission appears in supporting documentation.
To get an idea of the way Pyro works, here is a scenario:
- The programmer writes a module 'test' containing a class 'testclass', which will be
accessed remotely.
- If the programmer wishes, client proxy code is generated for 'testclass',
using the PyroC proxy compiler. It is written to the 'test_proxy' module.
This is not necessary however, because Pyro has Dynamic Proxies too.
- The server creates one or more instances of the 'testclass', and registers them with the
Pyro Naming Service.
- The client queries the Naming Service for the location of those objects. It gets a Pyro
URI (Universal Resource Identifier) for them.
- The client creates proxies for the remote objects by creating 'test_proxy.testclass'
instances for the given URI, or just using a Dynamic Proxy.
- As the proxy appears just like the real 'testclass', the client can now invoke methods
on the remote objects.
Using dynamic proxies has some small drawbacks, one of them being the little
extra processing time each method call takes. Two big advantages are that you
no longer have to make and deploy proxy files, and that invocations on a derived
class work - they don't if you use generated proxies (XXX this is a known shortcoming
of the proxy compiler. It will be fixed in a future release).
Related technology:
- Java Remote Method Invocation (RMI)
- Common Object Request Broker Architecture (CORBA)
- Fnorb - a CORBA implementation in Python
- Daniel Larsson's RemoteCall
package
Extract the Pyro archive. It has five subdirectories:
- Pyro/
- move this directory somewhere in your Python search path. On most systems, the
lib/site-python directory is a nice place. On Windows, move it in the Python folder
itself.
- bin/
- move the contents of this directory somewhere in your shell search path.
- docs/ and test/
- put those whereever you like.
- extra/
- contains some extra files. For instance, 'SocketServer.py' is in here. This is the
Python 1.5.2 version of the SocketServer module. Pyro requires this version so if you
don't already use Python 1.5.2, make sure this newer (and bugfixed) SocketServer.py is
used, for instance, by copying it over the old version in the Python library directory.
Alternatively, you can keep the Pyro/ and bin/ directories where they are after
extraction, and manually add the Pyro directory to your Python search path, and the bin
directory to your shell search path.
Pieces of Pyro - XXX details must still be added XXX
Pyro package (Pyro)
Pyro is a Python package and as such is contained in the 'Pyro' directory. This
directory contains the different module files which make up the Pyro system. One of them
is the initialization file, __init__.py.
- Pyro.config
- Defined in __init__.py and initialized at the time the Pyro package is initialized (= as
soon as something in the package is imported). Pyro.config contains all configuration
items of Pyro, like the default network port numbers. You can simply access them as
Pyro.config.<item>
Core library (Pyro.core)
- ObjBase class
- Server side object implementation base class. Pyro object implementations
must inherit from this class.
- getProtocolAdapter()
- Creates a protocol adapter for a given protocol ID. You wouldn't normally
have to use this.
- PyroError/ProtocolError/URIError/DaemonError classes
- Different Pyro exceptions.
- PyroException class
- For remote transportation of exceptions.
- PyroURI class
- Pyro Universal Resource Indicator. Pyro uses this to locate objects. You
wouldn't normally have to use it directly.
- DynamicProxy
- A dynamic proxy. Clients don't need to have a pregenerated proxy file available
if they use this instead. It is slightly slower though and has some drawbacks.
- getProxyForURI
- Creates a new Dynamic Proxy for the specified object URI.
- Daemon class
- Server side Pyro daemon. It accepts incoming method calls and dispatches
them to the object implementations.
- initClient()
- Pyro client applications must call this function before using Pyro.
- initServer()
- Pyro server applications must call this function before using Pyro.
Naming Service (Pyro.naming)
- NamingError class
- Naming Service exception
- NameServerLocator class
- To locate the Pyro Naming Service. It uses a broadcast mechanism to find
it, and returns a Pyro Proxy for the NS.
- NameServerProxy class
- The proxy for the NameServer - this code has been generated by the PyroC
proxy compiler.
- NameServer class
- The Pyro Naming Service itself.
- PersistentNameServer class
- An experimental NS which uses a persistent database for the naming
table.
- startServer()
- Starts the Naming Service, including a broadcast server which will provide
clients with the address of the NS.
- main()
- Function to easily start the NS from a script.
Naming Service Control tool (Pyro.nsc)
NOTE: Use the nsc or nsc.bat script to start this tool from a shell.
- This tool can be used to control a Pyro Naming Service remotely. You can
examine the database, add/remove items, resolve objects by hand, and just
check if the Naming Service is running by sending a 'ping'.
GUI Naming Service Control tool (Pyro.xnsc)
NOTE: Use the xnsc or xnsc.bat script to start this tool from a shell.
- This utility is the same as the preceding one, but this one has a nice GUI.
Proxy Compiler (Pyro.pyroc)
NOTE: Use the pyroc or pyroc.bat script to start this tool from a shell.
This tool parses a Python source and generates static Pyro proxies for the
classes it finds.
Utilities (Pyro.util)
A module which contains miscellaneous utility functions.
- ArgVParser
- Basic argument list (sys.argv) parser class. It is slightly similar to the getopt() Unix
C function. The command line utilities use this to parse the command line arguments.
- getUUID()
- Creates a Universal Unique Identifier (ho-hum...). This is a string which can and is
used as a unique identifier.
Other files
- ns & ns.bat
- Scripts to start the Naming Service. You can provide the port number to use instead of
the default.
- nsc & nsc.bat, xnsc & xnsc.bat
- Scripts to start the Naming Service Control tool. With this you can manually
control the naming service. The 'x-scripts' will start the GUI version of
the NSC tool.
- pyroc & pyroc.bat
- Scripts to start the Pyro Proxy Compiler. You must provide the name of a module (without
.py) as an argument.
- genuuid & genuuid.bat
- Simply print a new UUID to the output.
- test/*
- The test directory contains some test files (client and server and object
implementation).
You should follow the following guidelines when using Pyro.
Concerning the class which is made remotely available:
- The class can't have its own __init__ method. You should instead use a regular
initialization method which you must call explicitly after object instantiation.
- The proxy compiler doesn't support inherited classes. This is an issue future
versions of Pyro might deal with. Note that when you use the Dynamic Proxy
everything works just fine!
- The class which is actually instantiated in the server should inherit from
Pyro.core.ObjBase. You could define a new (probably empty) class in the server
which inherits both from Pyro.core.ObjBase and the class which is made remotely
available. This approach is used in the example below.
(Note: this example is from Pyro 0.5. There are some slight differences, mostly in the output, when you use the actual version.)
- Write a module 'test' containing a class 'testclass', which will be accessed remotely.
from Pyro.core import PyroException
class testclass:
def mul(self, arg1, arg2): return arg1*arg2
def add(self, arg1, arg2): return arg1+arg2
def sub(self, arg1, arg2): return arg1-arg2
def div(self, arg1, arg2): return arg1/arg2
def error(self): raise PyroException(ValueError('Server generated exception'))
- Using PyroC, generate client proxy code for 'testclass'. You can skip this part if you
use the Dynamic Proxy in step 5.
[e:\irmen\projects\pyro]pyroc test
Python Remote Object Compiler (c)1999 by Irmen de Jong. Pyro V0.5
processing module 'test' (test.pyc)...
examining class PyroException ... imported from another module. Skipped.
examining class testclass ... 5 methods processed
Generating proxy for testclass
This release of Pyro doesn't need server-side skeleton code.
All done. Output can be found in test_proxy.py .
- Write a server, testserver.py, that creates one or more instances of the 'testclass',
and registers them with the Pyro Naming Service.
import sys
import Pyro.naming
import Pyro.core
import test
######## testclass object
class testclass(Pyro.core.ObjBase, test.testclass):
pass
######## main program
def main():
Pyro.core.initServer()
PyroDaemon = Pyro.core.Daemon()
locator = Pyro.naming.NameServerLocator()
print 'searching for Naming Service...'
ns = locator.getNS()
print ' found at',ns.URI.host,ns.URI.port
PyroDaemon.useNameServer(ns)
# connect a new object implementation:
PyroDaemon.connect(testclass(),'test')
print 'Ready.'
while 1:
PyroDaemon.handleRequests(3.0)
print '.',
sys.stdout.flush()
if __name__=='__main__':
main()
- Write a client, testclient.py, that will find the Naming Service, then query the NS for
the location of the object.
import sys
import Pyro.naming, Pyro.core
import test_proxy
Pyro.core.initClient()
locator = Pyro.naming.NameServerLocator()
print 'Searching Naming Service...',
ns = locator.getNS()
print 'Naming Service found at',ns.URI.host,ns.URI.port
print 'binding to object'
try:
URI=ns.resolve('test')
print 'URI:',URI
except Pyro.core.PyroError,x:
print 'Couldn\'t bind object, nameserver says:',x
raise SystemExit
- Let the client create a proxy for the remote object by creating a 'test_proxy.testclass'
instance for the given URI, or creating a Dynamic Proxy for the URI. Because the proxy
appears the same as the real 'testclass', the client can now invoke methods on the remote
objects.
test = test_proxy.testclass(URI)
# you could use the following line if you'd rather use a Dynamic Proxy:
#test = Pyro.core.getProxyForURI(URI)
print test.mul(111,9)
print test.add(100,222)
print test.sub(222,100)
print test.div(2.0,9.0)
print test.mul('*',10)
print test.add('String1','String2')
print test.error()
- Run the application in the network. First, start the Naming Service on one computer.
[e:\irmen\projects\pyro]ns
Pyro Server Initialized. Using Pyro V0.5
*** registered 'Pyro.NameServer' with URI PYRO://atlantis:9090/c0a8003768b7-37542e7e63f830ff-217e4cbb
Pyro Naming Service V0.5.
Running on atlantis port 9090 ('192.168.0.55', 9090)
Broadcast daemon on 192.168.0.55 port 9091
Ready.
- Start the server on another computer (or in a different shell).
[e:\irmen\projects\pyro\pyro-0_5\test]python testserver.py
Pyro Server Initialized. Using Pyro V0.5
searching for Naming Service...
found at atlantis 9090
Ready.
. . .
- Finally, run the client on a third computer (or in a different shell).
[e:\irmen\projects\pyro\pyro-0_5\test]python testclient.py
Pyro Client Initialized. Using Pyro V0.5
Searching Naming Service... Naming Service found at atlantis 9090
binding to object
URI: PYRO://atlantis/c0a80037c2ac-37542ec57df417ff-d018e974
999
322
122
0.222222222222
**********
String1String2
Traceback (innermost last):
File "testclient.py", line 31, in ?
print test.error()
File "test_proxy.py", line 27, in error
return self.adapter.remoteInvocation('error',0)
File "Pyro\core.py", line 109, in remoteInvocation
File "Pyro\core.py", line 157, in raiseEx
ValueError: Server generated exception
- You might want to peek in the Naming Server:
[e:\irmen\projects\pyro]nsc list
Finding NS using broadcast @ port 9091
LOCATOR: Searching Pyro Naming Service...
NS is at atlantis port 9090
-------------- START DATABASE
Pyro.NameServer --> PYRO://atlantis:9090/c0a8003768b7-37542e7e63f830ff-217e4cbb
test --> PYRO://atlantis/c0a80037c2ac-37542ec57df417ff-d018e974
-------------- END