<previous | top | next> Pyro Manual

4. Pyro Usage

Introduction

This chapter will show the Pyro development process: how to build a Pyro application. For starters, let's repeat the scenario from the Introduction chapter here, but with some more detail:
  1. You write a Python class that you want to access remotely. Do this as if it were a normal Python class (but see the rules in the Rules and Limitations chapter).
  2. You decide if you want to use static precompiled proxies, or Pyro's dynamic proxy. If you choose the latter, skip the next step.
  3. Using pyroc, the proxy compiler, you generate static client proxy code for your Python class.
  4. Write a server process that performs the following tasks:
  5. Write a client program that does the following:
  6. Make sure the Pyro Naming Server is running.
  7. Start your server process. If it complains that the names it wants to register already exist, use the nsc tool to unregister them, or restart the NS.
  8. Run the client!
In the following sections each step is explained in more detail.

Pyro script tools

Before using them let us first study the usage of the script tools. Pyro comes with two flavors, Un*x-style shellscripts and Windows/DOS batch files. The Windows-style batch files have the '.bat' extension. When you're using an Amiga, you can use the Un*x-style shellscripts when you've installed the latest 'ExecuteHack'. You might want to make them executable using protect +es #?. If it doesn't work, try executing them as regular python scripts, for instance: python nsc list. All scripts have to be called from a shell command prompt. Most of them accept command line parameters, see below.
genguid   (GUID generator)
- No arguments.
- This is a very simple GUID generator. It uses the internal Pyro GUID generator to print a new GUID.

pyroc   (Proxy compiler)
- One argument; the name of the module to process (without .py suffix)
- The proxy compiler generates a module with static client proxies for each class in the processed module. If necessary, server side skeleton code is also generated.

ns, pns   (Naming Server)
These scripts are explained in the Naming Service chapter.

nsc   (Naming Server Control tool)
- Arguments: [-h host] [-p port] command [args...]
- Controls the Pyro Naming Service. '-h host' specifies the host where the Naming Service should be contacted. '-p port' specifies a non-standard NS broadcast port to contact. 'command' is one of the following:
xnsc   (Graphical NS control tool)
- No arguments
- This is a graphical version of the nsc command-line tool. Currently it needs Tk for the GUI, so you have to have a Tk-enabled Python on your system. It doesn't work on AmigaPython for instance. The GUI is simple and should explain itself. You can enter the hostname in the textbox at the top and press <enter> to contact the NS at that host, or just press the 'Auto Discover' button at the top right. If the NS has been found, the rest of the buttons are enabled.

Steps 1, 2 and 3: Writing the remote class

Just create a Python module containing the classes you want to access remotely. There are some restrictions induced by Pyro: If you keep those in mind, you should be safe. You can use all Python types and parameter lists and exceptions in your code. Pyro will deal with those nicely.

Static or dynamic proxy?

Pyro supports two kinds of client-side proxies: static and dynamic. The most important difference between the two is that dynamic proxies don't require additional code because all logic is embedded in Pyro. Static proxies are defined in a Python module that is generated by pyroc. So you need to distribute these additional source files when you want to use static proxies. So why should you even use a static proxy?

Step 4: Writing the server

Initialization

You should initialize Pyro before using it in your server program. This is done by calling
   Pyro.core.initServer()
If you provide the argument '0', no banner is printed, otherwise a short message is printed on the standard output. If the tracelevel is not zero, a startup message is written to the log. This message shows the active configuration options.

Create a Pyro Daemon

Your server program must create a Pyro Daemon object, which contains all logic necessary for accepting incoming requests and dispatching them to your objects by invoking their methods. You also have to tell the daemon which Name Server to use. When connecting objects to the daemon (see below) it uses this NS to register those objects for you. This is convenient as you don't have to do it yourself.
   daemon = Pyro.core.Daemon()
   daemon.useNameServer(ns)
You can provide several arguments when creating the Daemon:
protocolthe protocol to use (defaults to "PYRO")
hostthe hostname to bind the server on (defaults to '' - the default host). This may be necessary in the case where your system has more than one hostname/IP address, for instance, when it has multiple network adapters. With this argument you can select the specific hostname to bind the server on.
portthe socket number to use (defaults to the PYRO_PORT configuration item)
norangewhether or not to try a range of sockets (leave this at the default value, 0)
publishhostthe hostname that the daemon will use when publishing URIs, in case of a firewall setup. See the Features chapter. Defaults to the value given to the host parameter.

The second line tells the daemon to use a certain Name Server (ns is a proxy for the NS, see the next paragraph how to get this proxy). It's possible to omit this call but the Daemon will no longer be able to register your objects with the NS. If you didn't register them yourself, it is impossible to find them. The daemon will log a warning if it doesn't know your NS.

Find the Naming Server

As pointed out in a previous chapter, there are essentially three ways on how to get a reference to the Naming Server:

Create object instances

The objects you create in the server that have to be remotely accessible can't be created bare-bones. They have to be decorated with some logic to fool them into thinking it is a regular python program that invokes their methods. This logic is incorporated in a special generic object base class that is part of the Pyro core: Pyro.core.ObjBase. There are three ways to achieve this:

Connect object instances

Ok, we're going nicely up to this point. We have some objects that even already have gotten a unique ID (that's part of the logic Pyro.core.ObjBase gives us). But Pyro still knows nothing about them. We have to let Pyro know we've created some objects and how they are called. Only then can they be accessed by remote client programs. So let's connect our objects with the Pyro Daemon we've created before (see above):
   daemon.connect(obj,'our_object')
That done, the daemon has registered our object with the NS too (if you told it where to find the NS, see above). The NS will now have an entry in its table that connects the name "our_object" to our specific object.
Note 1: if you don't provide a name, your object is a so-called transient object. The daemon will not register it with the naming service. This is useful when you create new Pyro objects on the server that are not full-blown objects but rather objects that are only accessible by the code that created them. Have a look at the factory and Bank2 examples if this is not clear.
Note 2: the connect method actually returns the URI that will identify this object. You can ignore this if you don't want to use it immediately without having to consult the name service.
Note 3: see the Naming Service chapter for more about this and persistent naming.

In contrast to the simple (flat) name shown above ("our_object"), Pyro's Name Server supports a hierarchical object naming scheme. For more information about this, see the Naming Service chapter.

The Daemon handleRequest loop

We're near the end of our server coding effort. The only thing left is the code that sits in a loop and processes incoming requests. Fortunately most of that is handled by a single method in the daemon, haldeRequests. Usually your loop will look something like this:
   while 1:
      daemon.handleRequests(3.0)
      ... do something when a timeout occured ...
The timeout value in this example is three seconds. The call to handleRequests returns when the timeout period has passed, or when at least one request was processed. You could use '0' for timeout, but this means the call returns directly if no requests are pending. If you want infinite timeout, use 'None'. You can also provide additional objects the daemon should wait on (multiplexing), to avoid having to split your program into multiple threads. You pass those objects, including a special callback function, as follows:
   daemon.handleRequests(timeout, [obj1,obj2,obj3], callback_func)
The second argument is a list of objects suitable for passing as ins list to the select system call. The last argument is a callback function. This function will be called when one of the objects in your list triggers. The function is called with one argument: the list of ready objects. For more information about this multiplexing issue, see the manual page about the Un*x select system call.

This concludes our server. Full listings can be found in the Example chapter.

Step 5: Writing the client

Initialization

You should initialize Pyro before using it in your client program. This is done by calling
   Pyro.core.initClient()
If you provide the argument '0', no banner is printed, otherwise a short message is printed on the standard output. If the tracelevel is not zero, a startup message is written to the log. This message shows the active configuration options.

Find the Naming Server

This part is identical to the way this is done in the server. See above. Let's assume that the variable ns now contains the proxy for the NS.

Find object URIs

There are essentially two ways to find an object URI by name: In contrast to the simple (flat) name shown above ("our_object"), Pyro's Name Server supports a hierarchical object naming scheme. For more information about this, see the Naming Service chapter.

Create a proxy

You now have a URI in your posession. But you need an object to call methods on. So you create a proxy object for the URI. You can choose to create a dynamic proxy or a static proxy. In the chapter about Pyro Concepts the difference is explained. For the example below, assume that pyroc has generated the mymodule_proxy.py proxy module.
   obj = Pyro.core.getProxyForURI(uri)     # get a dynamic proxy
   obj = Pyro.core.getAttrProxyForURI(uri) # get a dyn proxy with attribute support
   obj = mymodule_proxy.myobject(uri)      # get a static proxy (also w/ attrib support)

Remote method invocations

And now what we've all been waiting for: calling remote methods. This is what's Pyro's all about: there is no difference in calling a remote method or calling a method on a regular (local) Python object. Just go on and write:
   obj.method(arg1, arg2)
   print obj.getName()
   a = obj.answerQuestion('What is the meaning of life?')
   # the following statements only work with a attribute-capable proxy:
   attrib = obj.attrib
   obj.sum = obj.sum+1
or whatever methods your objects provide. The only thing to keep in mind is that you need a proxy object whose methods you call.

This concludes our client. Full listings can be found in the Example chapter. For information on using Pyro's logging/tracing facility, see Runtime control and Logging, below.

Steps 6, 7 and 8: Runtime setup

This part is a no-brainer, really:

Starting the Naming Server

A Pyro system needs at least one running Naming Server. So, if it's not already running, start one using the ns utility. See Pyro script tools. After starting it will print some information and then the Naming Server sits in a loop waiting for requests:
irmen@atlantis:~ > Pyro/bin/ns
Pyro Server Initialized. Using Pyro V1.5
Will accept shutdown requests.
URI written to: /home/irmen/Pyro_NS_URI
URI is: PYRO://192.168.0.99:9090/c0a80063-1f345031-95f5e910-5be75f20
Naming Service started.
The NS writes its URI to a file, as it says. This file can be read by other programs, and this is another -very portable- way to discover the NS. Usually you'll want to use the default mechanism from the NameServerLocator (automatic discovery using broadcasting). This is easier. But if your network doesn't support broadcasting, or the NS can't be reached by a broadcast (because it sits on another subnet, for instance), you have to use another method to reach the NS.

Running the server

Just start the python module as you do normally. Before starting, you may want to set certain environment variables to change some of Pyro's configuration items. After starting, your server will usually sit in a loop waiting for incoming requests (method calls, actually).

Running the client

Just start the python module as you do normally. Before starting, you may want to set certain environment variables to change some of Pyro's configuration items.

Runtime control and Logging

Controlling the Naming Server

You might want to control the NS while it's running. For instance, to inspect the current registered names or to remove an old name, or to register a new one by hand. You use the nsc command-line utility or the xnsc graphical tool for this purpose, see Pyro script tools.

Controlling Pyro

Pyro has many configuration items that can be changed also during runtime. You might want to set the tracelevel to 3 during a special function, for instance. See the chapter on Installation and Configuration for more information.

Tracing (logging)

Pyro has two distinct logs: the system log and the user log. The system log is used by Pyro itself. You can use it in your own code too, but generally it's better to use the user log. Also see the chapter on Installation and Configuration for detailed info on how to configure the logging options.

Last Notes

Please be sure to read the chapter on Configuration, the Naming Service and the chapter about Pyro's Features and Guidelines.

These chapters contain invaluable information about the more detailed aspects and possibilities of Pyro.