Pyro-3.14/0000755000076500000240000000000011567263340012636 5ustar irmenstaff00000000000000Pyro-3.14/bin/0000755000076500000240000000000011567263337013414 5ustar irmenstaff00000000000000Pyro-3.14/bin/pyro-es0000644000076500000240000000014111517657576014740 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys from Pyro.EventService import Server Server.start(sys.argv[1:]) Pyro-3.14/bin/pyro-es.cmd0000644000076500000240000000015211517657576015504 0ustar irmenstaff00000000000000@echo off python -tt -c "from Pyro.EventService import Server; import sys; Server.start(sys.argv[1:])" %* Pyro-3.14/bin/pyro-esd0000644000076500000240000000142211517657576015107 0ustar irmenstaff00000000000000#!/usr/bin/env python ############################################################################# # # $Id: pyro-esd,v 1.2.2.2 2008/05/05 00:13:03 irmen Exp $ # Pyro Event Server Unix Daemon # Author: Jeff Bauer (jbauer@rubic.com) # This software is released under the MIT software license. # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import os, sys from Pyro.EventService import Server from Pyro.ext.daemonizer import Daemonizer class ESD(Daemonizer): def __init__(self): Daemonizer.__init__(self) def main_loop(self): Server.start(sys.argv[1:]) if __name__ == "__main__": ESD().process_command_line(sys.argv) Pyro-3.14/bin/pyro-essvc.cmd0000644000076500000240000000023311517657576016220 0ustar irmenstaff00000000000000@echo off python -tt -c "from Pyro.ext.ES_NtService import PyroES_NTService; import sys; sys.argv[0]='essvc.bat'; PyroES_NTService.HandleCommandLine()" %* Pyro-3.14/bin/pyro-genguid0000644000076500000240000000014011517657576015752 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.util Pyro.util.genguid_scripthelper(sys.argv[1:]) Pyro-3.14/bin/pyro-genguid.cmd0000644000076500000240000000014011517657576016514 0ustar irmenstaff00000000000000@echo off python -tt -c "import Pyro.util,sys; Pyro.util.genguid_scripthelper(sys.argv[1:])" %* Pyro-3.14/bin/pyro-ns0000644000076500000240000000012411517657576014752 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.naming Pyro.naming.main(sys.argv[1:]) Pyro-3.14/bin/pyro-ns.cmd0000644000076500000240000000012411517657576015514 0ustar irmenstaff00000000000000@echo off python -tt -c "import Pyro.naming,sys; Pyro.naming.main(sys.argv[1:])" %* Pyro-3.14/bin/pyro-nsc0000644000076500000240000000011611517657576015116 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.nsc Pyro.nsc.main(sys.argv[1:]) Pyro-3.14/bin/pyro-nsc.cmd0000644000076500000240000000011611517657576015660 0ustar irmenstaff00000000000000@echo off python -tt -c "import Pyro.nsc,sys; Pyro.nsc.main(sys.argv[1:])" %* Pyro-3.14/bin/pyro-nsd0000644000076500000240000000140311517657576015117 0ustar irmenstaff00000000000000#!/usr/bin/env python ############################################################################# # # $Id: pyro-nsd,v 1.2.2.2 2008/05/05 00:13:03 irmen Exp $ # Pyro Name Server Unix Daemon # Author: Jeff Bauer (jbauer@rubic.com) # This software is released under the MIT software license. # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import os, sys import Pyro.naming from Pyro.ext.daemonizer import Daemonizer class NSD(Daemonizer): def __init__(self): Daemonizer.__init__(self) def main_loop(self): Pyro.naming.main(sys.argv[1:]) if __name__ == "__main__": NSD().process_command_line(sys.argv) Pyro-3.14/bin/pyro-nssvc.cmd0000644000076500000240000000023311517657576016231 0ustar irmenstaff00000000000000@echo off python -tt -c "from Pyro.ext.NS_NtService import PyroNS_NTService; import sys; sys.argv[0]='nssvc.bat'; PyroNS_NTService.HandleCommandLine()" %* Pyro-3.14/bin/pyro-wxnsc0000644000076500000240000000012211517657576015472 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.wxnsc Pyro.wxnsc.main(sys.argv[1:]) Pyro-3.14/bin/pyro-wxnsc.cmd0000644000076500000240000000012211517657576016234 0ustar irmenstaff00000000000000@echo off python -tt -c "import Pyro.wxnsc,sys; Pyro.wxnsc.main(sys.argv[1:])" %* Pyro-3.14/bin/pyro-xnsc0000644000076500000240000000012011517657576015301 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.xnsc Pyro.xnsc.main(sys.argv[1:]) Pyro-3.14/bin/pyro-xnsc.cmd0000644000076500000240000000012011517657576016043 0ustar irmenstaff00000000000000@echo off python -tt -c "import Pyro.xnsc,sys; Pyro.xnsc.main(sys.argv[1:])" %* Pyro-3.14/docs/0000755000076500000240000000000011567263337013574 5ustar irmenstaff00000000000000Pyro-3.14/docs/1-intro.html0000644000076500000240000002121311556576221015747 0ustar irmenstaff00000000000000 PYRO - Introduction

1. Introduction

Thank you for choosing the often regarded number one Remote Method Invocation package for Python!

Pyro is short for PYthon Remote Objects. It is an advanced and powerful Distributed Object Technology system written entirely in Python, that is designed to be very easy to use. Never worry about writing network communication code again, when using Pyro you just write your Python objects like you would normally. With only a few lines of extra code, Pyro takes care of the network communication between your objects once you split them over different machines on the network. All the gory socket programming details are taken care of, you just call a method on a remote object as if it were a local object!

Pyro provides an object-oriented form of RPC. You can use Pyro within a single system but also use it for IPC. For those that are familiar with Java, Pyro resembles Java's Remote Method Invocation (RMI). It is less similar 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! For another overview of what Pyro is, see the Pyro page online (http://www.xs4all.nl/~irmen/pyro3). You can also download the package from there.

Copyright and Disclaimer
This software is copyright © by Irmen de Jong. It is subject to change without notice. Pyro comes as-is, without warranty and in no event can Irmen de Jong be held liable for any problems resulting from this software. License: Pyro is released under the MIT license, see the file "LICENSE".

Security Warning
In the Security chapter is an important security warning that you absolutely must read!

Features

To get an idea of how Pyro works, here is a scenario:

  1. You write a module 'test' containing a class 'testclass', which will be accessed remotely.
  2. The server creates one or more instances of the 'testclass', and registers them with the Pyro Name Server.
  3. The client queries the Name Server for the location of those objects. It gets a Pyro URI (Universal Resource Identifier) for them.
  4. The client creates proxies for the remote objects.
  5. Because the proxy mimics the real 'testclass', the client can now invoke methods on the remote objects. The proxy will forward the method invocations and return the results, just as if it was the remote object itself. Not a single line of network communication code has been written.
This looks like a big oversimplification, but it isn't!

To dive directly into the code, have a look at the various Pyro examples in the examples directory (read the Readme.txt). There is much to learn there. The easiest example to start with is the "simple" example, it shows how a normal python object is made into a Pyro object and accessed remotely from the client.

Required Software:

Important information Links

Related technology

Thanks

I want to thank everybody on the Pyro mailing list, your participation is of great value. And to everybody who contacted me about Pyro, for positive remarks, comments, or with problems (it all helps to improve Pyro): Thank You!

Pyro-3.14/docs/10-errors.html0000644000076500000240000005562611552620353016217 0ustar irmenstaff00000000000000 PYRO - Errors and Troubleshooting

10. Errors and Troubleshooting

The Pyro Errors section describes the various errors that can occur when using Pyro. It only describes Pyro's custom exceptions - other Python exceptions may occur due to other runtime problems.

The Troubleshooting section provides various hints and tips when you can't get things to work as expected.

The Specific issues section at the end contains more detailed discussion of specific issues you may encounter:

Pyro Errors

Pyro Exception Hierarchy

Pyro's exception extensions

Pyro transports a remote exception (and traceback) to the client, where it is raised again as if it occured locally. Pyro provides some powerful debugging possibilities (also see Dealing with Exceptions):

What error occurs where and when?

Initialization

OSError-- is raised when Pyro needs but can't create or open the storage directory specified by PYRO_STORAGE.

Name Server

NamingError-- The Name Server raises a NamingError exception (raised by the remote NS object) if an error condition occurs. It usually has a detailed error description in the args attribute. That is a tuple of one or two elements. The first element is the error string (such as "name not found") and the second (if present) is the offending name.

PyroError-- The NameServerLocator raises PyroError "Name Server not responding" if it failed to discover the Name Server (via the getNS method).

Also, the PYROAdapter raises NoModuleError "No module named XYZ" if it cannot import a Python module. See below in the table.

Also, the startup code in configuration.py raises PyroError "Error reading config file" if Pyro's configuration file cannot be read.

Pyro Core

URIError-- The PyroURI object raises URIError "illegal URI format" when you try to create a PyroURI object from an illegal URI. Also "unknown host" means that you supplied an unknown hostname or IP address for the URI.

DaemonError-- The Daemon object raises DaemonError "Couldn't start Pyro daemon" if it was unable to start. This usually means that the required network port (socket) is temporarily unavailable because another Pyro daemon is already running on it.

PYRO Protocol adapter

Various exceptions may be raised by the PYRO protocol code:
Exception Error string raised by description
ConnectionClosedError connection lost receive code The network connection was lost while receiving data.
ConnectionClosedError connection lost send code The network connection was lost while sending data.
ProtocolError incompatible protocol in URI bindToURI method of PYROAdapter You supplied an incompatible protocol ID. Use 'PYRO'.
ProtocolError connection failed bindToURI method of PYROAdapter Network problems caused the connection to fail. Also the Pyro server may have crashed.
ProtocolError invalid header receive code A message with an invalid header has been received.
ProtocolError incompatible version receive code A message from an incompatible Pyro version has been received.
ProtocolError unsupported protocol getProtocolAdapter You tried to get a protocol adapter for an unsupported protocol. Currently only the 'PYRO' protocol is supported.
ProtocolError compression not supported receive code An incoming message used compression, which this Pyro setup doesn't support (most likely you don't have the zlib module).
ProtocolError unknown object ID receive code Incoming method call for an unknown object (i.e. the object ID was not found in the Daemon's list of active objects).
NoModuleError No module named XYZ remote method call code The Pyro server cannot import the Python module XYZ that contains the code for an object that was passed in a remote call. If you don't use mobile code, the Pyro server must have all .py files available.
PyroError attempt to supply code denied remote method call code Either a client tried to submit code to a server that hasn't enabled mobile code, or the server had enabled mobile code but the codeValidator refused to accept it.
ConnectionDeniedError unspecified reason bindToURI method of PYROAdapter No specific reason given for denying the connection
ConnectionDeniedError server too busy bindToURI method of PYROAdapter The Pyro server has too many connections at this time so your request for another connection was denied.
ConnectionDeniedError host blocked bindToURI method of PYROAdapter The server blocks your host/IP address.
ConnectionDeniedError security reasons bindToURI method of PYROAdapter General security issue prevented a connection.
TimeoutError connection timeout while transferring data in PyroAdapter The communication took longer that the timeout perioud that was specified. (default: off)

Util

KeyError-- The ArgParser will raise KeyError "no such option" if you try to getOpt an option that has not been detected in the option string and you have not supplied a default value to getOpt (otherwise that default value is returned).

Troubleshooting

Use python -m Pyro.configuration to get a printout of Pyro's active configuration settings.


Problem Possible cause Hint/tip
The Name Server or my own Pyro server refuses to start, aborts with DaemonError The daemon can't set up the network port (socket), probably because another daemon is already running, or another process occupies the resource. Wait a little while and try again (sometimes it takes a while for resources to become available again). If this doesn't work, find the other daemon that is probably running and shut it down, or specify another port for the new daemon. Sorry, but your new process can't yet use the daemon of another running process - perhaps in a future Pyro version.

It could also be that there is something special about your network setup, like: multiple NICs / IP addresses on a single host. Please read the "Features and Guidelines" chapter.

Name Server acts weird with names or groups not using built-in static NS proxy always use the Pyro.naming.NameServerProxy returned by the Name Server Locator.
Pyro refuses to initialize, aborts with IOError and/or things saying 'permission denied' or similar You probably specified a wrong location for Pyro's storage directory and/or logfiles. Perhaps you don't have permission to write in those locations. Fix those locations by changing the configuration items. Make sure you have permission to write to those locations. Usually PYRO_STORAGE must be changed to point to a correct directory.
My code can't find the Name Server, it aborts with "Name Server not responding" The Pyro Name Server is not running or it can't be discovered automatically. The latter may be the case if your network doesn't support broadcasts or if the NS is running on a different subnet that cannot be reached by a broadcast. Start the NS if it's not running. If the problem persists, you have to help the NameServerLocator by specifying the host and perhaps even the port number where the NS is running. You can also try to obtain the NS' URI string and create a proxy directly from this string, bypassing the NameServerLocator.
Still can't contact Name Server or my Pyro objects, but NS is active. Log has warning about local address 127.0.0.1. NS or daemon binds on loopback address due to faulty network config. Look at the specific issues at the bottom for a discussion about this problem.
'name not absolute' errors after changing default namespace group. The default group name for the PYRO_NS_DEFAULTGROUP config item must be an absolute name (starting with the root char). Change it to an absolute name.
Client can't bind to URI or find URI in Name Server. NS is running ok, server is running ok. The URI is not registered correctly in the Name Server. Make sure that the client accesses the correct Name Server (the one that the server used to register the object), especially if you're running multiple NS. Also make sure that the client uses the correct URI.
Client problems when accessing Pyro object attributes, client prints stuff like <bound method DynamicProxy._invokePYRO of DynamicProxy ... > instead of attribute value You are using regular dynamic proxy Use attribute enabled proxy; see getAttrProxyForURI.
Client crashes with "No module named ..." or similar when accessing attributes The attribute object is an instance of a class that cannot be found/imported on the client make the module that contains every attribute class available on the client, or write getters.
Pyro doesn't use my configuration settings! Other settings overrule your settings. Check if there is an environment variable that overrules your config file, or perhaps even a setting in your code..
Pyro object doesn't initialise correctly Pyro objects cannot have a remote __init__ method Use explicit remote initialization by using a custom init method..
General network errors when using firewall or multiple network adapters (can't find NS, Pyro uses wrong IP address in URIs) Firewalls need special treatment Read the chapter Features and Guidelines.
Client cannot connect anymore after restarting the server Pyro URIs change every time the server is restarted, and the client uses an old URI update the URI on the client, or use the Name Server, or use Persistent Naming.
Deadlock (Pyro freezes) Callback waits for other object (occurs in 'conversation' or 'looping' invocation) Make sure your Daemon is running in multithreaded mode. Also consider using Oneway invocations or the Event Server.
Pyro Object created on server and returned to client doesn't work You are probably not returning a proxy but the object itself. Return a proxy instead: return Obj.getAttrProxy()
PicklingError or TypeError: can't pickle .... objects You're passing an unpickleable object to Pyro Remove any nonpickleable objects. You might want to try custom __getstate__ and __setstate__ methods.
Other PicklingErrors or 'invalid load key' or other vague errors Incompatilbe Python version or version mismatch You need at least Python 2.3 or newer, and you need the same major Python version everywhere. Otherwise the pickle protocol datastream is incompatible.
Syntax Error exception when using mobile code Pyro couldn't compile the downloaded code It could be that you have old *.pyc or *.pyo files around from a different Python version. Remove these files. If not, your source has a true syntax error in it, try to import it standalone and see if it works.
PyroError 'server wanted a non-existing module' when using mobile code You're probably importing other modules from within your mobile agent module, using a relative package/module name When you import other modules from your mobile object, you have to use the fully qualified module name. Using a package local module name doesn't work. See the agent3 example.
Other problem not mentioned here Your code probably breaks a Pyro rule read the the chapter Features and Guidelines.
SSL problems ? Sorry, I don't know much about SSL. Ask on Usenet or the mailing list.
Trouble using object.attr.subattr.subsubattr or calling 'nested' methods like obj.address.getStreet() All classes that are used for the nested attributes must be available on the client. See the "attributes" example.
Troubles with floating point numbers such as INF or NaN. Python's use of these numbers isn't standardized, and they cannot be pickled/unpickled. Python 2.5 contains some fixes for this issue. Might not work on Windows though.
Pyro crashes. Most likely a bug. Contact me.

Specific issues

127.0.0.1 connection problems

Sometimes you may encounter a problem with connecting to your Pyro server over the network, and Pyro complains that it cannot connect to your server. If you see some complaints about '127.0.0.1', 'localhost' or 'loopback adapter' in the error or in the log, continue reading.

What is happening? Pyro has connected the server to 127.0.0.1 which is the local loopback network. This means that the server is not accessible from anything but the local host. Pyro code on a different host can never make a connection to this server because even if it obtains an URI from the server, the address in it will be 127.0.0.1 and that means: 'hey, I am on your local host'. It then tries to connect to the server on the local host, but that won't work for obvious reasons.

Why is this happening? You may have told Pyro to use the loopback address yourself, by setting the hostname to bind on to 'localhost' or '127.0.0.1'. That is fine as long as you're only running stuff on this single host, but if external machines try to connect to it it will fail. The other explanation is that the hostname Pyro uses to bind the server on, resolves (via DNS) to the IP address of the loopback adapter (127.0.0.1). Quite a few Linux distributions are set up like this unfortunately, where the hostname of your computer resolves to 127.0.0.1. (you can usually check this by typing ping yourhostname at a shell prompt). When Pyro tries to bind the server to a network adapter, it is then likely it ends up with 127.0.0.1.

What can I do about it? There are several options to fix this problem:

Problems with user defined exception classes

In Python 2.5 and later, there is an issue with user-defined exceptions. Say you have a custom exception class written as follows:

class MyEx(Exception):
    def __init__(self, mymessage):
        self.mymessage=mymessage
    def __str__(self):
        return "[MyEx '%s']" % self.mymessage

If you are passing these exceptions over the wire in Pyro method calls, you will find that your program crashes with an error like this:

TypeError: ('__init__() takes exactly 2 arguments (1 given)', <class '__main__.MyEx'>, ())

This is not Pyro's fault: it is caused by a change in the Exception classes which was introduced in Python 2.5. This change seems to cause that Exception objects don't follow the usual pickle protocol. The unpickling of the exception object fails, because the __init__ method is called without any arguments (it shouldn't even be called at all by the way!). Unfortunately, you won't be able to fix this problem by writing a __getinitargs__ or __getnewargs__ method to force the correct parameters, because these methods won't be called. The easiest way to work around this problem now seems to change the init method so that it uses *args instead of a single argument (this is what the built in Exceptions seem to do), or change it a little so that it also works with zero arguments:

    def __init__(self, mymessage=None):
        self.mymessage=mymessage

If you don't need any special behavior of your own exception objects, it is probably best to just subclass them from Exception and not define any custom methods or properties. That avoids the problem as well. All builtin exceptions should accept a string argument to be used as the exception message, so there is no need to subclass the exception and add custom behavior as shown above, if you just want to remember a message string.

Pyro-3.14/docs/11-implementation.html0000644000076500000240000004126511526311451017720 0ustar irmenstaff00000000000000 PYRO - Implementation

11. Pyro's Implementation

The purpose of this chapter is to provide some insight in the Pyro's implementation. However please keep in mind that it is quite tedious for me to update this chapter with each Pyro release, so I want to stress the fact that the source code is probably the best place to look if you want to find out specific details about Pyro's implementation.

Nevertheless this chapter is a nice starting point to explore the different parts that make up Pyro.

The Pyro Principle

How does Pyro work??? Pyro uses two main principles to do what it does: method call interception (actually, attribute access interception) and marshalling.

Your code doesn't call a Pyro object directly. It calls a method on a proxy that acts just like the Pyro object it represents. The proxy intercepts the method call by using a special implementation of __getattr__, that passes the call to _invokePYRO. In turn, that method forwards the call to the protocol adapter, that creates a message containing all the details of the call (using pickle to marshall all Python objects). The message is passed via the network to the Pyro daemon on the other side.

The daemon receives the message and unmarshalls the request. It now knows the objectID of the required Pyro objects, the desired member function to call, and its arguments. It looks up the Pyro Object in its table and calls the Pyro_dyncall method. This method is in ObjBase which is the base class of all Pyro objects. This method uses the appropriate apply call on itself to call the actual method. The result of the method is handed back to the Pyro daemon, that marshalls it, and passes it back over the network to the calling protocol adapter. The adapter on the client side unmarshalls the result data, hands it back to the proxy that called it, and then the _invokePYRO method of the proxy returns the result data as if it was obtained using a regular method call on a local object!

This was Pyro in a nutshell. There are many more things to consider, such as transporting modules (mobile objects or even mobile agents) and oneway calls, but you have to read the source anyway to find all the gory details.

The command-line tools (bin/*)

The command-line tools are all in fact very small Python scripts. They contain a few Python statements that start the actual tools, which are part of the Pyro package:
Tool Implemented in Notes
pyro-es Pyro.EventService.Server start Event Server
pyro-nsc Pyro.nsc control Name Server
pyro-xnsc Pyro.xnsc Tkinter-GUI version of nsc
pyro-wxnsc Pyro.wxnsc WxPython/WxWindows version of nsc, with nice tree view of namespace
pyro-genguid Pyro.util create a GUID, entrypoint is genguid_scripthelper()
pyro-ns Pyro.naming start Name Server, entrypoint is main()
pyro-nsd / pyro-esd Pyro.ext.daemonizer Unix initd daemon scripts for name server and event server
pyro-nssvc / pyro-essvc Pyro.ext.NS_NtService and ES_NTService Windows NT Service control scripts for NS and ES

Pyro package initializer (Pyro/__init__.py)

Pyro is a Python package and as such is contained in the 'Pyro' directory. This directory contains the different module files that make up the Pyro system. One of them is the initialization file, __init__.py. It loads the configuration settings for Pyro (Pyro.config.* items). This way, whenever a module from the Pyro package is imported, it can use the config items right away.

Within the core package is also another package; Pyro.EventService. The event service is implemented here: the Event Server itself and the base classes for event publishers and listeners.

Configuration (Pyro/configuration.py)

This module contains the logic that deals with Pyro's configuration. There is a Config class and a ConfigReader class. The Config class is the container for all configuration items. The instance is available as Pyro.config (created in the package initializer, see above). The configuration items are all attributes of this object. Config uses the ConfigReader to read Pyro's configuration file. It deals with defaults and environment settings too.

Constants (Pyro/constants.py)

This module contains all globally used constants. By placing them in a distinct module that doesn't import any other modules (apart from util), we can also prevent import cycles between other Pyro modules. Things that are defined here are for example the Pyro version string, and the names for the Name Server and Event Server.

Core library (Pyro/core.py)

This module contains all core logic of Pyro that is not tied to the network protocol that is used. This means that you will not find any TCP/IP socket specific code in the core module; the protocol module contains this. What the core module does contain is all stuff that realize the core functions of Pyro:
ObjBase
Server-side object implementation base class or master class with the actual object as delegate. It supplies the methods for remote method invocation (Pyro_dyncall) and remote attribute access (remote_getattr etc.)
PyroURI
Pyro Universal Resource Identifier. This class represents a Pyro URI (which consists of four parts: a protocol identifier, an IP address, a portnumber, and an object ID). PyroURI can be converted to and from a - human readable - string.
DynamicProxy and DynamicProxyWithAttrs
These two classes are the dynamic Pyro proxies. They can be used by clients to invoke objects for which they have no precompiled proxy. The "WithAttrs" proxy is needed if you want to do direct attribute access on remote Pyro objects. The proxies have a special _invokePYRO method that intercepts method calls to pass them via the protocol adapter to the remote Pyro object. Special care is taken to make proxy objects suitable for pickling so they can be transported trough the Pyro protocol across the network.
getProxyForURI and getAttrProxyForURI
Helper functions to create proxies directly from a given Pyro URI (object).
Daemon
The server-side Pyro daemon. Accepts and dispatches incoming Pyro method calls. It is derived from the low level TCPServer and contains the server end of a protocol adapter. The daemon passes incoming method calls (via its handleRequest method) to the protocol adapter that sorts out what to do exactly. The connect and disconnect methods are used to keep track of server side Pyro objects. The Daemon's handleError is used in case of a Pyro error, it processes the error and cleans things up.
Pyro client and Pyro server initialization code
The initClient and initServer functions perform necessary initialization. For instance, the server init code first checks the availability of the PYRO_STORAGE directory.

Name Server (Pyro/naming.py)

This module contains all logic that deals with object naming and locations. Pyro's Name Server is implemented here as well as various other things.
NameServerLocator
Used to locate the Name Server by using various lookup methods (broadcast, direct lookup). Usually you call the getNS method and you get a Pyro Proxy for the Name Server back.
NameServerProxy
A built-in proxy for the Name Server that takes care of the naming rules (global names, default groups). You get this back from the NameServerLocator.getNS.
NameServer and PersistentNameServer
The actual pyro object that is the Name Server. It is created by some utility code that also connects it to a daemon. This code is called by the ns script that starts the Name Server.
Various classes for hierarchical naming support, they implement a tree structure.
We have a (name,value) tuple NameValue and a NamedTree recursive data structure.
BroadcastServer
The helper server that listens to UDP broadcast events from the locator, and replies with the URI of the Name Server object. This server's socket is joined with the regular Pyro Daemon that listens for incoming Pyro calls for the NS object.

Protocol Adapters (Pyro/protocol.py)

The idea behind this module is to make it fairly straightforward to switch the underlying protocol that Pyro uses. Currently only one implementation is available: the native PYRO protocol that sits on top of TCP/IP. This module contains all TCP/IP socket related code.

Utilities (Pyro/util.py)

This module defines various utility functions and classes. The most important ones are:
LoggerBase
This is the abstract base class of the various logging classes, see the next two items. Don't use this directly.
SystemLogger
An object of this class is directly available as Pyro.util.Log and this is Pyro's system logger that writes to Pyro's system logfile.
UserLogger
An object of this class can be constructed to create a user logger that writes to the user logfile.
ArgParser
A simple command line argument parser like the getopt function in ANSI C. Pyro's command line tools use this. It can parse command line options according to an option specification string. The getOpt member is used to read the results.
getGUID
Generates a new GUID. This is essentially a big number that is unique in time and in space. Unique in time means that every moment you generate a new GUID it is different from the ones you generated before. Pyro does this by using a time stamp as part of the GUID. Unique in space means that when you generate a GUID on a different machine, it will be different from the GUID you generated elsewhere (even when you generate them exactly the same moment). Pyro does this by using the network address of your computer as part of the GUID, and the Python process id. The latter is necessary because multiple Python processes may be running on the same IP address. The 128-bit GUID is returned as a hexlified string of 32 characters.
genguid_scripthelper
This helper function is used by the genguid command line utility, which prints a new GUID.
getPyroTraceback
Gets the remote traceback from the exception that is caught on the client, augments this with the local traceback, and returns a list of traceback steps. Use this to find out which part of the remote code caused the exception.

Exception definitions (Pyro/errors.py)

This module defines Pyro.PyroError and its derived exceptions. PyroError is the Pyro exception type that is used for problems within Pyro. User code should not use it! Also, this module defines the PyroExceptionCapsule class, which is used to represent any Python exception that has to be transported across the network, and raised on the other side (by invoking raiseEx on this object).

Event Server package (Pyro/EventService)

This package contains the Event Server modules:
Pyro/EventService/Clients.py
Here are the Subscriber and Publisher base classes defined that you can use to easily build publisher or listener clients for the ES.
Pyro/EventService/Server.py
Contains the actual EventService Pyro object that is started by some utility code. This module is called by the es script that you can use to start the Event Server.

Extensions package (Pyro/ext)

This package is deprecated. Don't use it in new code!
This package contains optional modules that provide certain extensions to the core Pyro library. It contains:
Pyro/ext/remote.py
The easy-remoting module that is shown in the "quickstart" example.
Pyro/ext/remote_nons.py
The easy-remoting module that doesn't use the Name Server, it is shown in the "quickstart-noNS" example.
Pyro/ext/daemonizer.py
Utility classes to "daemonize" a Pyro server (Unix only). Used by the nsd and esd initd daemon scripts for instance.
Pyro/ext/BasicNTService.py
Utility classes for creating Windows NT Services (Windows NT only).
Pyro/ext/NS_NtService.py
The Name Server as Windows NT Service.
Pyro/ext/ES_NtService.py
The Event Server as Windows NT Service.
Pyro/ext/ServiceTest.py
Test sets for the Windows NT Services.
Pyro-3.14/docs/12-changes-archive.html0000644000076500000240000021731211526311451017721 0ustar irmenstaff00000000000000 PYRO - Change Log (archive)

Pyro Change Log (archive)

The recent Pyro versions can be found in the regular change log.

Pyro 0.1 has been lost in the dust. It wasn't suited for publication anyway, I guess :-)

Pyro 0.2

First working release. Only static proxies and very little configurability.

Pyro 0.3

Pyro 0.4

Pyro 0.5

Pyro 0.6

Pyro 0.7

Pyro 0.8

Pyro 1.0

Pyro 1.1

Pyro 1.2

Pyro 1.3

Pyro 1.4

Pyro 1.5

Pyro 2.0

Pyro 2.1

Pyro 2.2

Pyro 2.3

Pyro 2.4

Pyro 2.5

Pyro 2.6

Pyro 2.7

Pyro 2.8

Pyro 3.0

Pyro 3.1

Pyro 3.2

Pyro 3.3

Pyro 3.4

Pyro 3.5

Pyro 3.6

Pyro 3.7

Pyro-3.14/docs/12-changes.html0000644000076500000240000002612311567263106016310 0ustar irmenstaff00000000000000 PYRO - Change Log

12. Pyro Change Log

(most recent entries at the end)

Pyro versions before 3.8 can be found in the archive.

Pyro 3.8.1

Pyro 3.9.1

Pyro 3.10

Pyro 3.11

Pyro 3.12

Pyro 3.13

Pyro 3.14

Pyro-3.14/docs/2-concepts.html0000644000076500000240000003575011526311451016433 0ustar irmenstaff00000000000000 PYRO - Concepts

2. Pyro Concepts

Introduction

For a good understanding of Pyro it is necessary to know the different elements in a Pyro system. This chapter summarizes them. Keep in mind that in a distributed object system, the client/server style is difficult to see. Most of the time all parts of the system switch roles, one moment it's a client calling a remote object, the other moment it is itself an object that is called from other parts of the system. For a good understanding though, it is important to see that during a single method call, there are always two distinct parts of the system: the client part that initiates the method call, and the server part that accepts and executes the call. To be precise, there are actually three parts: between the client and the server is the distributed object middleware, in this case: Pyro.

Another issue is that a client can - of course! - use more than one remote object, but also that a single server can have more than one object implementation. Thus, single objects in their own right are neither a client nor a server. I'll define the executable parts of the system that contain the objects as clients and servers (depending on their actual role). For simple Pyro applications, usually the different Python modules clearly show the different parts of the system, and they are the clients and servers I'm talking about here.

If you want a technical and more in-depth description of the material presented here, read the chapter on Pyro's implementation, or just browse the source code.

Pyro shell scripts

Pyro provides several tools to help you during development of a Pyro application. The Pyro Name Server is also started and controlled by two of these scripts. See the chapter on the Name Server for more information about them.

Client program

This is the part of your system that sends requests to the server program, to perform certain actions. It's the code that actually uses the remote objects by calling their methods.
Pyro client programs look suspiciously like normal Python programs. But that's the whole point of Pyro: it enables you to build distributed object systems with minimal effort. It makes the use of remote objects (almost) transparent.
Client code has to perform some initialization and setup steps. And because we're talking remote objects here, they cannot create object instances in the usual way. They have to use a two-step mechanism:
  1. Find the location identifier of the required object. This is done by using the Pyro Name Server, see below.
  2. Create a special kind of object that actually calls the remote object. This is called a proxy, see below.
Once it has this proxy object, the client can call it just as if it were a regular -local- Python object.

Server program

The server is the home of the objects that are accessed remotely. Every object instance has to be part of a Python program, so this is it. The server has to do several things:
  1. Create object instances using a extremely tiny bit of Pyro plumbing
  2. Give names to those instances, and register these with the Name Server
  3. Announce to Pyro that it has to take care of these instances
  4. Tell Pyro to sit idle in a loop waiting for incoming method calls

Remote object

Aside from the restrictions given in the Features and Guidelines chapter, Pyro object is just a regular Python object. The object doesn't know and doesn't have to know it's part of a Pyro server, and called remotely.

Proxy

A proxy is a special kind of object that acts as if it were the actual -remote- object. Pyro clients have to use proxies to forward method calls to the remote objects, and pass results back to the calling code. Pyro knows two kinds of proxies, and you are free to chose from them:
  1. Dynamic proxy.
    This is a very special Python object provided by Pyro. It is a general proxy for all remote objects!
  2. Dynamic proxy with attribute access support.
    This allows you to access object attributes directly with normal Python syntax (for instance, print RemoteObj.name prints the attribute 'name' of the remote object 'RemoteObj'). Because this requires even more builtin logic, this proxy is a few percent slower than the others. You can choose to use this proxy, or one of the others. Note: pay attention to the following issue when using attribute proxies: they might raise unexptected exceptions on the client! If your attribute is an object of a certain class that is available on the server, but not on the client, you will not be able to access the attribute on the client! Also, when you are using 'nested' attributes (such as RemoteObj.person.address.street) you must make sure that all needed classes (and modules) are available on the client. It is perhaps easiest to put such classes in separate modules and place them somewhere where the client is able to import them. An explicit import statement is not necessary in the client. When done correctly, you're also able to call 'nested' methods such as RemoteObj.person.address.getStreet(). The "attributes" example shows how it's done. But there are some serious pitfalls when using 'nested' attribute or method access! Please read the info about this in the Features and Guidelines chapter!
Proxies are bound to certain location identifiers, so they know on whose behalf the're running.

Proxy objects can be pickled: you can toss proxy objects around within your Pyro system. For more info see the guidelines in the "Features and Guidelines" chapter.

Pyro daemon

It's getting more technical now. Each server has to have a way of getting remote method calls and dispatching them to the required objects. For this task Pyro provides a Daemon. A server just creates one of those and tells it to sit waiting for incoming requests. The Daemon takes care of everything from that moment on. Every server has one Daemon that knows about all the Pyro objects the server provides.

Location discovery and naming

One of the most useful services a distributed object system can have is a Name Server. Such a service is a central database that knows the names and corresponding locations of all objects in the system.
Pyro has a Name Server that performs this task very well. Pyro Servers register their objects and location with the Name Server. Pyro Clients query the server for location identifiers. They do this by providing (human-readable) object names. They get a Pyro Universal Resource Identifier (URI) in return (which is not intended for humans. However, as it is, the current PYRO URIs look just like WWW URLs and can be read quite nicely).
You might be wondering: how can a Pyro client find the Name Server itself?! Good question. There are three possibilities: Object names can be anything you like as long as there isn't another object with that name already. So it's good practice to use some sort of hierarchical naming scheme, just like Java uses for Java package naming. This reduces the risk of naming clashes dramatically. In fact, Pyro's Name Server is a fully hierarchical naming service that has a filesystem-like directory structure of groups and object names in those groups. See the Name Server chapter for a description of the naming scheme.
Notice: the Pyro.xxxxx namespace is reserved for Pyro itself (for instance, the Name Server is called Pyro.NameServer). Don't use it.

There are two other ways to connect to a certain object you know the name of. These are the PYRONAME:// and the PYROLOC:// URI formats, instead of the regular PYRO:// URIs. The first is essentially a shortcut to the Name Server; it will find and query the Name Server under the surface (you don't have to do anything). The second bypasses the Name Server completely and directly queries a Pyro server host for the required object. You can find more information on these two methods in the Name Server chapter.

Communication protocol

The communication between a client and a server basically consists of two kinds of messages: By default Pyro uses the PYRO protocol (duh!) that relies on Python's built-in pickle facility to create these messages. The transport over the network is done using TCP/IP. The way I designed Pyro should make it easy to use other protocols, but for now, only the PYRO protocol is implemented.

Because PYRO uses pickle, everything that has to go over the wire should be pickleable. If you are using objects that can't be pickled, you will get a TypeError. Examples of objects that cannot be pickled are file objects and socket objects. Pyro can also use other marshaling methods (like XML marshaling) but still, the data you're transmitting must be pickleable. Transmitting file objects is impossible for instance.

A different protocol is used for the initial communication with the Name Server. Pyro uses UDP broadcasting over IP to discover the Name Server in the local subnet, and asks it to report back. Once Pyro knows the location ID of the Name Server, it switches to the PYRO protocol, because the Name Server is just another Pyro object!

Exceptions

What happens when an error occurs in your server? Pyro can't help you when your server crashes. But it does catch the Python exceptions that occur in your remote objects. They are sent back to the calling code and raised again, just as if they occurred locally. The occurrence is logged in the server log. Thus, Pyro makes no distinction between user generated exceptions (in the remote object) or exceptions that occur because of runtime errors, such as divide by zero.

The client has no way of telling whether the exception was raised locally or in the remote object. Well, there is a way, but you should really not depend on it. It's only to facilitate debugging in case of a remote exception (the remote exception is contained within the local exception). Any errors from Pyro itself will be signaled by a PyroError exception or one of its exception subclasses.

There is a slight problem with this scheme: traceback objects and stack traces are virtually meaningless if the exception occurred in the remote object. However, Pyro comes to the rescue: the stack trace of the remote exception is passed over the network to the calling object and can be printed there by using a utility function. So it is possible to see what remote code caused the problem.

Logging

A good trace facility is paramount in a complex system. Therefore Pyro provides a simple to use logger. It writes messages to a configurable logfile, annotated with a timestamp. It distinguishes errors, warnings and regular notices. Pyro uses it depending on the tracelevel you configured.

You can use the logging facility in your own code too. There is a special user version of the logger that operates independently of the Pyro system logger. You can configure the trace level and logfile location uniquely for both loggers.

By default, logging is turned off completely. You can simply turn it on during the course of your program's execution, or beforehand by setting a simple Pyro configuration option.

If it is available Pyro can also use the standard (or defacto) Python logging functionality from the logging module. Enable this by setting the PYRO_STDLOGGING config item to 1 (see config chapter). Also you can specify an alternate path for the logging configuration file via the PYRO_STDLOGGING_CFGFILE config item (see config chapter).

Pyro-3.14/docs/3-install.html0000644000076500000240000005156711526311451016270 0ustar irmenstaff00000000000000 PYRO - Installation

3. Installation and Configuration

Please read this entire chapter before trying to install Pyro.  Not that it's complex, but just that you've seen the choices you have to make.

Installation

Pyro distributions contain a "distutils" setup.py script that will install Pyro for you; just enter the following command from a shell prompt: " python setup.py install" and off you go. The script will ask if you want to install the Pyro script tools, and where to put them. If you want to do an automated (unattended) install, edit the setup.cfg file, following the directions in that file. It will not install the documentation and the examples, only the core Pyro library and the scripts.

But I will explain what exactly is in the Pyro distribution. It has a few subdirectories:

Pyro/
This is the actual Pyro package. If you do not use the supplied setup.py install script (see above) you have to install it by hand. Install this directory somewhere in your Python search path. On most systems (also Windows), the lib/site-packages directory is a nice place. The exact location might vary according to your specific Python installation.
Alternatively, keep it where it is and manually add the Pyro root directory to your Python search path (e.g. in the environment variable PYTHONPATH).
bin/
This directory contains the command-line utilities. Move the contents of this directory somewhere in your shell search path.
Alternatively, keep it where it is and manually add it to your shell search path.
docs/   and   examples/
Put those wherever you like. In docs you can find the Pyro manual, and in examples there are some Pyro examples.

Configuration

The default settings will do nicely in most cases. But sooner or later you will have to change some parameters of Pyro. Pyro's configuration is accessed through Pyro.config. This object has a lot of configuration items, shown in the table below.

Use python -m Pyro.configuration to get a printout of Pyro's active configuration settings.

Configuration item Type Description Default value
PYRO_CONFIG_FILE string The Pyro configuration file that is used. See below. Special, see below
PYRO_STORAGE string Location where Pyro stores data like log files. Read the notice at the end! Current directory
PYRO_LOGFILE string Name of the logfile. If it's not an absolute path, it's relative to $PYRO_STORAGE. It's best to modify this before importing Pyro.util! Pyro_log
PYRO_USER_LOGFILE string Name of the user logfile. If it's not an absolute path, it's relative to $PYRO_STORAGE. Pyro_userlog
PYRO_TRACELEVEL number The tracing level of Pyro, 0-3. 0=nothing, 1=only errors, 2=warnings too, 3=full: errors, warnings and notes. 0
PYRO_USER_TRACELEVEL number The user tracing level, 0-3. 0=nothing, 1=only errors, 2=warnings too, 3=full: errors, warnings and notes. 0
PYRO_DETAILED_TRACEBACK boolean Should Pyro dump detailed tracebacks (with dumps of local variable's values)? If set to 1 on the server, the clients will get detailed tracebacks from inside the server's code. You may not want this (security)... 0
PYRO_STDLOGGING boolean Should Pyro use new-style logging using the logging module (Python 2.3+)? 0
PYRO_STDLOGGING_CFGFILE string Name of the configuration file that is used to configure the new-style logging. If it's not an absolute path, it's relative to $PYRO_STORAGE. If this file doesn't exist, Pyro uses the default configuration that resembles the classic Pyro logging style. logging.cfg
PYRO_PICKLE_FORMAT integer The pickle protocol format that Pyro will use for marshaling. pickle.HIGHEST_PROTOCOL on Python 2.3+, else 1
PYRO_XML_PICKLE string Whether the marshaling is done using the safe xml pickling or the default pickle. The xml_pickle is not vulnerable for the pickle trojan problem, but it is an order of a magnitude slower, and requires more bandwith. Set to "gnosis" for Gnosis XML pickler. There are no other options available at this time. You need to have installed Gnosis_Utils (at least version 1.2.x). Note that you have to use the same Gnosis XML library version everywhere. You can't mix older versions with newer versions. empty (disabled)
PYRO_GNOSIS_PARANOIA number The 'paranoia' setting that will be used for the Gnosis XML pickler. Higher=more secure. The default setting (0) prevents automatic imports of modules during unpickling. Set it to -1 to enable automatic imports of user defined modules. When you use the mobile code feature together with Gnosis XML pickling, you need to set it to -1 as well. 0
PYRO_COMPRESSION boolean Whether the protocol should compress the data to save bandwidth (at the cost of CPU time). The zlib module is used for compression. If you don't have zlib, Pyro still works, but without compression. 0
PYRO_CHECKSUM boolean Whether the protocol should perform a checksum over the message data. This costs a little bit extra CPU time, but you will be quite sure that your communication is without errors. The zlib.adler32 function is used for checksumming. If you don't have zlib, Pyro still works, but without checksumming. The overhead of checksumming is very small, with regular messages less than 0.1%, but increasing with big messages (15% for 5 Mb or so). Note: the checksum is by no means secure. If you want secure transmissions, you'll have to use SSL or build your own encryption/secure hashing functions on top of Pyro. 0
PYRO_SOCK_KEEPALIVE boolean Whether Pyro should set the SO_KEEPALIVE socket option on the network sockets. This is used to detect broken client connections, to let the Pyro server clean them up nicely. It is enabled by default, but it could cause problems in certain situations so you can turn it off if you want. The timeout period is system-dependent but usually around 2 hours. It depends on your OS how to change this value, but have a look at "sysctl". (This feature may not be available on all OS's, if your OS doesn't support it, Pyro will automatically switch it off). 1
PYRO_MAXCONNECTIONS number The maximum number of simultaneous connections to one Pyro server. Note that a custom connection validator may or may not take this in account. The default validator does check for this limit. 200
PYRO_TCP_LISTEN_BACKLOG number The size of the TCP socket listen backlog for Pyro daemons. 200
PYRO_BROKEN_MSGWAITALL boolean Some systems have broken socket MSG_WAITALL support. Set this item to 1 if your system is one of these. When set to 1, Pyro will use a different piece of code to receive data (slower, but working on these systems as well). 0
PYRO_MULTITHREADED boolean Whether Pyro servers should be multithreaded or not. 1 (if supported)
PYRO_MOBILE_CODE boolean On the server: whether Pyro should automatically download Python code from clients if it isn't available on the server. On the client: whether Pyro should automatically download Python code from the server if it returns objects that aren't available on the client. 0
PYRO_DNS_URI boolean Whether symbolic DNS host names should be used in URIs instead of fixed IP addresses. 0
PYRO_BC_RETRIES number How often a broadcast will be retried if no answer has been received. Currently only used by the Name Server locator. A negative number (<0) means infinitely. 2
PYRO_BC_TIMEOUT number How long Pyro will wait (in seconds) for an answer to a broadcast request. Currently only used by the Name Server locator. A negative number (<0) means infinitely. 2
PYRO_PORT number The base socket number of the range of socket numbers that the Pyro daemon can use to listen for incoming requests (Pyro method calls). Set to 0 to let the operating system choose a random port. 7766
PYRO_PORT_RANGE number The size of the socket port range. Pyro will try to claim a socket for its Deamons in the socket port range PYRO_PORT to (but not including) PYRO_PORT+PYRO_PORT_RANGE. This means that if Pyro already has a Daemon listning on socket N, a new Deamon will claim socket N+1, and so on. You can disable this by using a special argument when construction a Daemon (or setting this item to 1). 100
PYRO_HOST string The hostname Pyro's daemon will bind on. Useful when your machine has multiple hostnames/network adapters on which it can listen. (Also influences NameServer.) '' (default host)
PYRO_PUBLISHHOST string the hostname that Pyro daemons will use when publishing URIs. Useful in case of a firewall/NAT setup. See the Features chapter for firewall info. None (same as normal hostname)
PYRO_NS_DEFAULTGROUP string The default group name in which names are located. This must be an absolute name (starting with the root character). :Default
PYRO_NS_URIFILE string The file where the Name Server will write its URI. If it's not an absolute path, it's relative to $PYRO_STORAGE. Pyro_NS_URI
PYRO_NS_HOSTNAME string The hostname that is initially tried to find the NameServer on, or when the broadcast lookup mechanism fails. empty
PYRO_NS_PORT number The socket number on which the Name Server will listen for incoming requests (Pyro method calls, in fact). Set to 0 to let the operating system choose a random port. Note that if you set this to 0, a client cannot use this config item to directly connect to the NS. It will have to use the broadcast lookup. 9090
PYRO_NS_BC_ADDR string Overrides the default broadcast address. It is used by the nameserver to bind the broadcast listener on this broadcast address, and by the name server locator when it uses broadcast discovery. When empty, the default broadcast address is used (usually 255.255.255.255). empty
PYRO_NS_BC_PORT number The socket number on which the Name Server will listen for broadcast requests (usually to find the location). 9090
PYRO_NS2_HOSTNAME string Like above, but for the second (paired) Name Server. empty
PYRO_NS2_PORT number Like above, but for the second (paired) Name Server. 9091
PYRO_NS2_BC_ADDR string Like above, but for the second (paired) Name Server. empty
PYRO_NS2_BC_PORT number Like above, but for the second (paired) Name Server. 9091
PYRO_ES_QUEUESIZE number The size of the message queues per subscriber that the Event Server allocates. Use 0 (zero) for infinite size. 1000
PYRO_ES_BLOCKQUEUE boolean If true (1), a publisher will block if an event queue on the server is full, and continue as soon as the queue has some space again. If false (0), the publisher won't block, but the event is lost (but only for the subscriber who has a full queue). 1
PYRO_ONEWAY_THREADED boolean If true (1), oneway method calls will execute in a new server thread. This allows for the server to continue to process other method calls on this object in the meantime. If false (0), oneway method calls will execute in the main server thread and need to complete before the server can process other calls on that object. 1
PYROSSL_CERTDIR string The directory where openssl certificates are stored. 'certs' in the PYRO_STORAGE location.
PYROSSL_CA_CERT string Certificate of the Certificate Authority. Used to check if client and server certificates are valid (that they are signed by the given CA) ca.pem
PYROSSL_CERT string Certificate file for SSL host.pem
PYROSSL_KEY string Optional SSL key file (required if your host key is not part of the certificate file) None
PYROSSL_POSTCONNCHECK boolean Tells the SSL layer if it should do 'post-connection' validations on the certificate(s) for instance. Set it to 0 to disable these checks (not advised! But convenient to be able to use a certificate that hasn't got a matching commonName or stuff like that) Default is 1: the SSL layer will do its checks as enabled by its own default settings. 1

There are several ways to change the default settings:

  1. Change the settings in your code, at runtime. You can change all settings before starting Pyro, and most settings can be changed dynamically during execution too. Note that you cannot use this to change Pyro.config.PYRO_STORAGE! See below!
    ... Pyro.config.PYRO_PORT = 7000
    ... Pyro.config.PYRO_TRACELEVEL = 3
  2. Define environment variables that override the default settings.
    Every configuration item has an equivalent environment variable. If you define this, you can override the default setting for that item. For instance, it might be convenient to have your Pyro programs generate log files and put them in a designated log directory:
    ...$ export PYRO_LOGFILE=/var/log/PYRO/logfile
    ...$ export PYRO_TRACELEVEL=3
    (This is for bash - syntax is different for other shells or Windows.)
  3. Configuration files
    You can use a configuration file that can contain some small configuration changes or a fully new configuration for all items. Pyro checks if the environment variable PYRO_CONFIG_FILE is set. If it isn't set, or set to an empty string, Pyro checks for a Pyro.conf file in the current directory. If it exists, Pyro uses it as a configuration file. If it doesn't exist, Pyro uses the default built-in configuration.
    If the environment variable is set, Pyro uses the value as the name for the configuration file. If the configuration file can't be read, a PyroError exception occurs.

    The format of the configuration file is very simple. It is a text file, and each line can be empty, a comment, or a configuration item setting. A comment starts with '#'. A config item setting is of the format 'ITEM=VALUE'. If Pyro finds an unknown config item, a KeyError exception occurs.

    Note that PYRO_CONFIG_FILE is useless inside a configuration file. After initialization, it is set to the absolute path of the configuration file that was used (or the empty string, if no configuration file was used). Note that setting PYRO_CONFIG_FILE from within your code is useless too because Pyro is already initialized at that point.

Environment variables override configuration file settings. Configuration file settings override the built-in default settings.

PYRO_STORAGE is used at initialization time, that is, as soon as a part of the Pyro package is imported in your program. You can only change PYRO_STORAGE beforehand by either setting the environment variable or making an entry in the configuration file. Changing Pyro.config.PYRO_STORAGE in your program leads to unexpected results, because the initilization has already been done using the old value. So don't do this, and use one of the two other ways.

Pyro-3.14/docs/4-usage.html0000644000076500000240000011461111526320251015713 0ustar irmenstaff00000000000000 PYRO - Usage

4. Pyro Usage

Introduction

This chapter will show the Pyro development process: how to build a Pyro application. 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 Features and Guidelines chapter).
  2. Write a server process that performs the following tasks:
  3. Write a client program that does the following:
  4. Make sure the Pyro Name Server is running.
  5. Start your server process. If it complains that the names it wants to register already exist, use the pyro-nsc tool to unregister them, or restart the NS.
  6. 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 command files. The Windows-style command files have the '.cmd' extension.

pyro-genguid   (GUID generator)
No arguments.
This is a very simple GUID generator. It uses the internal Pyro GUID generator to print a new GUID.
pyro-ns   (Name Server)
This script is explained in the Name Server chapter.
pyro-es   (Event Server)
This script is explained in the Event Server (Pyro Services) chapter.
pyro-nsc   (Name Server Control tool)
- Arguments: [-h host] [-p port] [-c bcaddr] [-i identification] command [args...]
- Controls the Pyro Name Server. '-h host' specifies the host where the Name Server should be contacted. '-p port' specifies a non-standard NS broadcast port to contact. '-c bcaddr' allows you to override the broadcast address. With '-i identification' you can supply the authentication passphrase that is used to connect to the Name Server. When it contains spaces, use quotes around it. 'command' is one of the following:
pyro-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. 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. If your Name Server requires an authorization passphrase, you must enter that first in the ID entry box. After that, you can connect to the NS. Once connected, the passphrase is erased in the display for security reasons. You have to type it again if you need to reconnect.

pyro-wxnsc   (Alternative Graphical NS control tool)
- No arguments
- This is similar to the xnsc tool, but based on the WxPython GUI toolkit.

pyro-nssvc, pyro-essvc   (Windows-only Name Server and Event Server 'NT-service' control scripts)
These scripts are explained in the Name Server chapter and the Event Server chapter.

Using python -m to start various tools
python -m Pyro.naming - start the name server
python -m Pyro.EventService.Server - start the event server
python -m Pyro.nsc - start the nsc tool. Also works with xnsc and wxnsc.
python -m Pyro.configuration - print a dump of Pyro's active configuration settings.
python -m Pyro.test.echoserver - start the built-in echo server. Use '-h' parameter to get some help.

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.

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 optional argument banner=1, a short version message is printed on the standard output. There is also a second optional argument storageCheck. By default it is 1 and Pyro will check the availability of the PYRO_STORAGE directory. If you set it to 0, Pyro will not perform this check.

If the tracelevel is not zero, a startup message is written to the log. This message shows the active configuration options.

It is not strictly required to call Pyro.core.initServer(), if you are creating a Pyro Daemon first. If you're doing that (see next paragraph-- it's a very common thing to do first), Pyro will initialise itself automatically. If you're not doing this, and are using other Pyro things first, it won't work because Pyro will then think you are a client, and call the wrong initialization function. So it's best to call Pyro.core.initServer() yourself. All Pyro code you see in this manual and the Pyro examples do this too.

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:
protocol the protocol to use (defaults to "PYRO")
host the 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.
port the socket number to use (defaults to the PYRO_PORT configuration item). Keep in mind that Pyro will pay attention to the PYRO_PORT_RANGE config item: if it cannot claim the socket on the given port, it will try the next higher port, and so on, as long as PYRO_PORT_RANGE allows. Setting this to 0 lets the operating system choose a random port for you (you need to set norange to 1 or True as well, if you want this).
norange whether or not to try a range of sockets, i.e. don't pay attention to the PYRO_PORT_RANGE setting. (It's usually best leave this at the default value, 0) You need to set this to 1 or True if you want to use the random port selection (when setting port=0).
publishhost the hostname that the daemon will use when publishing URIs, in case of a firewall/NAT 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.

If your daemon is no longer referenced, it might be garbage collected (destroyed) by Python. Even if you connected Pyro objects to the daemon. So you have to make sure that you keep a reference to your daemon object at all time. This is recommended anyway because you can then cleanly terminate your Pyro application by calling daemon.shutdown() when it exits. Usually this is not a problem because your program creates a deamon and calls its requestLoop. But a situation might arise where you don't keep a reference to the daemon object, and then things break.

Find the Name Server

You have to get a reference to the Pyro Name Server, which itself is a Pyro object. The easiest way is by using the NS Locator:
   locator = Pyro.naming.NameServerLocator()
   ns = locator.getNS()
ns now contains a reference. There are more advanced ways to get a reference to the NS, please read the chapter about the Name Server to find out about them.

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: For advanced purposes, there are two other base classes that you can use instead of ObjBase:
Pyro.core.SynchronizedObjBase
Use this to make your Pyro object thread-safe; all (remote) method calls are automatically synchronized for this object.
Pyro.core.CallbackObjBase
Use this for special callback objects that need to report errors also on the client, not only on the server. For more information, please read about Callbacks in the Features and Guidelines chapter.

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:
   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, as we explained earlier: daemon.useNameServer(ns)). 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 Name Server. 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: there is also a connectPersistent method that is used for a special purpose. Look under the "Automatic Rebinding" topic in the "Features and guidelines" chapter for more info.

In contrast to the simple (flat) name shown above ("our_object"), Pyro's Name Server supports a hierarchical object naming scheme.

Disconnecting object instances

Usually you don't have to worry about cleaning up, the daemon will cleanly remove any registered objects from the Name Server if it exits. (Note that 'persistently' connected objects are not removed automatically.) But sometimes it can be better to manually remove any objects that you don't need any longer. Use the following method to do that:
   daemon.disconnect(obj)

Just pass the Pyro object you want to remove from the Daemon (and the Name Server). It is also possible to pass the object's UID (string) instead of the object itself. This allows you to remove objects from the daemon that you only know by their UID (for instance, you only have a Pyro URI or you got the object ID directly from the daemon's getRegistered() object list).

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. For many applications calling daemon.requestLoop() is enough. For finer control, you can give a few arguments to the function:
requestLoop(condition, timeout, others, callback)
All arguments are optional. The default is that requestLoop enters an endless loop waiting and handling Pyro requests. You can specify a condition callable object (for instance, a lambda function) that is evaluated each cycle of the loop to see if the loop should continue (the condition must evaluate to 1). The timeout can be used to adjust the timeout between loop cycles (default=3 seconds). The requestLoop doesn't use the timeout (it only returns when the optional loop condition is no longer true), the timeout is simply passed to the underlying handleRequests call. This is required on some platforms (windows) to cleanly handle break signals like ^C. The others and callbacks can be used to add your own socket or file objects to the request handling loop, and act on them if they trigger. For more details, see the paragraph below.

For those that like to have more control over the request handling loop, there is also handleRequests. Usually your loop will look something like this:

   while continueLoop:
      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 a new proxy object got a connection to the daemon. 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.

Including the Pyro Daemon in another (external) event loop

Some applications already have their own event loop. If it is select-based, or can process additional sockets to wait on, you can also use your application's event loop instead of the Daemon's requestLoop. Do this by querying the Daemon for a list of active socket objects that it is currently listening on, and pass every socket in that list to your external event loop. The Daemon has a method getServerSockets() that returns this list of socket objects. This list changes so you have to call it every time you enter the 'foreign' event loop. When your code returns from the 'foreign' event loop, check if one of Pyro's sockets has an event, and if so, call the regular handleRequests(). Pyro will then process every event that's pending for it. An example:
while some_condition :
        socks=daemon.getServerSockets()
        ins,outs,exs=select.select(socks,[],[],2)   # 'foreign' event loop
        for s in socks:
                if s in ins:
                        daemon.handleRequests()
                        break    # no need to continue with the for loop
Have a look at the "AllInOne" example. It shows two approaches of starting various Pyro servers from within a single program and then using a custom event loop to wait for incoming requests. That code is easily adapted to integrate Pyro in a GUI toolkit's event loop, for instance.

Stopping the server, cleaning up

To signal the Daemon that it should stop its requestloop, you can call daemon.shutdown() or send the process a break signal (ctrl-C). This issues an asynchronous request to the Daemon to terminate the request loop once any processing that is currently going on, is finished (it can still take a while before the requestloop is actually stopped). Once the loop stops, and all references to the daemon object are gone, it is garbage collected and Python tries to run the finalizer code that nicely unregisters any connected objects (so their names are removed from the Name Server unless you're using persistent mode).

However this may not work in all cases, or perhaps you want to control it explicitly. If you want to explicitly tell the daemon to unregister its objects and shut down, you should use daemon.shutdown(True). So your code might look like this:

 …
daemon.connect( … )
try:
    daemon.requestLoop()
finally:
    daemon.shutdown(True)
    # at this moment, the objects have been unregistered
 …

If you're not doing any more processing in your server after the requestloop, it is usually not necessary to add this explicit cleanup logic. However, if the server is aborted in a 'hard' way (terminated, crash) instead of a normal shutdown or ctrl-C signal, Python may not execute the finalizer code and your objects are still registered in the NS. There is not much you can do about this; even the explicit shutdown code above doesn't help (because it is not executed as well!). A solution is to change the registration of the objects: if you encounter errors because the name already exists in the NS, just unregister the old name and re-register.

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 'banner=1', a short version message is printed on the standard output. In contrast to the server initialization (see above), this method does not check the availability of the PYRO_STORAGE directory. This means that you can run Pyro clients on a read-only system, as long as they don't have to write something (log!) to PYRO_STORAGE!

If the tracelevel is not zero, a startup message is written to the log. This message shows the active configuration options.

It is not strictly required to call Pyro.core.initClient(). If you don't call it, Pyro will initialise itself automatically.

Find the Name 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 three ways to find an object URI by name:

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.
   obj = Pyro.core.getProxyForURI(uri)     # get a dynamic proxy
   obj = Pyro.core.getAttrProxyForURI(uri) # get a dyn proxy with attribute support

   # if you're sure that the URI is a real PyroURI object, you can do this:
   obj = uri.getProxy()                    # get a dynamic proxy directly from the URI  
   obj = uri.getAttrProxy()                # same, but with attribute support
If you're using attribute proxies, be aware of their properties and limitations.

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. There may be some extra configuration necessary when you're running Pyro behind a firewall, and want to access it from outside the firewall, or have machines with dynamic IP addresses. You can find more information about this in the Features and Guidelines chapter. Otherwise it's simple:

Starting the Name Server

A Pyro system needs at least one running Name 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 Name Server sits in a loop waiting for requests:
irmen@atlantis:~ > projects/Pyro/bin/ns
*** Pyro Name Server ***
Pyro Server Initialized. Using Pyro V2.4
Will accept shutdown requests.
URI written to: /home/irmen/Pyro_NS_URI
URI is: PYRO://10.0.0.150:9090/0a000096-08620ada-6697d564-62110a9f
Name Server 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 Name 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 pyro-nsc command-line utility or the pyro-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 Installation and Configuration chapter 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.

Threads, sessions and objects

For more complex uses of Pyro, it is important to understand how Pyro uses threads and how the objects interact. Below are, in condensed form, the rules Pyro follows. For detailed information about these subjects, please refer to the relevant chapters elsewhere in the manual.

Built-in echo server

Sometimes it's convenient to have a minimal Pyro server that you can talk to. To avoid having to write it again and again (even though it's only a couple lines of code), Pyro provides a simple echo server for you.

The echo server has two methods:

The server is available in the Pyro.test.echoserver module and recognises a few command line arguments. You can use -h to get some help:

$ python -m Pyro.test.echoserver -h
Usage: echoserver.py [options]

Options:
  -h, --help            show this help message and exit
  -H HOST, --host=HOST  hostname to bind server on (default=localhost)
  -p PORT, --port=PORT  port to bind server on
  -n, --naming          register with nameserver
  -N, --nameserver      also start a nameserver
  -v, --verbose         verbose output    

Once running (try python -m Pyro.test.echoserver -N), you can simply connect to it, given the printed URI strings or object name. For instance:

>>> import Pyro.core
>>> e=Pyro.core.getProxyForURI("PYRONAME://:Pyro.test.echoserver")
>>> e.echo("hello world")
'hello world'

>>> p.error()
Traceback (most recent call last):
  ...
  ...
ZeroDivisionError: integer division or modulo by zero
>>>

Last Notes

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

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

Pyro-3.14/docs/5-nameserver.html0000644000076500000240000006274711526311451016775 0ustar irmenstaff00000000000000 PYRO - Name Server

5. Pyro Name Server

The commands for starting a Name Server

There are a few commands (actually scripts) supplied in the bin directory that will start a Pyro Name Server for you:
pyro-ns   (Name Server)
- Arguments: [-h] [-k] [-m] [-r] [-x] [-n hostname] [-p port] [-b bcport] [-c bcaddr] [-i identification] [-d [databaselocation]] [-s securitymodule] [-1 [host:port]] [-2 [host:port]] [-v]
- Starts the Pyro Name Server. The '-h' argument prints some help, '-k' makes it immune to shutdown requests (a crude form of security against malicious shutdown requests), '-m' allows multiple Name Servers to be started in the same network segment (default=not allowed), '-r' means that no lookup will be done to check for an already existing nameserver, '-x' means that no broadcast listener will be started, '-n hostname' selects the hostname the server should bind on (useful on systems with multiple network adapters/hostnames), '-p port' override the Name Server port number (0=random), '-b port' specifies the broadcast port number. '-c ipaddress' specifies a broadcast listener ip address override. With '-i identification' you can supply the authentication passphrase that will be required to connect to this server. When it contains spaces, use quotes around it. '-d' starts the persistent server. You can provide an optional name to specify the database location (directory). The '-s' option specifies the Python module for security plugins (make sure it is in your PYTHONPATH). Use '-v' to enable slightly more verbose output.
The '-1' and '-2' options are used for paired mode. More info below.

pyro-ns.cmd
The Windows script for the above.

pyro-nssvc   (Windows-only Name Server 'NT-service' control script)
- Arguments: [options] install|update|remove|start [...]|stop|restart [...]
- On windows NT (2000/XP) systems, it's possible to register and start the Name server as a NT-service. You'll have to use the nssvc.cmd script to register it as a service. Make sure you have Pyro properly installed in your Python's site-packages. Or make sure to register the service using an account with the correct PYTHONPATH setting, so that Pyro can be located. The NS service logs to C:\Pyro_NS_svc.log, and writes its URI to C:\Pyro_NS_URI.txt (C: being your system drive).
You can configure command line arguments for this service in the Registry. The key is: HKLM\System\CurrentControlSet\Services\PyroNS, and the value under that key is: PyroServiceArguments (REG_SZ, it will be asked and created for you when doing a nssvc.cmd install from a command prompt).
Running the NS as a windows NT service it not well supported.
You can also use python -m to start the tools:
python -m Pyro.naming - start the name server
python -m Pyro.nsc - start the nsc tool. Also works with xnsc and wxnsc.
Using the pyro-nsc command (explained in the Usage chapter) you can control a Name Server that is already running.

Consider setting PYRO_CHECKSUM to 1 before starting the NS. It will communicate more reliably and the overhead is very small.

If you want to start the NS from within your own program, you can ofcourse start it by executing the start script mentioned above. You could also use the Pyro.naming.NameServerStarter class to start it directly (this is what the script also does). Be sure to start it in a separate process or thread because it will run in its own endless loop. You probably have to wait until the NS has been fully started, call the waitUntilStarted() method on the starter object. It returns true if the NS has been started, false if it is not yet ready. You can provide a timeout argument (in seconds).

What does the server listen for?

When you start the name server it usually does two things. It starts a broadcast listener to respond to broadcast messages that may arrive, and it starts the name server itself. It binds (listens) on the default network interface, unless you specify otherwise with the hostname argument. Pyro does things a bit differently when it discovers that the server is listening on the local loopback adapter (also called localhost or 127.0.0.1). In this case, no broadcast server is started at all because it is not useful to have any: the name server is only accessible on the local system! Note that there could be a number of reasons of this happening: you force it to bind on localhost by specifying that as the hostname parameter, or there is no external network available, or the DNS of your system is wrongly configured. The Pyro log wil contain notes of this happening (and you can see it on the screen as well if you enable the verbose flag).

Pyro's hierarchical object naming scheme

Brief: it's just like a filesystem with directories and files, and path separators.
Verbose: Pyro's object naming is hierarchical. Let's call the full set of names the namespace. A namespace has a single root that contains all other names. A name in the namespace is the name of a group or an object. A group can contain other groups, and objects. The root is a group.

Object names can be absolute or relative. Relative object names are searched in the default group (which has a special name, set by PYRO_NS_DEFAULTGROUP). Absolute names are always searched from the root. Absolute object names start with a special character that signifies the root; the colon (':'). Relative object names don't have this character at the start.

Object names can be simple or compound. Compound object names consist of multiple parts separated by a special group separator character: the dot ('.'). Compound names are used to search within the namespace hierarchy. Each component of the name is the name of a group. The last name component can be the name of a group or an object (usually the latter).

Object name parts can only consist of the ASCII characters in the range 33-126 ('!'-'~') and except the backslash (\), dot ('.') and colon (':'). So spaces are also illegal in names, but normal slashes ('/') are okay.

Let's finish with a few examples to clarify all this. Note that by name alone you cannot distinguish a group name or an object name.

: The namespace root group
:TestObj The TestObj name in the root (most likely an object name)
:Test.simple.object1 The object1 name in the simple group in the Test group in the root.
Test.simple.object1 The object1 name in the simple group in the Test group in the default group (which is ":Default" if not configured otherwise. This is the Default group in the root).
object1 The object1 name in the default group.

The different Name Server types available

There are two kinds of Name Servers available at the moment: The non-persistent NS stores its naming database only in memory. It is really fast because of that, but when it stops or crashes, the naming database is lost. All objects that were registered in the NS have to be re-registered.

The persistent NS stores its naming database on disk. Currently this is implemented the easy way; there is a direct mapping between the group names and directories on disk, and between object names + URIs and files in these directories on disk. The database by default is stored in a "Pyro_NS_database" directory that is created in the directory configured by PYRO_STORAGE. You can specify a different name if needed (with the '-d' option of the name server start script, and a parameter to the start method of the NameServerStarter class in the code).

Usually you don't access the NameServer or PersistentNameServer classes directly: there are scripts to start the right name server. Or you use the NameServerStarter helper class from the Pyro.naming module.

Connecting objects to the Pyro Daemon with persistent naming

Usually you'll use the connect method of the daemon to connect your object instances to the daemon on the server. The daemon will register your object with the name server too, if you supplied a NS to the daemon and your object isn't transient (it has a name).

But, when using the persistent name server, there is a complication here: if you didn't explicitly remove your object from the NS, the entry will still be there the next time. Your connect attempt will then fail because your object cannot be registered again in the NS.

The solution is to use the connectPersistent method of the Pyro daemon. Except for the method name, you call it exactly like the regular connect method. It tries to find your object in the NS. If it's there already, the previous URI is used for your object (that also means that the object's GUID is replaced by the previous GUID that was found in the NS). If it isn't there, the regular connect call takes over.

Of course you could always play safe and explicitly unregister any possible previous occurrences from the NS before you connect new instances. This is what all examples do by the way, so you can safely run an example again and again.

For your information, the code that starts the Persistent Name Server uses connectPersistent to connect the name server object to the daemon. Why? because the name server itself is also registered in the NS database, and it is necessary that when the NS restarts, it uses the URI of the previous instance if found in the persistent database.

Security plugin features

The Name Server supports security plugins, to facilitate access control to the Name Server. When starting the NS, you can supply an option that tells the NS to load that specific Python module and use the security plugins that you defined in there. For more information, see the Security chapter.

Paired (failover) mode

For improved availability you can run the Name Server in paired mode. This means that you start another Name Server instance that works together with the other one, as a pair, but providing a single Namespace. If one of them dies, the other takes over. They synchronize every namespace change and update so that they are always each other's exact copy. If a dead NS is restarted, it resyncs the full Namespace from the other copy that is still running.
Pyro clients that do a NS lookup automatically get the other NS if the first is dead and vice versa, you don't have to change anything in the client code.

Paired mode is activated by using the -1 and -2 options when starting the NS. 1 means: this is the first one, 2 means: this is the second one of the pair (this distinction is necessary because of a slightly different startup procedure depending on being the first or the second).
You can add a hostname:port argument to the -1 and -2 option, that specify the location of the other name server. So, for instance:
pyro-ns -n atlantis -p 4000 -1 atlantis:5000 to start NS #1,
pyro-ns -n atlantis -p 5000 -2 atlantis:4000 to start NS #2. But usually just -1 or -2 is good enough.

There are three new config items dealing with paired mode NS: PYRO_NS2_HOSTNAME, PYRO_NS2_PORT and PYRO_NS2_BC_PORT. They can be used to specify non-default values for the hostname and port number that the second NS will use. See the config chapter.

Resync mechanism: Start A, then B. B remembers A's location (either discovered trough NS lookup or via command line URL). B notifies A that it has started, provides own location, and gets a copy of A's namespace database. A remembers B's location. Any namespace change in A is replicated in B and vice versa, using ONEWAY calls. If an error occurs, the reference to the faulty NS is discarded. If you discover that somehow the namespaces get out-of-sync, just kill the one that is faulty and restart it. It will automatically resync with the 'good' one.

With Identification: If you want to use paired mode together with identification (-i), you must supply the same identification argument to both name servers.

The Name Server Locator

Pyro's NS is actually two servers: the normal Pyro server but when using TCP/IP sockets, also a broadcast listener. The latter helps clients find the location of the Name Server by answering to broadcast packet requests. To hide that broadcast lookup mechanism from you, and to make the lookup very easy, we have the NameServer Locator, defined in the Pyro.naming package. This object gets a Pyro proxy for the NS for you. Because this is the recommended (and easiest way) to gain access to the Name Server, you're not interested in the internal name Pyro uses for the Name Server. But for consistency, it is defined, and the Name Server object itself is known in the Name Server's namespace under the name available in Pyro.constants.NAMESERVER_NAME.

There are essentially three ways to get a reference to the Name Server:

The special PYRONAME://, PYROLOC:// and PYROLOCSSL:// URIs

For the lazy ones among us, there is an even simpler way of using the Name Server to look up objects by their name. Instead of using the PYRO:// URIs that the Name Server returns, and where you then get a proxy object for, you use another URI format. This format is as follows:

    PYRONAME://nshostname:port/objectname     (PYRONAMESSL is not yet implemented)

The nshostname is the name of the host the Name Server is running on, and port may be a non-default port the Name Server is listening on. Both may be omitted. objectname is the name of the object you want to find! So, the next code fragment will find the NS using the default lookup mechanism, resolve the object name to a real URI, create a proxy for that, and call a method:
    Pyro.core.getProxyForURI('PYRONAME://:Test.MyObject').getQuote()
So we now have remote object method invocation in one statement :-) There is one important point: each time a PYRONAME:// URI is used, a lookup for the Name Server has to be performed, and then a lookup for the object name. This is much slower than the regular method. However, once you've constructed a proxy for this URI, no more lookups are performed.

There is another special URI, that bypasses the Name Server completely:

PYROLOC://hostname:port/objectname
PYROLOCSSL://hostname:port/objectname (use this if the server is running in SSL mode)

This time hostname is required and is the name of the host that your target Pyro object runs on. port may be a non-default port the Pyro daemon is running on, and may be omitted. objectname is the internal name for the Pyro object you want to access. When you use this URI, the Name Server is bypassed and the target server is contacted directly to get the regular URI for the desired object. The advantage of this is that you don't have to have a Pyro Name Server running. The disatvantages are obvious; you miss all the features of the Name Server and you have to administrate server object names yourself somehow. You must use the name that is passed to the connect method of the Daemon. There is no hierarchical naming scheme because the Name Server is not used at all. Once the object is found, the real URI is stored and no more lookups need to be done. The next code fragment shows how to call a remote object, without requiring a Name Server to be present:

    Pyro.core.getProxyForURI('PYROLOC://localhost/MyObject').getQuote()
(Use PYROLOCSSL:// if the server is running in SSL mode) Remember that your server does not have to rely on a Name Server when you want to use this mechanism. When you enable Pyro logging, you might get a WARN that a Name Server is not specified. You can ignore this. Please also see the "noNS" example, that shows how to use this URI, and also how you could connect directly by using an URI string that comes off the server.

The methods on the Name Server Pyro object

Ok, you've got a proxy for the Name Server. Now what to do with it? You've already seen the most important method: resolve. There are more methods, see below. One very important thing to realize: all names you supply must be absolute, i.e. ":Group.Objectname" instead of just "Objectname". The NameServerProxy that you'll usually be working with has some logic that takes care of this.
resolve(name)
Look up the object with the given name and returns the Pyro URI. (it will always return a real PyroURI object, not a string).
register(name, URI)
Registers an object with the given name and the given Pyro URI. The URI can be a PyroURI object or just a string.
unregister(name)
Removes the object with the given name from the naming database.
ping()
Does nothing, just to test if the Name Server is running.
list(groupname)
Returns a list of the given group. If groupname is None, lists the root group. The list contains tuples (name,type) where name is the object name, and type is 0 for a naming group, and 1 for an object name.
flatlist()
Returns a list of all objects in the naming database. The list contains tuples (name, uri) where name is the absolute object name and uri is the associated PyroURI object.
createGroup(name)
Create a new empty naming group.
deleteGroup(name)
Delete a naming group, including all its contents.
fullName(name)
Returns the full (absolute) name of the object (prefixed by the default group)
setMeta(name, meta)
sets user meta information for the given name. meta can be any object that can be pickled
getMeta(name)
retrieves the user meta information for the given name.
See the source code of the (x)nsc tools (nsc.py / xnsc.py) for more info.

Important notice:

By default, there is no access control on the Name Server. It is possible for anybody to remove and overwrite an existing registered object. You must be very aware of this, because it is very easy to sneak a trojan in by overwriting a name with a reference to the trojan object! This free access is necessary for instance to be able to use the "pyro-nsc" tool without restriction. Pyro-3.14/docs/6-eventserver.html0000644000076500000240000005214611526311451017167 0ustar irmenstaff00000000000000 PYRO - Event Server

6. Pyro Event Server

Introduction

In various situations it is needed that the servers and the clients are decoupled. In abstract terms this means that information producers do not know nor care about the parties that are interested in the information, and the information consumers do not know nor care about the source or sources of the information. All they know is that they produce or consume information on a certain subject.

Here does the Event Server fit in nicely. It is a third party that controls the flow of information about certain subjects ("events"). A publisher uses the Event Server to publish a message on a specific subject. A subscriber uses the Event Server to subscribe itself to specific subjects, or to a pattern that matches certain subjects. As soon as new information on a subject is produced (an "event" occurs) all subscribers for this subject receive the information. Nobody knows (and cares) about anybody else.

It is important to rembember that all events processed by the ES are transient, which means they are not stored. If there is no listener, all events disappear in the void. The store-and-forward programming model is part of a messaging service, which is not what the ES is meant to do. It is also important to know that all subscription data is transient. Once the ES is stopped, all subscriptions are lost. The clients that are subscribed are not notified of this! If no care is taken, they keep on waiting forever for events to occur, because the ES doesn't know about them anymore!

Usually your subscribers will receive the events in the order they are published. However, this is not guaranteed. If you rely on the exact order of receiving events, you must add some logic to check this (possibly by examining the event's timestamps). The chance of events not arriving in the order they were published is very, very small in a high-performance LAN. Only on very high server load, high network traffic, or a high-latency (WAN?) connection it is likely to occur.

Another thing to pay attention to is that the ES does not guarantee delivery of events. As mentioned above, the ES does not have a store-and-forward mechanism, but even if everything is up and running, the ES does not enhance Pyro's way of transporting messages. This means that it's still possible (perhaps due to a network error) that an event gets lost. For reliable, guaranteed, asynchronous message delivery you'll have to look somewhere else, sorry ;-)

The ES is a multithreaded server and will not work if your Python installation doesn't have thread support. Publications are dispatched to the subscribers in different threads, so they don't block eachother. Please note that events may arrive at your listener in multithreaded fashion! Pyro itself starts another thread in your listener to handle the new event, possibly while the previous one is still being handled. Theevent method may be called concurrently from several threads. If you can't handle this, you have to use some form of thread locking in your client! (see the threading module on Semaphore), or Pyro.util.getLockObject.

To summarize:

Starting the Event Server

Start the ES using the pyro-es command from the bin directory (use pyro-es.cmd on windows). You can specify the following arguments:

pyro-es [-h] [-n hostname] [-p port] [-N] [-i identification]
-h
Print help.
-n hostname
Change the hostname/ip address the server binds on. Useful with multiple network adapters.
-p port
Change the port number the server uses. (Omit to use Pyro defaults, 0 to let the operating system choose a random port).
-N
Do not use the Name server
-i identification
Specify the authentication passphrase that will be required to connect to this server. If it contains spaces, use quotes around the string. The same identification is also used to connect to other Pyro servers such as the Name Server. (this is required ofcourse when the Name Server has been started with the -i option).

There is also: pyro-essvc   (Windows-only Event Server 'NT-service' control scripts)
- Arguments: [options] install|update|remove|start [...]|stop|restart [...]
- On windows NT (2000/XP) systems, it's possible to register and start the Event server as a NT-service. You'll have to use the essvc.cmd script to register it as a service. Make sure you have Pyro properly installed in your Python's site-packages. Or make sure to register the service using an account with the correct PYTHONPATH setting, so that Pyro can be located. The ES service logs to C:\Pyro_ES_svc.log where C: is your system drive.
You can configure command line arguments for this service in the Registry. The key is: HKLM\System\CurrentControlSet\Services\PyroES, and the value under that key is: PyroServiceArguments (REG_SZ, it will be asked and created for you when doing a essvc.cmd install from a command prompt).
Running the ES as a windows NT service it not well supported.
You can also use python -m to start it:
python -m Pyro.EventService.Server

Like the Name Server, if you want to start the Event Server from within your own program, you can ofcourse start it by executing the start script mentioned above. You could also use the EventServiceStarter class from the Pyro.EventService.Server module to start it directly (this is what the script also does). Be sure to start it in a separate process or thread because it will run in its own endless loop. Have a look at the "AllInOne" example to see how you can start the Event Server using the EventServiceStarter class.
You probably have to wait until the ES has been fully started, call the waitUntilStarted() method on the starter object. It returns true if the ES has been started, false if it is not yet ready. You can provide a timeout argument (in seconds).

To start the ES you will first have to start the Name Server because the ES needs that to register itself. After starting the ES you will then see something like this:

*** Pyro Event Server ***<
Pyro Server Initialized. Using Pyro V3.2
URI= PYRO://192.168.1.40:7766/c0a8012804bc0c96774244d7d79d5db3
Event Server started.

Configuration options

There are two config options specifically for the ES: PYRO_ES_QUEUESIZE and PYRO_ES_BLOCKQUEUE. Read about them in the Installation and Configuration chapter. By default, the ES will allocate moderately sized queues for subscribers, and publishers will block if such a queue becomes full (so no events get lost). You might want to change this behavior. Every subscriber has its own queue. So if the queue of a slow subscriber fills up, other subscribers are still serviced nicely. By setting PYRO_ES_BLOCKQUEUE to 0, new messages for full queues are lost. This may be a way to allow slow subscribers to catch up, because new messages are put in the queue when there is room again. Note that only messages to the slow or frozen subscribers are lost, normal running subscribers still receive these messages.

Using the Event Server (publish)

The ES is just a regular Pyro object, with a few helper classes. Its name (to look it up in the Name Server) is available in Pyro.constants.EVENTSERVER_NAME. All subjects are case insensitive, so if you publish something on the "stockquotes" channel it is the same as if you published it on the "STOCKQuotes" channel.

To publish an event on a certain topic, you need to have a Pyro proxy object for the ES, and then call the publish method:publish(subjects, message) where subjects is a subject name or a sequence of one or more subject names (strings), and message is the actual message. The message can be any Python object (as long as it can be pickled):

import Pyro.core
import Pyro.constants
Pyro.core.initClient()
es = Pyro.core.getProxyForURI("PYRONAME://"+Pyro.constants.EVENTSERVER_NAME)
es.publish("StockQuotes",( "SUN", 22.44 ) )

If you think this is too much work, or if you want to abstract from the Pyro details, you can use the Publisher base class that is provided in Pyro.EventService.Clients. Subclass your event publishers from this class. The init takes care of locating the ES, and you can just call the publish(subjects, message) method of the base class. No ES proxy code needed:

import Pyro.EventService.Clients

class StockPublisher(Pyro.EventService.Clients.Publisher):
    def __init__(self):
        Pyro.EventService.Clients.Publisher.__init__(self)
    def publishQuote(self, symbol, quote):
        self.publish("StockQuotes", ( symbol, quote) )

sp = StockPublisher()
sp.publishQuote("SUN", 22.44)

Authentication passphrase

The __init__ of both the Publisher and the Subscriber takes an optional ident argument. Use this to specify the authentication passphrase that will be used to connect to the ES (and also to connect to the Name Server).

Not using the name server

The __init__ of both the Publisher and the Subscriber takes an optional esURI argument. Set it to the URI of the Event Server (string format) if you don't have a name server running. Look at the 'stockquotes' example to see how this can be done. Note that the Event service usually prints its URI when started.

Using the Event Server (subscribe)

As pointed out above, the ES is just a regular Pyro object, with a few helper classes. Its name (to look it up in the Name Server) is available in Pyro.constants.EVENTSERVER_NAME. All subjects are case insensitive, so if you publish something on the "stockquotes" channel it is the same as if you published it on the "STOCKQuotes" channel.

Event subscribers are a little more involved that event publishers. This is becaue they are full-blown Pyro server objects that receive calls from the ES when an event is published on one of the topics you've subscribed to! Therefore, your clients (subscribers) need to call the Pyro daemon's handleRequests or requestLoop (just like a Pyro server). They also have to call Pyro.core.initServer()because they also act as a Pyro server. Furthermore, they usually have to run as a multithreaded server, because the ES may call it as soon as a new event arrives and you are not done processing the previous event. Single-threaded servers will build up a backlog of undelivered events if this happens. You still get all events (with the original timestamp - so you could skip events that "have expired" to catch up). You can change this behavior by changing the before mentioned config items.

Subscribing to receive information

The Event Server has a few important methods that you'll be using to subscribe:
subscribe(subjects, subscriber) Subscribe to events. subjects is a subject name or a sequence of one or more subject names (strings), and subscriber is a proxy for your subscriber object
subscribeMatch(subjectPatterns, subscriber) Subscribe to events based on patterns. subjectPatterns is a subject pattern or a sequence of one or more subject patterns (strings), and subscriber is a proxy for your subscriber object
unsubscribe(subjects, subscriber) Unsubscribe from subjects. subjects is a subject or subject pattern or a sequence thereof, and subscriber is a proxy for your subscriber object

But first, create a subscriber object, which must be a Pyro object (or use delegation). The subscriber object should have an event(self, event) method. This method is called by the ES if a new event arrives on a channel you subscribed to. event is a Pyro.EventService.Event object, which has the following attributes:

msg the actual message that was published. Can be any Python object.
subject the subject (string) on which the message was published. (topic name)
time the event's timestamp (from the server - synchronised for all subscribers). A float, taken from time.time()

To subscribe, call the subscribe method of the ES with the desired subject(s) and a proxy for your subscriber object. If you want to subscribe to multiple subjects based on pattern matching, call the subscribeMatch method instead with the desired subject pattern(s) and a proxy for your subscriber object. The patterns are standard re-style regex expressions. See the standard re module for more information. The pattern '^STOCKQUOTE\\.S.*$' matches STOCKQUOTE.SUN, STOCKQUOTE.SAP but not STOCKQUOTE.IBM, NYSE.STOCKQUOTE.SUN etcetera. Once more: the subjects are case insensitive. The patterns are matched case insensitive too.

To unsubscribe, call the unsubscribe method with the subject(s) or pattern(s) you want to unsubscribe from, and a proxy for the subscriber object that has been previously subscribed. This will remove the subscriber from the subscription list and also from the pattern match list if the subject occurs as a pattern there. The ES (actually, Pyro) is smart enough to see if multiple (different) proxy objects point to the same subscriber object and will act correctly.

Using the Subscriber base class from the Event Server

As you can see it can be a bit complex to get your subcribers up and running. An easier way to do this is to use the Subscriber base class provided in Pyro.EventService.Clients. Subclass your event listeners (subscribers) from this class. The init takes care of locating the ES, and you can just call the subscribe(subjects),subscribeMatch(subjectPatterns) and unsubscribe(subjects) methods on the object itself. No ES proxy code needed. This base class also starts a Pyro daemon and by calling listen(), your code starts listening on incoming events. When you want to abort the event loop, you have to call self.abort() from within the event handler method.

The multithreading of the event method can be controlled using the setThreading(threading) method. If you threading=0, the threading will be switched off (it is on by default unless otherwise configured). Your events will then arrive purely sequentially, after processing each event. Call this method before entering the requestLoop or handleRequests or listen.

A minimalistic event listener that prints the stockquote events published by the example code above:

from Pyro.EventService.Clients import Subscriber
class StockSubscriber(Subscriber): def __init__(self): Subscriber.__init__(self) self.subscribe("StockQuotes") def event(self, event): print "Got a stockquote: %s=%f" % (event.msg) sub = StockSubscriber() sub.listen()

Authentication passphrase

The __init__ of both the Publisher and the Subscriber takes an optional ident argument. Use this to specify the authentication passphrase that will be used to connect to the ES (and also to connect to the Name Server).

Threads, Subscribers and Queues

As pointed out above the events are delivered to your subscribers in a multithreaded way. Your subscriber may still be processing an event when the next one arrives. Use the setThreading(threading) method of the Subscriber base class to control the threading. If you set threading=0, the threading will be switched off (it is on by default). But a better way to process events sequentially is to use Python's Queue module: you create a Queue in your subscriber process that is filled with arriving events, and you have a single event consumer process that takes events out of the queue one-by-one:
Pyro Event Server multithreaded
Subscriber(s) multithreaded
Queue.Queue
Consumer/Worker singlethreaded

Examples

To see how you use the ES, have a look at the "stockquotes" and "countingcars" examples. Also have a look at the client skeleton code that comes with the ES. To exercise the ES to the max, have a look at the fully threaded "stresstest" example. To see how to start and use the ES from within your own program, have a look at the "AllInOne" example.
Pyro-3.14/docs/7-features.html0000644000076500000240000014707411526311451016443 0ustar irmenstaff00000000000000 PYRO - Features and Guidelines

7. Features and Guidelines

This chapter discusses some of the more technical features of Pyro, that don't fit in elsewhere. Also it provides some guidelines you must follow when developing with Pyro.

Nested attribute access

As we've already seen in the Pyro Concepts chapter, there is a special Dynamic Proxy that allows direct remote attribute access on Pyro objects. You can use normal Python syntax for this (for instance, print RemoteObj.name prints the attribute 'name' of the remote object 'RemoteObj'). There are a few important points to keep in mind when you're using this: For a better understanding why these rules have to be followed, I'll try to explain exactly what happens when accessing attributes on a Pyro proxy:

When you call object.sub1.sub2 where object is a Pyro proxy to your Pyro server object, Python processes the statement left-to-right. First Python wants to get the sub1 attribute from object. So Pyro intercepts a getattr call from Python and the remote Pyro object returns the sub1 attribute. Unfortunately, this value is an object (i.e. a class instance)! The client receives a class instance of a class it doesn't know about! (that's the reason it crashes with AttributeError: 'module' object has no attribute 'SomeClass', when the class of the attribute object is not available on the client).

The easiest way to solve this is to add a delegation method to your Pyro object (something like getSub2() that returns self.sub1.sub2. Then, don't access the object.sub1.sub2 attribute directly, but get it with object.getSub2(). The other solution is to make the class available to the client process: put it in a separate module and place the module somewhere the client code has access to (i.e. it must be able to import it). An explicit import statement is not necessary.

But there is a serious, confusing issue with nested attributes: you will have a local object instead of the remote object! What's going on: Say you took care of supplying all needed modules and classes to both server and client. Then you want to access object.sub1.sub2. What you get is the sub2 attribute of a LOCAL sub1 object instance! This is no problem when you just want to read the value, but it is a huge problem when you want to perform some actions on this attribute, or change the value! This will be performed in your client, not in the server object!

Explanation: as pointed out above, Python searches for sub1 in object. Because Pyro intercepts the getattr call, you get the remote sub1 object. Its state is pickled on the server (by Pyro) and unpickled on the client (by Pyro), thereby recreating the remote sub1 object on the client. Only after that, Python looks up sub2 in sub1. What happens is that Python itself returns you the sub2 attribute of the local sub1 object! If you call methods on that object, or change it's value, that will only happen on the (temporary) local sub2! All changes are lost because they're not propagated to the server!

Currently the only working solution for this problem is to use extra methods on your original Pyro server object, that manipulate the sub2 object on the server (for instance, you should call object.setSub2(newvalue) to update sub2's value on the server, instead of object.sub1.sub2=newvalue).

Note that this problem only occurs when you're using nested attribute access. Manipulating the attributes of object itself (the Pyro proxy) is no problem, because all attribute accesses (read and write) are intercepted by Pyro and are remoted to the actual server object.

Automatic rebinding

Pyro has an "auto rebind" feature. This means that - with a little help of yourself - your clients can recover from network errors that kill the connection with the server. More specifically, when your client detects a problem with the network (ConnectionClosedError or even ProtocolError) it can use a special 'hidden' method of the internal Pyro protocol adapter object to request it to reconnect to the server:
yourpyroobject.adapter.rebindURI()
You can supply two arguments if desired, tries and wait. The first is the number of retries that is performed (default: sys.maxint), the second is the delay time between each retry in seconds (default: 1).
NOTES:
  1. If your server crashes, and you want to restart it and let the clients reconnect to it, the server has to be prepared for this feature. It must not rely on any transient internal state to function correctly, because that state is lost when your server is restarted.
  2. Your server must register the objects with the connectPersistent method of the PyroDaemon. Otherwise the reconnect feature does not work because the client tries to access an invalid URI. The connectPersistent method reuses the object's URI that is still known in the Name Server. It's no problem to always use connectPersistent instead of the regular connect, but there is a naming lookup overhead per call.
  3. The NS does not have to be the persistent version on disk, as long as it keeps running. All that is needed is that the URI of the objects concerned stay available in the NS, so the server can reuse those URIs when it comes up again. When the NS dies, you're in deep trouble, unless it was the persistent NS, that reloads its naming database when it comes up again.
  4. The client is responsible for detecting a network problem itself. It must call the 'hidden' rebindURI method of the object's protocol adapter itself if this is the case.
  5. The method call that triggered the auto rebind is likely lost, i.e. it almost certainly has not been executed. You have to call it again explicitly if you want to be sure that it has been executed. However, it might be the case that the server received the call and that the connection was lost after the server executed the call but before returning the result. Calling the method again will result in two executions on the server. This is all usual transaction semantics stuff, and Pyro effectively gives you at-most-once semantics (methods are called zero or one times in case of failure).
  6. If a daemon goes down, automatic rebinding only works if the daemon is started again on the same port number. This may be an issue if you start multiple daemons while relying on Pyro's automatic port selection feature. Start the daemons with an explicit port parameter to make sure they reuse the same port every time. The reason this is needed is that the rebinding of the proxy will use the PYRO:// uri that is stored in the proxy. That URI only contains the exact location of the object in a specific daemon. No new naming lookup via the name server is done.
You're probably wondering "Why isn't this transparent? Why no PYRO_AUTOREBIND config item?" The answer is: because you have to have control about it when a network problem occurs. Furthermore, only you can decide if your system needs this feature, and if your system can support this feature (see points 1 and 2 above).

About the difference between the exceptions that you can catch: You get a ConnectionClosedError when Pyro was able to establish a connection initially, but the connection gets interrupted later (because of network problems or something like that). You get a ProtocolError ('connection failed') when Pyro was unable to establish a connection at all, for instance, when the Pyro daemon on the server isn't running or reachable at the time Pyro tries to establish a connection. It is debatable if we should talk about reconnection in the second case, because there has never been a connection to begin with. But, if you want to handle this case too, just catch a ProtocolError instead of ConnectionClosedError.

Examine the example code provided in the "autoreconnect" example, and Pyro's internal adapter code, to see how the rebind feature works.

Mobile code

Pyro supports the concept of mobile code (albeit with a few limitations). What does this mean? Imagine a Pyro object that accepts other objects as arguments in its methods. It will invoke various methods on these objects as they were passed in from the client. The client can pass any object as an argument to a remote method call. What happens on the server is that as soon as the method call arrives, Pyro needs the Python module that contains the code for the objects that were passed in. If this module is not available, you'd normally get an ImportError. However, Pyro intercepts this and returns a NoModuleError.

Unless you enable PYRO_MOBILE_CODE. Now, Pyro internally returns a special code, which makes the client submit the missing Python code to the server. Pyro then proceeds as if nothing was wrong, i.e. it can now load the previously missing module and call those objects! This happens automatically, you only have to enable mobile code on the server by setting the PYRO_MOBILE_CODE config item to 1!

There is one more thing: loading and running arbitrary code is dangerous. Pyro has codeValidators to help you protect your program; see the Security chapter for more info.

Pyro supports 2-way mobile code: your client can also receive code from the server that wasn't available before! This means the server can return objects that are unknown in the client. The module(s) on the server that define those objects will be sent to the client to make them available there, too. Just like enabling mobile code support on the server, you have to enable PYRO_MOBILE_CODE on the client to allow mobile code downloading from the server to the client. The codeValidator on the server checks both directions.

There are a few important limitations with the current mobile code implementation:

It is perfectly ok to put your mobile object module in a Python package. If your codeValidator allows it, your mobile object module can also import other modules that are unknown on the server (but only from within the mobile object class members, not at the module level or from __init__). They will be transported too (if you import them by their fully qualified name). It's easy enough to write an appropriate codevalidator. If you don't want cascaded loading, check for specific module names. If you want to allow cascaded loading, check for module name patterns, for instance allow everything that starts with "agent.". Have a look at the "agent2" example to see how this all works.

Note: if a compiled module (*.pyc or *.pyo) is available, Pyro will use that. If you have old *.pyc files around made with a different Python version, Pyro will crash (with a syntax error) because it can't recognise these compiled files and tries to compile them! Be sure to delete all *.pyc and *.pyo files if you switch Python versions.

Multithreading in Pyro

Pyro has multithreading support in Pyro servers. It is turned on by default. This means that you can have a Pyro server that processes multiple remote object invocations in parallel. This is useful in cases where a single invocation takes a long time to complete. Without multithreading, the next invocation has to wait before your server is finished with the current one. With multithreading, each invocation runs in its own thread, and new invocations can be started while the others are still in progress.

Another case where multithreading is necessary, is when you are using callbacks in your Pyro object (or when you are calling other Pyro objects). Because this call may arrive at the same daemon, it must create a new thread to handle the new incoming call. When running in single threaded mode, Pyro will freeze because the calls are waiting on each other to be processed.

Pyro's use of threads on the server is as follows: the main daemon loop only waits for new connections. If a new connection arrives, a new thread is created that will serve this connection. The thread will process incoming calls sequentially, but that is good ofcourse since they can only arrive sequentially over the socket. The thread keeps running while the connection is still there. Be aware of this: if you ^C a program, you abort the main loop but many other threads might still be running. Pyro makes them 'daemon threads' so they will be terminated when your program exits, but it is preferred to clean up the nice way: call daemon.shutdown() when your program exits.

Of course, a little overhead is introduced. You can see this quite clearly when you are running the "benchmark" example in single- and multithreaded mode. Pyro will default to the multithreaded mode if your system supports it, because usually you'll need Pyro to be able to accept new invocations while others are still in progress. If you want, use the PYRO_MULTITHREADED config item to switch to singlethreaded mode (set it to zero). Pyro will default to single threaded mode if Python threads are not available. Switching to multithreaded mode if Python threads are not available, by setting PYRO_MULTITHREADED to 1, will crash Pyro. Please use Pyro.util.supports_multithreading() to test whether or not your system is able to use multithreading.

Concurrent method invocations, or perhaps not: Pyro.core.SynchronizedObjBase

Be aware that your Pyro objects can be accessed concurrently, i.e. method calls may occur simultaneously from different threads. It can be hard in some cases to create a program that behaves correctly with this. For instance, often a shared data structure may only be accessed by a single thread at the same time. To make it easier, there is the special Pyro.core.SynchronizedObjBase base class that you can use instead of the regular Pyro.core.ObjBase. When you use it, all (remote) method calls are automatically synchronized for you, because a thread lock object is used for your Pyro object. This has a huge negative impact on heavily multithreaded applications, but it saves you from much threading headaches. Note that other Pyro objects may still be accessed concurrently. If you share data over different Pyro objects, you still have to make sure that everything behaves in a thread-safe manner. Note that when you use this object base class, you don't have most of the threading issues mentioned above regarding further calls on proxies stored in your Pyro object, because you are running in a single thread all the time. However, the thread is a different one every call so you still have to transfer thread ownership on the proxy object!

Transferring proxies over remote calls

It's possible to pass proxies as parameters to remote calls. They will be pickled and unpickled in the Pyro object on the other side. Notice that the thread in which the call is executing on the remote side, is automatically taking ownership of the unpickled proxy object. That means that you don't have to worry about any threading issues (mentioned above) when you are using a proxy that you obtained via method parameters! It could be a good idea to just pass callback objects via method parameters on each call, instead of having the server store a callback object reference and the need to deal with all the gory threading details described above.

So when to use multithreading or not?

There are four situations where you don't want multithreading: All other cases will likely benefit from a multithreaded server, or even require one (callbacks/calling other Pyro objects!). Do some tests to find out what suits your needs better in your specific situation!

The "multithread" example shows what's going on, and how multithreading can help to improve performance and response times.

Thread Local Storage in Pyro objects

Sometimes it is required to keep track of some data that should only be used by a single thread. An example could be an open database connection object, because the connection isn't thread safe, or it needs this because of transactions, or some other reason. If you don't want to re-establish the connection for every call you can store it on what is called the Thread Local Storage.

TLS is a container that is local to the current thread. Other threads have no access to that data, and you don't overwrite global data by accident when you use this kind of storage. Pyro's TLS can be accessed from within the remote methods of your Pyro objects ( it exists only when your object is invoked by Pyro, so you cannot access it from your __init__ method for instance).

How do you gain access to the TLS? The ObjBase base class has a method getLocalStorage() that returns an instance of a storage class to put your data in. So you write something like this, to store something on it:

  	 self.getLocalStorage().something=...
  
The TLS object already has one predefined attribute: caller. This attribute contains the Pyro.protocol.TCPConnection object of the client proxy that is performing the current method call. You could use it to identifiy the unique client that is making the method call, but remember that it is just a single proxy that you are pinpointing. Your client application could have many proxies, even many different ones that connect to the same server. It is advised to leave the caller object alone, but you could do nasty things with this if you want to (such as dropping the connection by calling close() on it). You might also use the caller object to access any specific data associated with the connection, that you have stored there yourself (for instance, the authentication id placed as an attribute on it from within a custom connection validator). See the "user_passwd_auth" example. It puts it there to be able to uniquely identify the client, rather than just the calling proxy.

How do you initialize the TLS? Pyro allocates the TLS as soon as it's needed. You cannot initialize it when your objects are initialized because it doesn't exist yet at that time. Instead, you have to set a custom init function in the Daemon using the setInitTLS(initfunc) method of the Daemon. initfunc must be a callable object that takes a single argument: the TLS object to initialize.

For example, this function can make sure that several attributes are created on the TLS object that your methods assume to be there. If you would not do this, you need a check in all your methods for the existence of those attributes, creating them when not yet available, and so on.

Note: When running Pyro in singlethreaded mode the TLS is shared across every Pyro object and every method invocation! This is different than normal, when running in multithreaded mode!

If you use delegation instead of inheriting from ObjBase, you cannot use TLS because you don't have a means of getting to it (the getter is in ObjBase). This isn't bad because objects that use delegation know nothing about Pyro in the first place. (Well, actually, there is a sneaky way of getting to the TLS: use threading.currentThread().localStorage (it's an attribute of the current thread object). But if you do use this, be aware that your delegate object becomes dependent on the Pyro environment, and you probably chose to use delegation approach to avoid this!) Also, in a single-threaded environment, this is not possible because the threading.currentThread() call might not be available. In any case, when Pyro is running in single threaded mode, the current thread (or main thread) does not contain a localStorage attribute. To avoid any problems, it is probably best not to use it at all. Remember that you can use self.getLocalStorage() fine from a Pyro object that is inherited from Pyro.core.ObjBase, even in single threaded mode!

Examples Have a look at the 'user_passwd_auth' and 'sessions' examples to see possible usages of the TLS.

DNS, IP addresses and firewalls

There are some issues to be aware of, depending on your network configuration.

Usually Pyro will locate its servers and objects using fixed IP numbers encoded in the Pyro URIs. This may or may not be appropriate. For instance, when a machine has an IP number that is only temporary, such as DHCP-leased IP numbers. The machine can get a new -different- IP number while Pyro is still running. URIs with the old IP number are now invalid! Therefore it is possible to tell Pyro to use symbolic DNS hostnames in URIs instead of raw IP numbers. Pyro will then use DNS to look up the actual IP number of the specified host (by name). You can enable this by setting the PYRO_DNS_URI config item to 1. However note that each time Pyro has to look up a host, there is a DNS lookup delay.

If your machine has multiple IP addresses (for instance, when it has multiple network cards), you have to decide on what IP address your Pyro servers reside. When you create a Pyro Daemon, use the host argument to specify the hostname/ip address to bind the server on (defaults to '' - the default host). The Name Server can be started with a -n hostname argument, to specify the hostname/ip address to bind on. You can also use the PYRO_HOST config item.

Another issue is when you're using Pyro behind a firewall. There is one specific form of firewalling that is addressed in Pyro: simple Network Address Translating firewalls using port forwarding. Let's say you're at 192.168.0.1 (a private address) behind a NAT gateway that's 192.1.1.1. You have port forwarding on the NAT, so that Pyro requests go to the private box. However, with the way that Pyro is set up by default, the daemon will publish URIs as though they come from 192.168.0.1 -- an address unavailable to the rest of the Net. There is no way to have 192.168.0.1 publish URIs as though it were actually 192.1.1.1. But there is the extra publishhost parameter for the constructor of Pyro.core.Daemon. When constructing a Pyro Daemon, you can give it a special hostname or IP address that it should use when publishing URIs, via the publishhost parameter. The host parameter still is the "real" hostname of the machine the daemon is running on. When publishhost is not specified (it isn't by default) the value for host is taken. If that isn't specified either (it isn't by default) the hostname of the machine is queried and that is used. In our little example, host should be 192.168.0.1 (or just empty/omitted) and publishhost should be 192.1.1.1, the address of the firewall/gateway. By the way, you can also use the PYRO_PUBLISHHOST config item to specify a publish hostname.

Callbacks and Oneway calls

Callbacks are method invocations that are 'reversed'-- the server calls your client. This is useful when the server is something that publishes information, for instance, where the client cannot or doesn't want to poll for new info. Instead, the server calls a method on a callback object on the client to let it know that something happened.

Your client must publish a callback object that is a true Pyro object. In fact, for the callback part, your client must act as a true Pyro server. So you have to code your client as both a client and a server. This may require that your client must run a separate thread to handle Pyro messages (the Pyro daemon loop must be running to accept incoming calls). If your client only sits idle, and only waits for incoming callbacks, you can just run the daemon's requestLoop in the main thread. Have a look at the "callback" example for more details.

Be very aware of threading issues. Callbacks occur in their own thread. A callback may even occur while your client is still busy processing the previous callback. Your server should be even more prepared for callbacks. Usually you have a method that "registers" a client as a callback object. The client will call this method and has to pass a proxy to the callback object, not the object itself! (see usage rules, below).

Possible deadlock: if your objects enter a conversation, deadlock may occur easily. For instance, A calls B, B calls back to A, A calls B again... deadlock! B was still waiting for A to answer the callback, but A invokes a new method instead. Pyro cannot handle this yet. This issue might be addressed in a future Pyro version. In the meantime, please read on about possible alternatives.

Your server usually has a list of callback objects that it should call. Be very careful here: a client can unexpectedly disappear. You have to handle ConnectionClosedError exceptions and you must then remove the defunct callback object from the list. But you have to do this in a thread-safe way (the list must be under a thread-lock), because the server may be multithreaded! The server also has to handle any exceptions that occur in the callback object on the client. Don't trust it. Catch any exception that occurs, otherwise your server dies.
Be aware of a complex issue when your callback object raises an exception: if a callback occured in a remote method, that was called by the client, the exception might travel right back to the client if the server doesn't take precautions!

Not strictly callbacks, but when your Pyro object is calling other Pyro objects, you have to run in multithreaded mode. Because the new call may arrive at the same daemon, it must create a new thread to handle the new incoming call. When running in single threaded mode, Pyro will freeze because the calls are waiting on each other to be processed.

Please consider using the Pyro Event Service, instead of custom callback objects. It will handle all those nasty things for you, at the cost of some control. But it's very easy to use.

Special callback object Pyro.core.CallbackObjBase: Usually any exception that occurs in the callback object is silently transported back to the server, where it is raised again. For many callback objects, this is not exactly what you want, because usually the client is interested in an error within a callback object, and the server often doesn't care. If you use the special Pyro.core.CallbackObjBase as a base class for your callback objects (instead of the regular ObjBase), any exception that occurs in the callback object is not only sent to the server, but also raised again on the cient. You can see this work in one of the "callback" examples.

Oneway calls: These are remote method calls that do not expect any answer, so they return immediately after sending the remote call to the remote object. The call does not wait for the remote method to finish. At a lower level, it doesn't even wait for a protocol reply, so performance is much better too.

Normally, on the server side (the object that executes the oneway call), the call is executed in its own thread so that other calls can be processed in the meantime, if the oneway method takes some time to complete. This only works if multithreading is enabled. This property of oneway calls allows you, for instance, to start a computation and continue your own business while the computation runs. Later on you use another remote call to retrieve the results of the computation (or use a callback). This way, you don't have to worry about programming a multithreaded client, just use a oneway call. You can disable this by setting the PYRO_ONEWAY_THREADED config item to false (0). Oneway calls will then execute in the server's main thread. New method calls on the object will have to wait until the oneway method call has finished processing, as usual.

Note: There is no way to find out what the result of your request was - whether it succeeded or failed. No result nor any exception is ever returned. You're still quite sure that the call is performed though, because the request is sent using the regular PYRO protocol, but there is no guarantee (nor is there with regular calls by the way).

Oneway calls are very nice to have in a callback scenario. The Event Service also makes heavy use of them. Why? Your server is freed from the burden of handling exceptions that may occur in the remote method, and it doesn't block on slow or buggy clients. It just sends out the method invocations and continues on happily while the callback clients process the incoming method call.

You have to specify at runtime in your program which methods of what objects have this Oneway semantics. You do this by calling a special method on the Pyro proxy object:

obj._setOneway(methods)
where obj is your proxy and methods is a list or tuple of method names that have to be called Oneway. It may also be a single method name. Currently there is no way to specify from within the remote object itself, or the creating process, that a method has to be called oneway. The calling party has to set this property. Ofcourse you could build some sort of inquiry method that has to be called first and that tells the caller what methods can have this property, and maybe this will become automatic in a future version, but it's not yet there.

Dealing with exceptions

Pyro objects act just like regular Python objects. Your method calls are performed as if you're calling a regular local object. If the object raises an exception, the calling code will receive that exception and will act just as if the exception occurred in a local object.

Assume the remote Pyro object raises a ValueError exception. Your calling code will receive this and crash wit the same ValueError exception. However, the stacktrace in the traceback info is from your local code, not from the remote code. If you're just catching and processing exceptions, and don't want to deal with stacktrace/traceback info, there is no problem with this. But if you want to print the stacktrace, it is meaningless! It is a stacktrace from within the bowels of the local Pyro code! It provides no clue what piece of remote code caused the problem.

To help you with this, Pyro puts the remote stacktrace inside the exception that travels to your calling code. It is a list of text lines and can be obtained from the special attribute that is defined in Pyro.constants.TRACEBACK_ATTRIBUTE, but it's probably more convenient to use the utility function Pyro.util.getPyroTraceback. You pass the exception object and the function returns a list of lines that contain the remote traceback (if available) and the local traceback. Note that if you set PYRO_DETAILED_TRACEBACK to 1 on the server side, it will not be a normal traceback but a much more elaborated one including local variable values etc. An example:

try:
        print thing.method()            # thing is a Pyro proxy
except Exception,x:
        print ''.join(Pyro.util.getPyroTraceback(x))

This function is safe to call on all exceptions, also normal (local) exceptions. See the "simple" and "exceptions" examples.

Remotely accessing the Daemon

Usually you won't care a bit, but the Pyro Daemon that is running in each Pyro server program, is exposed by a Pyro object itself. That means that you can access a limited set of functions of remote Daemons! Pyro itself uses this for the PYROLOC: direct lookup protocol that bypasses the Name Server; it queries the remote Daemon directly. Because the Daemon is always there, it has a special, fixed GUID: Pyro.constants.INTERNAL_DAEMON_GUID. (this is not a GUID in the true sense, because it is not unique). Here's an example to query all registered objects on a remote Daemon at 192.168.1.50, running on the default port:
import Pyro.core
Pyro.core.initClient()
d=Pyro.core.PyroURI(host='192.168.1.50',objectID=Pyro.constants.INTERNAL_DAEMON_GUID).getProxy()
print d.getRegistered()
The result is a dictionary that maps GUIDs to object names, like this: {'c0a8013208585602469aec911dc92a20': ':Pyro.NameServer', 'c0000000011000001000000010000001': '__PYRO_Internal_Daemon'}.

Currently there is only one other daemon method that is remotely accessible, ResolvePYROLOC. You will probably never call this yourself, it is used for the PYROLOC: protocol. Note: If the daemon is running in SSL mode you have to add an additional prtcol="PYROSSL" argument to the PyroURI constructor call above!

Timeouts and cleaning up unused objects

If network errors occur, you often want to find out quickly. By default, Pyro may take a very long time to notice that a host is unreachable (it relies on the operating system to notice this). That's why it is possible to specify a custom timeout period. It is disabled by default because a default timeout period is very hard to decide on and the timeout logic also slightly decreases performance. But if you need, you can specify a timeout period on data transmission (sends and receives).

Because Pyro's connection protocol requires a handshake at connection time, the timeout also works for connecting to a Pyro daemon. This is nice, because evil clients now cannot eat connections indefinately by just connecting and never finishing the request. Once a connection has been established, it stays there.

How do you set the timeout?

        proxy._setTimeout(20)           # set 20-sec. timeout on proxy object (client)
        daemon.setTimeout(20)           # set 20-sec. timeout on daemon (server)
Clear it again by passing None. If a timeout occurs, Pyro raises a TimeoutError exception, which is subclassed from ConnectionClosedException. The connection has already been dropped by Pyro. (why is it _setTimeout -- with an underscore -- for the proxy? To avoid possible name clash with your own method names)

Note about NS and ES: the Name Server and Event Server have a built-in fixed timeout of 20 seconds. The connection is killed if data transmission takes longer than that, to prevent evil clients of clogging up the servers.

Cleaning up unused objects: reaping transients and passivate proxies

To save resources, Pyro facilitates in cleaning up unused connections or even objects. Your client may decide to release the connection if it doesn't need a proxy for some time. (remember that each proxy opens a network connection). Call proxy._release() to release the connection. Pyro will automatically create the connection again as soon as you start using the proxy again. An additional benefit is that the socket object associated with the proxy is destroyed, so you are able to pickle the proxy object again.

Things are a bit more complex on the server. Usually the Pyro objects you create there must stay active because it is not known when a client comes by to use them. This is especially true for objects that are registered in the Name Server, because that sort of tells the clients "this object is available there-and-there". But for transients, things are different. Remember that transients are objects created on the server, without a name, and usually a proxy is returned to the client to access these new objects. There's no way to tell when the client no longer needs the object! If it 'forgets' to call some sort of "release" method that destroys the object in the server, your server could overflow with all left-over unused transient objects. This is why you can specify an inactivity timeout for transients, by calling daemon.setTransientsCleanupAge(timeout) on your daemon. The argument is in seconds.
Pyro tracks the time an object has not been used, and destroys it when the timeout has expired. You may want to override the _gotReaped() method in the ObjBase to act on the destroy event (for instance, remove the object from a list).
Please be aware that if Pyro runs in single-threaded mode, the reaping of expired objects is only done when a method is invoked!
Also, cleaning up objects on the server doesn't automatically release any network connections and threads that might have been created for these objects. This is because these resources are not necessarily associated with a unique object, so they have to remain active. See also the "Bank2" example, it uses timeouts.

Usage rules and guidelines

You should follow the guidelines below when using Pyro.

  1. The remote class can't have a remote __init__ method. You should use a regular initialization method that you must call explicitly after binding to the remote object. The __init__ method will only be called on the server side when the object is created.
  2. All objects that pass over the wire have to be pickleable. You cannot create a remote method like openFile(filename) that opens a file on the server and returns the file object, because file objects cannot be pickled. This also holds for sockets, and various other object types. As long as you don't access these objects in your client, you're OK though (there's nothing wrong with a Pyro object that has various open files or sockets as attributes -- if you don't access them from the client).
  3. The remote class cannot support 'rich comparison', i.e. object1==object2 for instance. This is because the proxy class needs to hijack the rich comparison mechanism to be able to compare two proxy classes with each other.
  4. You have to choose explicitly for the special proxy that will allow direct attribute access, because it is slower than the regular proxy. Direct attribute access will not work with the regular proxy.
  5. You have to use the built-in proxy for the Name Server, provided in Pyro.naming.NameServerProxy. When you use the Locator, you're safe.
  6. All Python .py source files that contain the code of the objects that are used as parameters in remote method calls, must be available on the client and on the server. Otherwise the server cannot load the implementation code of an object that arrives in a remote method call. This is no longer necessary if you enable the mobile code feature. See the "agent2" example.
  7. All Python .py source files that contain the code of the objects that are used as attributes of Pyro objects on the server, must be available on the client also. Otherwise the client cannot recreate the server object's attributes and will crash if you access these attributes. This cannot be fixed with enabling mobile code yet.
  8. The class that is actually instantiated in the server should inherit from Pyro.core.ObjBase. You could define a new (probably empty) class in the server that inherits both from Pyro.core.ObjBase and the class that is made remotely available.
  9. You could also use the Delegation pattern instead of subclassing from Pyro.core.ObjBase. This works as follows: create an object of your remote class, create a Pyro.core.ObjBase object, and tell the latter to use the former as delegate:
    ...
    impl = MyRemoteClass()
    obj = Pyro.core.ObjBase()
    obj.delegateTo(impl)
    ...
    
    and you then connect obj to the Daemon.
  10. It is preferred to call daemon.shutdown() when your program exits. This will cleanly stop all threads that might still be running.
  11. Note that, because Python doesn't do this automatically, you have to call the __init__ from the base class in the __init__ method -if you have one- of your remote class. You cannot omit this, your Pyro object will not work without the initialization:
    def __init__(self):
        Pyro.core.ObjBase.__init__(self)
        ...
    
  12. If you create new Pyro objects on the server, and you want them to be accessed from the client, or vice versa (such as callback objects), you have to consider some things carefully. Make sure you:
  13. Funny and unexpected things happen if you try to use a Pyro proxy object in various statements such as while obj:. Like the limitation with direct member access, this is also caused by the way the proxy object is currently implemented. It intercepts each and every method call. And testing for zero-ness or nonzero-ness or coercion are also method calls! (__nonzero__, __coerce__ etc.)
  14. Consider subclassing callback objects from Pyro.core.CallbackObjBase instead of the regular ObjBase. (see above at "callbacks")
  15. If you want to use augmented assigment operators (such as +=, *=) and other special things on your Pyro object, please read the chapter of the Pytho/Language Reference, "Emulating numeric types". Also when you're using other special class methods such as __nonzero__. For example, to make obj+=50 work, you need to implement the __iadd__ method in your Pyro object and let it return the object itself (that is, a proxy for the object!) because the method returns the in-place modified object!
    def __iadd__(self,value):
        self.value+=value
        return self.getAttrProxy()
    
  16. You have to make sure that you keep a reference to your Daemon at all time, because if the daemon is no longer referenced, it might be garbage collected (destroyed) by Python. Even if you connected Pyro objects to the daemon. This is recommended anyway because you can then cleanly terminate your Pyro application by calling daemon.shutdown() when it exits. Usually this is not a problem because your program creates a deamon and calls its requestLoop. But a situation might arise where you don't keep a reference to the daemon object, and then things break.
  17. Pyro proxy objects (including the NameServerProxy) can be pickled, and therefore you can pass them around within your Pyro system. Thread ownership is taken by the thread that unpickled the proxy.
  18. If your Pyro object calls other Pyro objects, it is strongly recommended to run the Daemon in multithreaded mode. Failing to do so will probably lock up Pyro, for instance when a "looping" request arrives.
  19. Consider disconnecting objects from the daemon if you don't need them anymore; daemon.disconnect(object) (you can disconnect the object directly or provide its UID)
  20. Be careful when using large data structures. For instance, when you are getting an element from a dictionary using attribute access on the remote object (like this: value=remoteObject.somedict['somekey']) Pyro will transfer the full dictionary to the caller and do the key lookup only after that. If it's a big data structure, you have a big performance hit. Consider writing getter and setter methods on the remote object instead.
Pyro-3.14/docs/8-example.html0000644000076500000240000002324211526311451016247 0ustar irmenstaff00000000000000 PYRO - Example

8. Example

In this chapter you'll find a short but complete example that shows how Pyro must be used and how it works. However, much more interesting examples can be found in the examples directory. For the real impatient people, I recommend the "quickstart" example, because you'll see that you can eliminate very much of the (already little!) extra work you have to do to make a Pyro application.

For the really impatient, first two minimalist Pyro examples.

Minimalist's Pyro - not using a Name Server

Server:
import Pyro.core

class JokeGen(Pyro.core.ObjBase):
        def __init__(self):
                Pyro.core.ObjBase.__init__(self)
        def joke(self, name):
                return "Sorry "+name+", I don't know any jokes."

def main():
    Pyro.core.initServer()
    daemon=Pyro.core.Daemon()
    uri=daemon.connect(JokeGen(),"jokegen")

    print "The daemon runs on port:",daemon.port
    print "The object's uri is:",uri

    daemon.requestLoop()
    
if __name__=="__main__":
    main()
Client:
import Pyro.core

# you have to change the URI below to match your own host/port.
jokes = Pyro.core.getProxyForURI("PYROLOC://localhost:7766/jokegen")

print jokes.joke("Irmen")

Minimalist's Pyro - using a Name Server

Server:
import Pyro.core
import Pyro.naming

class JokeGen(Pyro.core.ObjBase):
        def __init__(self):
                Pyro.core.ObjBase.__init__(self)
        def joke(self, name):
                return "Sorry "+name+", I don't know any jokes."

def main():
    Pyro.core.initServer()
    ns=Pyro.naming.NameServerLocator().getNS()
    daemon=Pyro.core.Daemon()
    daemon.useNameServer(ns)
    uri=daemon.connect(JokeGen(),"jokegen")
    daemon.requestLoop()

if __name__=="__main__":
    main()
Client:
import Pyro.core

# finds object automatically if you're running the Name Server.
jokes = Pyro.core.getProxyForURI("PYRONAME://jokegen")

print jokes.joke("Irmen")

There we go with the complete example:

  1. Write a module 'testmod.py' containing a class 'testclass', which will be accessed remotely.
    class testclass:
        def mul(s, arg1, arg2): return arg1*arg2
        def add(s, arg1, arg2): return arg1+arg2
        def sub(s, arg1, arg2): return arg1-arg2
        def div(s, arg1, arg2): return arg1/arg2
    
  2. Write a server, testserver.py, that creates one or more instances of the 'testclass', and registers them with the Pyro Name Server.
    import Pyro.naming
    import Pyro.core
    from Pyro.errors import PyroError,NamingError
    
    import testmod
    
    ###### testclass Pyro object
    
    class testclass(Pyro.core.ObjBase, testmod.testclass):
            pass
    
    ###### main server program
    
    def main():
            Pyro.core.initServer()
            daemon = Pyro.core.Daemon()
            # locate the NS
            locator = Pyro.naming.NameServerLocator()
            print 'searching for Name Server...'
            ns = locator.getNS()
            daemon.useNameServer(ns)
    
            # connect a new object implementation (first unregister previous one)
            try:
                    # 'test' is the name by which our object will be known to the outside world
                    ns.unregister('test')
            except NamingError:
                    pass
    
            # connect new object implementation
            daemon.connect(testclass(),'test')
    
            # enter the server loop.
            print 'Server object "test" ready.'
            daemon.requestLoop()
    
    if __name__=="__main__":
            main()
    
  3. To make it interesting, the shortest client possible looks someting like:
    import Pyro.core
    o=Pyro.core.getProxyForURI('PYRONAME://:Default.test')
    print o.mul(5,33)
    
    ... But for educational purposes, we use the long way around. Read on.
  4. Write a client, testclient.py, that will find the Name Server.
    import Pyro.naming, Pyro.core
    from Pyro.errors import NamingError
    
    # locate the NS
    locator = Pyro.naming.NameServerLocator()
    print 'Searching Name Server...',
    ns = locator.getNS()
    
    (... continued ...)
  5. Let the client query the NS for the object's URI. Then create a proxy for the remote object. Because the proxy appears the same as the real 'testclass', the client can now invoke methods on the remote objects.

    (...continued from above...)

    # resolve the Pyro object
    print 'finding object'
    try:
            URI=ns.resolve('test')
            print 'URI:',URI
    except NamingError,x:
            print 'Couldn\'t find object, nameserver says:',x
            raise SystemExit
    
    # create a proxy for the Pyro object, and return that
    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')
    
  6. Run the application in the network. First, start the Name Server on one computer.
    irmen@atlantis:~ > pyro-ns
    *** Pyro Name Server ***
    Pyro Server Initialized. Using Pyro V3.7
    URI is: PYRO://10.0.0.150:9090/0a00009604005c6282a8a516d79917fd
    URI written to: e:\Pyro_NS_URI
    Name Server started.
    
  7. Start the server on another computer (or in a different shell).
    irmen@atlantis:~/ex > python testserver.py 
    Pyro Server Initialized. Using Pyro V3.5
    searching for Name Server...
    Server object "test" ready.
    
  8. Finally, run the client on a third computer (or in a different shell).
    irmen@atlantis:~/ex > python testclient.py
    Pyro Client Initialized. Using Pyro V3.5
    Searching Name Server... finding object
    URI: PYRO://10.0.0.150:7766/0a0000960c545c62a91e3021bceb7f26
    999
    322
    122
    0.222222222222
    **********
    String1String2
    
  9. You might want to peek in the Name Server:
    irmen@atlantis:~/ex > pyro-nsc listall
    Locator: searching Pyro Name Server...
    NS is at 10.0.0.150 (isengard.lan) port 9090
    -------------- START DATABASE
    :Default.test  -->  PYRO://10.0.0.150:7766/0a0000960c545c62a91e3021bceb7f26
    :Pyro.NameServer  -->  PYRO://10.0.0.150:9090/0a00009604005c6282a8a516d79917fd
    -------------- END
    
  10. And if you're interested you may want to try the logging facility of Pyro. First, set the tracelevel to something other than the default, 0. See the chapter on configuration how to do that. Usually you'll set the environment variable PYRO_TRACELEVEL to 3 (=maximum logging). Then, when you start Pyro programs (like the nameserver), they will write something like this to the logfile:
    ------------------------------------------------------------ NEW SESSION
    2005-03-13 13:23:40   Pyro Initializing, version 3.7
    This is initServer.
    Configuration settings are as follows:
    PYRO_BC_RETRIES = 2
    PYRO_BC_TIMEOUT = 2
    PYRO_CHECKSUM = 0
    PYRO_COMPRESSION = 0
    PYRO_CONFIG_FILE = 
    PYRO_DETAILED_TRACEBACK = 0
    PYRO_DNS_URI = 0
    PYRO_ES_BLOCKQUEUE = 1
    PYRO_ES_QUEUESIZE = 1000
    PYRO_LOGFILE = E:\temp\Pyro_log
    PYRO_MAXCONNECTIONS = 200
    PYRO_MOBILE_CODE = 0
    PYRO_MULTITHREADED = 1
    PYRO_NS2_BC_PORT = 9091
    PYRO_NS2_HOSTNAME = None
    PYRO_NS2_PORT = 9091
    PYRO_NS_BC_PORT = 9090
    PYRO_NS_DEFAULTGROUP = :Default
    PYRO_NS_HOSTNAME = None
    PYRO_NS_PORT = 9090
    PYRO_NS_URIFILE = E:\temp\Pyro_NS_URI
    PYRO_PICKLE_FORMAT = 2
    PYRO_PORT = 7766
    PYRO_PORT_RANGE = 100
    PYRO_PRINT_REMOTE_TRACEBACK = 0
    PYRO_SOCK_KEEPALIVE = 1
    PYRO_STDLOGGING = 0
    PYRO_STDLOGGING_CFGFILE = logging.cfg
    PYRO_STORAGE = E:\temp
    PYRO_TCP_LISTEN_BACKLOG = 200
    PYRO_TRACELEVEL = 3
    PYRO_USER_LOGFILE = E:\temp\Pyro_userlog
    PYRO_USER_TRACELEVEL = 0
    PYRO_XML_PICKLE = None
    Init done.
    ----------------------------------------------------------------------
    2005-03-13 13:23:44 [1084:MainThread] ** NOTE ** NameServer ** created group :Pyro
    2005-03-13 13:23:44 [1084:MainThread] ** NOTE ** NameServer ** created group :Default
    2005-03-13 13:23:44 [1084:MainThread] ** NOTE ** NameServer ** Running in single mode
    2005-03-13 13:23:44 [1084:MainThread] ** NOTE ** NameServer ** registered NameServer with URI PYRO://10.0.0.150:9090/0a000096043c5c63117eead5b89ea267
    2005-03-13 13:23:44 [1084:MainThread] ** NOTE ** NameServer ** URI written to E:\temp\Pyro_NS_URI
    2005-03-13 13:23:44 [1084:MainThread] ** NOTE ** NS daemon ** This is the Pyro Name Server.
    2005-03-13 13:23:44 [1084:MainThread] ** NOTE ** NS daemon ** Starting on isengard port 9090  broadcast server on port 9090
    
Pyro-3.14/docs/9-security.html0000644000076500000240000006235511526311451016474 0ustar irmenstaff00000000000000 PYRO - Security

9. Security

This chapter discusses the security aspects of Pyro, the features you can use to control security, and some important warnings.

Imporant Security Warning

Read this carefully: Pyro is a technology that may easily expose private data to the world, if used incorrectly. While Pyro has some security related functions such as connection validators, it is imporant to understand that exposing a remote object interface in any way (with Pyro, with XMLRPC, or whatever) on an untrusted network (like the internet) possibly creates a big security risk. The risk could be because of a hole in Pyro itself or because of security issues in the used libraries (such as pickle), Python version, or even operating system. Be sure to know what you are doing when using Pyro outside a trusted network and outside trusted applications! Pyro has never been truly designed to provide a secure communication mechanism, nor has it had a security review or -test by a security expert. Read the Pyro software license and DISCLAIMER.

Authenticating using Connection Validators

To guard against unwanted or unauthorized connections, Pyro uses so-called new connection validators. These are objects that are called from the Pyro Daemon to check whether a Pyro server may or may not accept a new connection to that daemon that a client tries to make. By default, there is only one built-in check; the number of connections was limited to a certain amount specifed in the PYRO_MAXCONNECTIONS config item). This check is done by the default connection validator Pyro.protocol.DefaultConnValidator. The fun is that you can supply your own validator object, and that you can therefore implement much more complex access checks. For instance, you might want to check if the client's site is authorized to connect. Or perhaps you require a password to connect.

The default validator already supports passphrase protection as authentication validation. This means that a client that wants to connect to your Pyro server needs to supply a valid authentication passphrase, or the connection is denied. The check takes place automatically (it is performed by the default connection validator), at connect time. The following items are important:

The Name Server and the Event Server can both be instructed to require authentication too.

Look at the "denyhosts", "authenticate" and "user_passwd_auth" examples to see how you can use the connection validators.

How to use the default connection Validator

You can specify the maximum number of connections that Pyro accepts by setting the PYRO_MAXCONNECTIONS configuration item. This limit is always checked when a new client connects.

To enable passphrase authentication, you must tell the Pyro Daemon a list of accepted passphrases. Do this by calling the setAllowedIdentifications(ids) method of the daemon, where ids is a list of passphrases (strings). If you use None for this, the authentication is disabled again. Note that the ID list is a shared resource and that you will have to use thread locking if you change it from different threads. To specify for your client what passphrase to use for a specific object, call the proxy._setIdentification(id) method of the Pyro proxy, where id is your passphrase (string). Use Null to disable authentication again. Call the method right after you obtained the proxy using getProxyForURI or whatever.

If a connection is denied, Pyro will raise a ConnectionDeniedError, otherwise the connection is granted and your client proxy can invoke any methods it likes, untill disconnected.

The default SSL connection Validator

For SSL connections, the Pyro.protocol.BasicSSLValidator is used by default. This is an extension to the normal validator, it also checks if the client has supplied a SSL certificate. See the "ssl" example for details.

Customizing authentication using custom Validator

All authentication logic is contained in the Connection Validator object. By writing your own specialization of the DefaultConnValidator, you can control all logic that Pyro uses on the client-side and server-side for authenicating new connections. You are required to make a subclass (specialization) of the default connection validator Pyro.protocol.DefaultConnValidator. There are two methods that you can use to set your own validator object:
Client side, on the Proxy:
_setNewConnectionValidator(validator)
Server side, on the Daemon:
setNewConnectionValidator(validator)
In both cases, you have to pass an instance object of the validator that you want to use. Don't forget that you still have to use the two methods already mentioned above:
Client side, on the Proxy:
_setIdentification(ident)
Note: the ident that you provide doesn't have to be a single string (or passphrase). It can be any Python object you want, for instance a login/password tuple! It is passed unchanged into the connection validator (see below) that creates a protocol token from it.
Server side, on the Daemon:
setAllowedIdentifications(idents)
The "denyhosts" and "user_passwd_auth" examples show two possible ways to use a custom connection validator.

Below you see the meaning of the different methods that are used in the connection validator class (and that you can override in your custom validator):

class MyCustomValidator(Pyro.protocol.DefaultConnValidator):
...has to be inherited...
def __init__(self):
Pyro.protocol.DefaultConnValidator.__init__(self) ...required...
def acceptHost(self,daemon,connection):
...called first, to check the client's origin. Arguments are the current Pyro Daemon, and the connection object (Pyro.protocol.TCPConnection object). The client's socket address is in connection.addr. You can check the client's IP address for instance, to see if it is in a trusted range. The default implementation of this method checks if the number of active connections has not reached the limit. (Pyro.config.PYRO_MAXCONNECTIONS) See table below for return codes
def acceptIdentification(self, daemon, connection, token, challenge):
...called to verify the client's identification token (check if the client supplied a correct authentication passphrase). The arguments are: daemon and connection same as above, client's token object (that was created by createAuthToken below), server challenge object that was sent to the client. The default implementation uses createAuthToken to create a secure hash of the auth id plus the challenge to compare that to the client's token. Effectively, it checks if the client-supplied hash is among the accepted passphrases of the daemon (hash of passphrase+challenge) -- if any are specified, otherwise it is just accepted. See table below for return codes. NOTE: you can use the connection argument to store the authentication token (which might be a username). A Pyro object may access this again by getting the connection object self.getLocalStorage().caller and getting the authentication token from there.
def createAuthToken(self, authid, challenge, peeraddr, URI, daemon):
...called from both client (proxy) and server (daemon) to create a token that is used to validate the connection. The arguments are: identification string (comes from mungeIdent below), challenge from server, socket address of the other party, Pyro URI of the object that is to be accessed, current Pyro Daemon. When in the client (proxy), daemon is always None. When in the server (daemon), URI is always None. The default implementation returns a secure hmac-md5 hash of the ident string and the challenge.
def createAuthChallenge(self, tcpserver, conn):
...called in the server (daemon) when a new connection comes in. It must return a challenge string that is to be sent to the client, to be used in creating the authentication token. By default it returns a secure hash of server IP, process ID, timestamp and a random value. Currently it is required that the challenge string is exactly 16 bytes long! (a md5 hash is 16 bytes).
def mungeIdent(self, ident):
utility method to change a clear-text ident string into something that isn't easily recognised. By default it returns the secure hash of the ident string. This is used to store the authentication strings more securely (setAllowedIdentifications). The ident object that is passed is actually free to be what you want, for instance you could use obj._setIdentification( ("login", "password") ) to use a login/password tuple. You have to use a custom connection validator to handle this, of course.
def setAllowedIdentifications(self, ids):
To tell the Daemon what identification strings are valid (the allowed secure passphrases).

(the following only if you subclass from Pyro.protocol.BasicSSLValidator (for SSL connections):
def checkCertificate(self, cert):
...checks the SSL certificate. The client's SLL certificate is passed as an argument. Note: this method is called from the acceptHost method, so you must leave that one as-is or call the BasicSSLValidator base class implementation of that method if you override it. See table below for return codes
The three check methods acceptHost, acceptIdentification and checkCertificate must return (1,0) if the connection is accepted, or (0,code) when the connection is refused, where code is one of the following:
Deny Reason Code Description
Pyro.constants.DENIED_UNSPECIFIED unspecified
Pyro.constants.DENIED_SERVERTOOBUSY server too busy (too many connections)
Pyro.constants.DENIED_HOSTBLOCKED host blocked
Pyro.constants.DENIED_SECURITY security reasons (general)

Pyro will raise the appropriate ConnectionDeniedError on the client when you deny a new connection. On the server, you'll have to log the reason in the Pyro logfile yourself, if desired. When you accept a connection, the daemon will log an entry for you.

Name Server security plugins

The Name Server supports security plugins, to facilitate access control to the Name Server. Different options are available: You'll have to write a Python module that contains the following: When you start the NS using the '-s' switch, it will read your module and call the two functions mentioned above to get your validator objects. Make sure your module is in your Python import path. The NS prints the names of the plugins to show that it's using them and then starts. Have a look at the "NS_sec_plugins" example to see how things are done.

Mobile objects and Code Validators

The mobile code support of Pyro is very powerful but also dangerous, because the server is running code that comes in over the wire. Any code can enter over the wire, correct, buggy, but also evil code (Trojans). It's obvious that loading and running arbitrary code is dangerous. That's why you should set a codeValidator for each Pyro object that might load mobile code (mobile objects). The default validator offers no protection: it accepts all code. Be aware that a simple check on the name of uploaded code is not enough to make things safe; the client may supply it's own evil version of the module you thought was perfectly safe. Currently, there is no mechanism to guarantee that the code is safe (for instance using some form of "code signing").

This codeValidator is a function (or callable object) that takes three arguments: the name of the module, the code itself, and the address of the client (usually a (IP,port) tuple). It should return 0 or 1, for 'deny' and 'accept'. Pyro.core.ObjBase, the base class of all Pyro objects, has a setCodeValidator(v) method that you must call with your custom validator function (or callable object). You can set a different validator for each Pyro object that your server has.

The codeValidator is used for both directions; it checks if code is allowed from clients into the server, but also if code is allowed to be sent from the server to clients. In the first case, all three parameters have a value as mentioned above. In the second case (code from server to client), only the name has a value, the other two are None. For example, the code validator shown below is taken from the "agent2" example. It checks if incoming code is from the "agent.ShoppingAgent" module, and outgoing code is from the "objects" package:

def codeValidator(name,module,address):
        if module and address:
                return name=='agent.ShoppingAgent'              # client uploads to us
        else:
                return name.startswith("objects.")              # client downloads from us
   .
   .
   .
mall.setCodeValidator(codeValidator)

Notice that a client doesn't have a code validator. If you're using 2-way mobile code (you've enabled PYRO_MOBILE_CODE on the client), you will silently receive everything you need from the server. This is because the clients usually trust the server... otherwise they wouldn't be calling it, would they?

Firewalls

Using a firewall to protect your network has nothing to do with security in Pyro, but it may affect Pyro. Ofcourse the firewall can be used to fully protect your network for systems outside the firewall; it can make it impossible for those systems to connect to your Pyro servers. But if you want to access Pyro objects from outside the firewall, you may have to take some additional steps. Because they have to do with configuring Pyro, and not with security, they are described in detail in the Freatures chapter.

The pickle trojan security problem, and XML pickling

Security warning: possible trojan attack

By default, Pyro uses the native Python pickle protocol to pass calls to remote objects. There is a security problem with pickle: it is possible to execute arbitrary code on the server by passing an artificially constructed pickled string message. The standard Python Cookie module also suffers from this problem. At the moment of writing, the Python documentation is not clear on this subject. The problem is known to various people. Using Pyro over the internet could expose your server to this vulnerability!!!!

Using the (safe) marshal module is no option for Pyro because we lose the ability to serialize user defined objects. But, if you accept a performance penalty of an order of a magnitude, and more required bandwith (2-4 times more), you can choose to use the safe XML pickling. To enable this, set the PYRO_XML_PICKLE config item to the appropriate value. You need to have the appropriate library installed otherwise Pyro won't start. The server will answer in XML pickled messages also, regardless of the server's PYRO_XML_PICKLE setting. So make sure that the correct XML packages are installed on both ends of the communication. If the server is configured to use PYRO_XML_PICKLE, it will only accept XML pickled requests! This means that if you set this option, your server is safe against pickling attacks.

Please note that at least since Python 2.2 a few pickle security flaws appear to have been removed, and the obvious trojan exploit with pickle no longer works on Python 2.2+. But still, do you trust pickle? ;-) Use PYRO_XML_PICKLE if you want to be safe.

If you decide to use the 'gnosis' XML Pickler, there is an additional config item to think about: PYRO_GNOSIS_PARANOIA. It sets the 'paranoia' level that will be used for the Gnosis XML pickler. Higher=more secure. The default setting (0) prevents automatic imports of modules during unpickling, because this is potentially unsafe. However, it creates problems when you are sending arbitrary user-defined types across the wire. The receiving side may not be able to fully reconstruct the data types that were sent. You could explicitly import the needed modules on the receiving side, or you could consider to set this config item to -1, which enables automatic imports of user defined modules in the Gnosis pickler. Note that setting it to a higer value than 0 breaks Pyro altogether because the pickler will operate in a too strict way. The only sensible values at this time are 0 and -1. When you want to use mobile code with Gnosis XML pickler, you need to set this to -1 as well. Note that you have to use the same Gnosis XML library version everywhere. You can't mix older versions with newer versions.

SSL (secure socket layer) support

Pyro supports communication over secure sockets (SSL). See the "ssl" example. Because Python doesn't support server-side SSL out-of-the-box, you'll need the following add-on libraries to enable SSL support:

To start using SSL, you need to tell your Pyro daemon that it must use SSL instead of regular sockets. Do that by passing a prtcol parameter when you create a daemon, as follows:

daemon = Pyro.core.Daemon(prtcol='PYROSSL')
(the prtcol defaults to 'PYRO' ofcourse). All Pyro objects connected to this daemon will get registered in the Name Server using the special PYROSSL protocol, that tells Pyro to use SSL instead of regular sockets. You may also want to add a special SSL connection validator on your daemon that checks the client certificate. The client programs don't need any changes because Pyro knows automatically how to deal with the PYROSSL protocol. There are a few configuration items that deal with the SSL configuration, look for PYROSSL_CERTDIR and the other items starting with PYROSSL. See the M2Crypto homepage or OpenSSL documentation for instructions on how to create your own Certificate Authority- and server/client certificates. There's also a nice guide here.

There are a few config items to tweak how Pyro uses SSL, where it can find your cert files, key files, and so on. Have a look in the Configuration chapter to see what they are (they all have 'SSL' in their name)

Note: it is very likely that you have to set PYRO_DNS_URI to True, because Pyro uses IP addresses by default while SSL certificates will usually contain hostnames. If you don't set this config item the SSL certificate will be rejected because the name won't match.

Please note that there is a known bug in M2Crypto that makes it impossible to use socket timeouts (socket.setdefaulttimeout) when using SSL.

Pyro-3.14/docs/index.html0000644000076500000240000000104711526311451015555 0ustar irmenstaff00000000000000 PYRO Documentation index This is the index of the Pyro documentation. There is only one interesting piece of literature here at the moment:

Pyro manual.

The following might be of some value:

Pyro homepage.

Pyro-3.14/docs/LICENSE0000644000076500000240000000247511517657576014620 0ustar irmenstaff00000000000000 PYRO - Python Remote Objects Software License, copyright, and disclaimer PYRO is Copyright (c) by Irmen de Jong (irmen@razorvine.net) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This is the "MIT Software License" which is OSI-certified, and GPL-compatible. See http://www.opensource.org/licenses/mit-license.php Pyro-3.14/docs/pyro-large.png0000644000076500000240000007616611517657576016412 0ustar irmenstaff00000000000000‰PNG  IHDR,‚üõ_ pHYs × ×B(›x IDATxœì½YdÙyß÷;ËÝr­ÌZ»«—éY"@(’"%pSXa>H$m˶~Ãa3”åÚ|’ [Š MjãfÉ  )‘ @ìÉÁ3XfÅÌôôL÷ôôVÕµgå~—süpîÍ¥*«»§g$5õEddVæ­sÏ=÷Üÿù–ÿ÷a­µ˱˱ÜB¬µ!ÈŒAÀèo|~=E~[Îr,Çr,eÅ$IÆf«Â’¦YšaŒÁZË·Kï9¬c9–c9RLF­^ÂÏý‡gùÉ_{Œ÷=|Ò˜NÈ0NÈ23®×[ôë~†c9–cù++ÖZ”|ëÚ>Ÿ~f‹Š¯øÅϼŒ—Åü·Î±?ð(—ßR¾¾æá±†u,Çr,G‹…$I¹·¡ùîÕ ùŠ_~è&þôƒý=öö» †C²,{ÝÍÃcÀ:–c9–[Jœd”5üÜÎQ $ÆBfÿê«-»¸A·µGk¿Ë`“¦†×Ó2<¬c9–c¹¥ 3L9S¶ü÷o x öcÉo|³ÏÅ+›´÷Z´»=âØiZ¯—Oë°ŽåXŽå–"„@+A/ƒï˜KùÛ -†™ Ôp­§yï“CÖ×·ØßkÑí ˆçˆ?¬c9–cù¶Š ¥DkM) A‰w6†|o´EßH*<»ïñÿ==`gk‹Ý½}z½I’¾.þ¬cÀ:–c9–#ER‚Àó(•"êõ*•F“j´y“Ú¦omùʦæ“ç»tv·Ùmµ †$Iúš›†Ç´†c9–c9R„H)ñrÀjÌU†ÄÃ!?¯ÓÝõX·uJ:åSW5çj{¼SyxZ£”D ‰ðÄkFu8¬c9–7 Xk1¤dÆ EΟ ˜!@iIàûT+’ùŒ8ÉÈÒ„w§|¤’ØX>ø¢`¥¼ð||O£”BHãh½zÐ:¬c9–7˜&š’›eh%0©!1 ”Ó† È}–HiñC§‘J!¥Àá‹¥$¾¯)bM45diÊ;ã«Ü¸¾Ã³É%òå­û.wyw¸ƒï{hO@ëÕ2áëXŽå $™1()ø÷_^çWºA5P|ùr‡gn´ù?Öd¡‘øQ裵Ôd¬µH)ñ=ŸrÉ’f†$IIâ˜wÇk¬¯G´L„–]õ9SÛE!AࡵBIùªµ,õž÷¼ç=¯Ùh˱Ë_Z±Ö‚µ  ¿úð:íØPò_rm?ãÒf·×‡XœI¨¤DäZÖ!3QÊ\X@™°Çóý Z Z©¢ÕOyKuˆö§iiå|ZâîðÇ´†c9–7d™EÚ”Ó5MœXc©‚¯oÂÿóõ}¶o®³»·O·? MSŒó© °QJÕj…f³ÎÜâ"o_öùÞÒ6#)iË“­G¯ôèìmç©;CÒôÕñ³Ž5¬c9–7ŒXÒ,#‰cæÄ€¯\‹d‚;Ф×åþÒ«}|íüO…6h¹‚~2a!É –ìrµ+i™!`«oySÔ¥xÁDäð.MÃcÀ:–cyƒˆµc ~Bv »<¾ç9Êñ¤àù¶G”´9SÊrz´)7iÒóŒaRTŸçz%<)ØN4A6àžrŠ B‚ÀùÆ”’we›„Çr,o)ÀAIZÅ[ªC~8¸Al%°°­$Y+óÄË»ìooÓjw ãQ~à¤yèH¥>åR‰F³ÆÜÂß¹ìó–¨ÍÀHeyd'âÒÍ}öw÷hwº ãø®s ëXŽå $.‘Y>ºTã{Ê-Þœ¼D,=$+@bàñá+Wol°·Û¢ÛëÇÓIÍ…†¤µ$|ª•FùÅ~hqHY$!he>_ZWìïìÐjué÷$I2å»S96 åXÞpâLÃ,Ë&†hï =h ”5XZÀVê÷{Ü ~HàiÔ´x,y§©¥ÆdZ>—²Üx¬ê e…„øKß)ÚºS9¬c9–7LA-…_Ëî^áš­ëÒº"|ZÂÕA@3Ýe¥$OàitZ®½ G¼ËëÁXHRC9Ùçù=Ißj+i o.÷ ¢ˆ0ðrü¤Cÿvr XÇr,o™Žò‰œc%±B"³!vï&WÔòø8kÉ„âzOqNíR‰üÀÍd{î=ÏC´NËÒfHÜëp¾HËVì±$;¬”~èðÞˆg øcÀ:–cùO@¬u;Ü€ÓpŒ±Læ0OƒcJ‚tïF(Âáûí.7õ"ƒ¥e|zý!÷‡]tøúÝañs”k-qj)§û\jYv3‹ NS…!¾ïåZÖ™†Ç€u,ÇòW\Œ±HK¡Á 1H‰Û;03ÌÂZ2é@ IÔYçÚУ«ªH °û,f»,•^0f®;>ÕAíH`±dÆ@3èvy®âIË^¢9©Ú,”4A„…ix XÇr,ÿÉ‹1)¡3HøÐ·vyÿ7wxø¥[í'K¤™=š…Rбv#$¦¨Ö—m#5`‘X¡ÙîU{„¥AàáyÓÚѤ¾­$1„i—çv “iR6¹7D%¢(¿¼ Íí@ë°ŽåXþŠŠ1°lµcþñ'oð{O¶¸´›ðÜÖ?»ÔãÂæ·Ô|iɬó/Mš„… W˜†B@†ÄÛì·ÚÜ ¨‘–eÙ1!Q¼Çj”âE%çƒ:5„ mËBj ÊÄìwzœß÷ 쥊³ºM£ì–"‚ ÈÁïöZÖ_(`9ÚØþ‘Òòï ††p|¶×uƒFÈ£&fÜ'wòÂðúžû/BŠ(‘{wßMF‘¾­çŸñ2Åum¦YN¼Æj¥%Ë\ ;ƒdöZX1ÁG/,iêlñ×ûÜß.1ÆUš´¹)á&nñ¬YŒ‰¥b¦ÿã®ÏW¹4ÆÕd’؉óÛñ ÷’Â=î}Æ‹/aŽüh!3“÷íήÇäàdòUXëæEßÌøsŽ”ÆX2SÌq°ŒÔù5ž/Ö‚Äp}wÀ¯}moÔ¾;·ÀXKY ^îJz]29.óR8Ì‹¾>-„»2™Å ÷w¸”5.ð‡ÆÒ¶!Ä]î øå2aè´£ƒf᨟X’Ì ³!›­>/t||û©ä^¯Mµ•"Gd½^ÖëZËZw®ø—REG,ÃaÌæn—½ý>Ã$c§¤™A+‰ï)|­¨Uj¥Èw¼‰+–Ùúz Ö9°ÝŒ±´‡ûCà 5¤Æ’7·µ%¾„Š/¨xnnHéüA)\©–&Ó«›ƒåf{HœÙñå;%„ µ–’¶üÙV…E¹Éš ¥À=;ŸÊչҔˮn{oe™wìw¸°¶ÍñDбàIË3ñO­õ¹ÚJÙdì ƒÔ@Z(%ÅXJÇœý]˜«Bøþ Hø·?ìó½'CÐa^DÎͱ¦5r×ñaœr½r¥rq7e½›ÑN-âÌ’g2#Àò¤¥ì *æC8W•¬–%+eE=T C¡Ò=¯v±+\)Ã$£¦SJôR”¬ÈâBRÁÇ6ë,é›|§×¥’2˜ê‹J¥D³Q§b‰ïoßd­=‡Ur­®#K<Ùò9½±I¥V£Trô9ZÈ‹Ðä¿ø÷ð£ß}’J’$jd{€ù— ¸œÆ* ³<ø­5~ï‘ßÜ5(!¸™†ÜØßeq¿M·;GµZ&´~5,kŸÉiŠKW6ùÝ~ƒ}îi^¾±‡ú¥À£\ò¶=eçæ«†™Ì(ù6Ab¤V ›{}>ñðE>ù•Kœ^®òŸÿÀ½üÌ»àMgˆMF"Tî,ÌÅ[ƒÈÈñŸe ãk-B:^+I(Ýb|í…m½°Í¾ùe~þï~œn'>å0Àæ\’âFýEJaŠk%¹¹ÛåŸ}ôŸ{f +$_”tîqRlf²¶¶É5¯G}a%‹ÅD¹•û6ŽäÂ/¥%  ¾ÐâƒOíòäZŸnbñ•Ä÷$õÈy˜–Â3Řh°v§ÎßE1w Ml"X`s–µEà‰Œë×nPN*Ì/[<¥Ðž3 OA–f’׺Ò&_YÛç'¢›9cÝ='RzS ãy¥(¢>Weay‰ïÝ»Á »1F(À¢Èh©*ÏîûœÚÚd®Q§R‰ð}­¥{†òö¤”hå´¬°\áMK-–®fl  —º÷µ÷ét{ úC¢(ÄóÔ‘–Ò«vºi­7·Zü‹÷§üü/‚Ï~ùÃ8£\ò §.Z;nxÈGi4Æ7mb2OD µR„¾3÷{ _}fÏ<ú2kÛûܳTb®â'Ùhð:ZO á2ÐMÊ¥ëÛ|üÑ+Óçfü ¾&ð4ovùⳜ¨HNÎ$ÆNLºWïS{uR€•à‰—vøÙ÷?Í#/íS =|-Ç0oáØ6KÃkTâ¥0 ý#TmäiJ áÓÏìðO>{ß|‡û)¾’”|…–.r4Ž®ÍИfiQ#3Q®ZaŽŽ= mY¥ñ‡æo~ O ¢(¢hß#ÐOZž^ðÞ§:|ôâ€õž!Ô’Š/ t®Qå~'¿ŠÏJÿ.^R:M¬Ð.¤È}[J*§Mn'’§Z’ó»"îÓT1§‰âÚ(.ãösÇZ0YF˜t;ø{7xnX%“Ò©YSš¨’õØçT¶A-òñÃß;å+üP®(ŸÅ‹;\i¥lØ2žp3Ç I/œ2ÔªJ¥ˆ ôG‹Ü!绵$i†L‡\Ýp±­ÐR’ý~‡r¹D¹9'þMmR^`¥iæ_Àï}ôëüÿôùä΃TËÎQn&é £eê€ós¤Eå5高:&¿øü†)¥(šA’ñèù zü:ÊfÜ»\FAšYÔDÛ³ž–,s™ë—®mó©o\›Ù·ñÑúŠý¾á‹ç·9[œ¨û¤¶p¸ÊWl’¾–’f<žyy—ø¾§Xo%Ô#Ì* ãw!rnŽÓZj»—¨É˜rµF­Z"*EÚêôäsï`¬ÁS’ kmÞóÙ«üûG6Ùìe”¸°"_p&¤ 3õ˜ ·6ß' : ؾLåêã”j5jÕ2¥J™¹R@+¶üÁs=~ÿ¹>}CÙ—”=‘›h s@ònVÅg•Õ´FïÎ-!óþK¾x Z©â©–âZ;¥fúTU6rÎËÑ¥^h'Eä×iŒ%MSzqŠèì0ØÙàŠ^fäv<0VKW„ úÎé~äæž§rMk¼Pÿ–ƒM3’ÎÏö*£¶†®ˆ¨Æ;¬D¥JÅ‘@=JcG/ ™1˜4eÐëòÍ 7V=£8¥ZÌWßèA¹+Àr&`†§5k›{üOïù ¿ô[Ÿ§?L¨U‚‘ù65`£òˆ•QŒoÔ´suä&WÙ _…”‚(ôhuþì±\º±Ë›N–©——‹L)aù£onð|â*ÏÜPñ5ž*È̘™šÔ-€iÆÜµuèíÈ î]¥±¸H³Ycy®Ìù®æ7¿óÔ–¡ì ʾs9 ä)÷ÒÒ˜§Ü»VŒŽ)~›Ô°”›ó{1ÎÓËýuÇÉÏýak±æ[{†]å+$VŒi=S×7CÜš‘S72¸ð÷®³ÙMÙ Q6ÉHJX¶MÈâð&ó‘ÄœÃ|R“žÖ²œ¯Ì¶¹¸gØ6ºðù Mš$œaÇ9ß˳é î͆IŠJz<¹‘² R4eÛçL)&*W)—‹ª¤³«8¼bÀrutܦŠ=rÿöç?À—¾ñÍz %¥[Ég®¦““nâ9â*®ô6útž/^Øc®T€Õ,°?ऒhóyCËâÒóÍ êOPÆþJ­~Ì?ûÌ~ó+ %ßñ¢íŒ{?k>LÞ¿é{^s+›­qI%Ñ6#ýÚ'¨{°¼²ÈÒ|oÆó|øš&6‚z G ãå`4§´\¹àŒ&Áê I¨¦Àj°ò÷bç™÷Y਩œïl·‡¬ˆ:_8”£4•;›?“2z÷*—i’èa úìx|–Xú †1gÙ&,Wr.Õì²1à‚76é·[<×+“ @ßj–†7™¯øT*e§eéY¬u5–85ÈtÈõ>Ï·$ž„4ƒ{ý6ÕJ‰J¹D†µß§¯ÿqŒÉëžâ?|üëüÌ?ú]._Û¥Y/9-¥0;ÆÃô ä¨ÿ°³²_扵’ÇÝ¿ð¾ÇøÃ?–~gŸV»C0%„ªth‹ËÑÝ8xÂÌ@Ù<³‘òàÓëìmo³×j3ÈÏs·;ƒÜd™Ekøòù->ý­-æJÞH³šìóôN—e†,53Y wz@ÒÌà)Áú^Ÿÿùƒù£'w¨.\Ÿ™£pöw㮦É㦙·Éü0"½ôÙúK¥%_JNòù½2”}§‘Àä)§]ôÏW_9 *~óµûÝ÷Üg?ÿ.È_Åçâ{_ãŽÏçžãö4£ó*é6}({‚'†uþðªÏ•[´[-:½>qœ’eÙ-w˜‘R¸Ý˜Ã¹¹*‹+KÜbŽ·_ÄØ1èLŽ•<2.Ë%žÛJimoÑîôc’$ËÓ}&Û÷(—BÊõ:oW,ª©3v‘†ªÄËýöÛ]ý!I–Úi§ 8GX*ñ¶%E¨çm×D¬w-ýN‡nÏU#Ͳé¾rÇQB“«ž¾§ù<Ä/üËOâûšRä‘eæ¨ÄÀ&'áí£N¿¢¯±Ž[zŠÔZ~ùÃçÙkùo~ü͘Ì8Tà#„ž‰(BN3N'dñBH>ÿbŸ·,¯£ƒh*ÝýþzŠK1 )ï{ø*If}…K((´«Ù×5!r¼å(˜M2ƒ§×;üã?¹Ì…­!Í’&3E㶦5©±b=ùÝL'Í\ÕïÙ¦ ããI2:ßü3æ}M•¹°øNvå"‘:‡tÚÓ¤¶¤e•ŒüOî³@æÍK!F—%¦:eGAXëXæFºÅSmL­È5àq"²40Ô=õ´Ê7?™lqv9%3u*6Â÷¯vrŜԼ”Rø¾O¥\¦ÙLè\åí¸Ú½Áõèžó¨jNÀµ ¬!U¹o}“Ê\“J9mx:é4Wy9åR¥ÂÉù*oZ°ÞvLXëè ×E“­«,íwèõTâ$wæË©>K)]iš âÞfÀRØg­/H…ææ@Ñëvéõ CÊå¤!Ž'À=I#Ú‚§ùõ÷?ÄÿöKs¶ª’dÙÄôžµx3û»Ùšî4(ˆYß{5C{‹±%¾§øwŸ»Ìû?ó,Ö.»{mƒa¾š¸)8 @‰§‡{dq~‰µ®stïnmÓÚïÐÆù*1«×VŒ±h×ÛôÝh9³ÍÈG4‹³5«ƒŠv†âÂÍ!››Ûìïwô†$Iеw·;ÈÊ8ýÉðÈÅöû)zê®TOî¨Õ ®´k_ ËÚNÿõÃ/qe/¦ªü¼Ó+àÔ‰`ËÔùg-64¯Ûš‚Åo&#¬ÍÑ»ð;Ÿùõ¹:ú‡~šìÄÙ¥ÔØ/5áH÷&ÀËWAÇýV˜yS`¥‹—œø<6}/og 8z²ø~ `…O¬¢ -]æS{ ^¾¾Ekw/7“CótR P)E!sõ*Í¥%Þºèq.]#F#°‡ÇÑr>®³µ±áÙþ`4g ‘RàiExå2÷7}NúCëî»ÓÖB®e5Z»{´;ý#6Lu›V ßó(—#îm¸¾I,Û¦Ä~?¦×sÿŸ¤³¯ù6&¡%Mfõ¹/=ÃÏýóø)òê†BÜÁp u;mëNšý>}€µ®°™ç)Þûàuª¡æ'ÞuÏÈ©þá‰p$ÚCí"…`­+ØÜÚa~¹Í|Ó‘é‚ÀGÊ£™»¯…XkIâ”g®wFeD¦4Ѓš w0Œù{fSº;Hù…^æ¹µ07Ò¢L«©âàþy–ïêVË̬ :G÷…ÇÙøÃC͇ÆOü×è7½÷Ðz¬Y£p¢ÿVæaa&¡œ2 Ç-x‚…ÕF€’–Ì8ß^&©Œ3/'4ï ]˜²5lë9>×Jù;ö&–I~_afMÜG!F©W¾ïQ.E45+˼³½Î•dÙÕµ²¦˜î\ÂU½ê-sugƒ…Ý=ææª”JÑT23äy†¾+³0Wá­Õ6/m(œïB°©æØÙ{‰vÛ_œ¤„ƈŠÜ„õÐ~È}MHÉ,tðÙV{]úƒá¡z ¹%`e™ÁÓ’KW6ø_þÏ‘eß—¹zz‡“ëàï·Ã¯W ”‰£ú01£yQB†à·>¥šæ»¾C¢¤¢H©°ÆL‘ªoÛ‰ƒŠ‹ôRÁÖN‹ýý6ýž»iYæR•^/±yµ€Þ f·› T‘Ivœëí´ØxScøÅ¯ñå—;4KštVtbRcQZfŸ"wõÔ IDATã°9x'Ý*|0v´pˆ D)Më‘O³ó©ß¡*3–ìïRúžw;°RcŸ•’âEÁ›ü[8°QDîÃÎ%&üY£n¸iÇäf—G+0ÂåJéüXÂàüWˆÑÐŒîSΦµ¬„²ÍXóxx·ËÊ5„šÜyÙ›ÊïœeV+ezóó¼yw—{olò,§p»áŒ'·@ÚŒ¾Wçb»Â¹­-š óÔªeÂÐG5òµ 9öeE•*om´xh;%E!Hkè¨*›ÝŒvkŸ^¯OœÄdiˆ>€0E?•ïsª0 Yï†øl'ƒ~Ÿ~¿¬,/P8^ð¬¢LFo0äý_ÌË×w™«Gyõ£þë¶³î€h’U*tm½19wj·gø­¯ñ¿WŽëR¦Ã„w‚œyoó‰g‚½vŸN§KPì½–aíëZk ý8a·— „¸àæ2Ë;Ô¦#¡F>ôø¿ÿØs‘>y<ÐÖlpä€rU<é@¾­”È“’®š„ÀUçpå!~ R!<k2Wž§ýÅ“^|œfµÌÂw¼êßúi¤‰GdÏÂG¤EnŠÉiƒw@»Òª 20âPIœÉ äŒvLqÏH> Rä¹{‚Lº2=Ò€´ä@g‘#:7¥s.UñX+FšZ Ão•úÖ |Ÿ·V¹sœ÷whˆ…Ë,EµZ……¥¾ko‹ú©óeå éã‹’kržíËÌ·Ú4UÊ¥hÊi>JŒö4~)âÌ\À‚Ÿr=VH\´0Öe6;íV‹n·O<ˆIËž™®v*„@K‰ö4seŸ³5¸Úq‘ÕM18 k8ŒI³,ßñ‚ē䈡¾§ùõ÷žOñ< Í ivØk¤ÌDµ ˆ„©@8œ{òˆœ‘—"9d^ÌtŠeBŽ¿7Æù‚çÖc>òè5þ»²TŠZÍzrTÄÿ`KbjY=Z2Ý~L¿?`0Œ§(“ùU¯¥¸LKœ¤ ’Ìå^Ú[gòZ WLß/ë4OZ.oöøµ‡7ð¤œZÇÇŠ©[sè4“æhq¬1®ƒ ´çÁdØdˆ5YÞãl+d`³kRL¯Íàò³ /=MvùiBiY:½J³^!x÷O¡¢:8¦´+”02ûÔ„¦¥r’ŒøWZ‚ùﰎ†A Ð²âÁ,Q€òÀdăs6+)1ÒÑ^2cIóˆ`2…+VÅc•"—˜írsŸLN±pãy¼ pŒôQï0G©H6ö}Ÿr)¤:×ä-ó{œ¼Þâ óø$ã©aY¨lÊž?ÏZûEV÷vétT«Â0ËR9<Î)AЬ•8[îòòÀ¬q&̶¬ÓÙߥÓë3Æ$iJ02 Ýi¥Ž+—ë¹9ÅÃkBÀ¶‰èö  ‡ i’ætÛhXî Åóoð¯÷!ªåÐQfÌûCrKõÞ‚ô@HlÚÇöw¡¿² ʦ›!´Ð>V…˜¨‰  “_Ø {00göIŒÐ—<ø|‡¿¶ºÆ÷y¾Œ’O–eG\ÔQ×4~ø…p7nÇ9§%pêß¾Ù»FŸº?£uá(³ ³m¶cÁd¼÷‘Ööê‘§÷äm9mjÒ¯7ù>£m›!¤BFL<`xóe—Ÿ%Ûº{0ìB–PlŽ0ÕݸôI• ¨„>Õ“ËÔs4Kâ;¾ŸÎÊý¨tˆÒjœN#Æ 4Î OÊÑ÷Þ?–ÄEµZ%&Ü|ù2Ï~í›\½x‰~»K§ÕBõæ¥Z•÷œá­ï|'î9ƒ_‹èvœóZæÉûEa‘£X,r}ÇÜs&¢ÁõÃXo3za•'öšÔo¬á…¡Ó|r‚ç¤i8•l¬aP®–XœŸã»vZ\é7Æ&õĽÖø%®íWx`{—Ήž£”¢œ]˜±bD¡(—KÜWëòðŽå„ `ß«³×Y£Ûqm¤I:‰÷µHˆVžÏýM@e`-]²CÜ0ŒcçR9àx?X#¿…üßïýsnl´GÄPÄ•q‡LvÒ:Jz˜þvïÞp_$.¡Ø÷ð|íi”Hcº¤ƒM†Ÿ¡ž£W>Mæ×g˜n“ŸýõAQº‰àSOîrf!ÂJ¢ŒÍw™íO3>øKÒ4#‰“ÑÍr*þëƒX…æfsÀJ3;~†ÿôiMx]¹ 2cñ¥ák/·ùÌ…6•@æNv1c`ÆàtäÐçÚŠ ËØdÈÞ#Ÿ¢ûÔðsÏÄ®þY¸Ò"žçVa;‘) @† U)ãù ‚(¤TŠ(—"æª%‚z“îûë(›H™a"9™ÜücÌŚЬŠÜA) Õj›¥|þCã ù8×^¼Ä°ÛÃÓŠ ð ­ëÍ(tÿ§¿ÿGœßRoó…Þ€®Q¸Eyªb–ÜôØÞ»H{¿C¿ï"ÜïâLÊ÷^rßœ¢$-‰•®ú«Íèú vÛ‚nÛµ1œrœç€-\F€Ö åy¬Ô<Êz@'± …G7á Ž\*ãë;XYÎdÿú—øèŸ=M½ä7å*Õ-Â"¤Æ¦1vãYt÷:¥@RiV©V«”+%Ê¥hTjUçQc q3èèw:ì.°ÝŸg?8…Ur¦§æÎËXµàüfÆã/nòaˆ®l†¯%ýÄÎhæ6m[—[•XI–¹•ÁWÚùõ”b2Ä©¡ÛÜ$<ÐÕ[IáÌv)a­!MR>v¾Klœá0æÞB›ÍASŒVt Ëtžÿ&û_ú0¬_¢R-S]ž§Z«R.—(•ßw)Åžw9X“ÜÕPSAàªX–J4Jšóѽ ¼ ‘æÿ—›…#]Nפæ•ÿ梃–Z½Ê³|?þÿ—ç’r)baiú¹ÓãÔ?ßLT:Í(MSúý­]>û¾ðäæÇþ«Ÿæ­ßÿ}ôº}DæJ>«¢Z†•9Ä ”uÅUN8ÍŒ•‚V~ÀE¹Ì‰µ«”jµ1¹Ð¦¦†ÂIä„ÏS³Û=žL"Ü>8Œ´-gf´Ãy¶ZÏÓÞß§×' ™ÉШѽ.|d:X©ù¬ /õ}|ÂZRå³›ùt;Ž@Z0Ö‹ -£5TºÚòJ»"™Íöó¹ÛÎÃáx“¦.heŒùÓ¦«(lo2Ã{ÿè«ìw4ê%G½#÷˃¬åc‡mX{Œ²ì1·Ø¤ÑlÐhÔh6jÔëUª•ò¨2€ÎËcÆ ýÁN§Çþ^‹Ýí-ÖÚ/qÙœ!•%4ö(*ÙmTA! 1‚¯\êsnqË9sÅS‚^lï¨é˵Xé‘YA61ÐGóÆ_;ÖÒdÄY1·»YME Éa,_ZžÙˆyì¦G¤óꡯh,ò/, %ÒØù‡è~ñ©UJ4î=G³Q§1Wcn®J­V¡R)åâ¸2èÁM:¥R®"­ï®lQŒæÊÞ <Ì4C½-9¯ÂåLAFÎx•ó³jõ2Ÿyÿä÷~ùß>÷¿éÆsõ*zZ½B¹\†.?.Í2†Cç»l·»¬¯mðÑ_ýM6¯\åoýÌßc0ˆ‰SƒUcmÈÚÜg•›ôÊ¦à˜¡ŒÀH‰—¥´*'¸tóeæ66GÀY€ÖÁ$aAîËò<Â( ^¯ñÖJ‹§vÜæ¨Î7¥¦ç†+â©©E>ó‘äÅ=ƒ¯%m  †‰ûÿ)nS€eŒÅS’óoð©/ÆÁޏœBÃËÝB°ùñ÷’>õy–Ož`qq¥ÅËË ,.4h6ç¨×«”Ëa0NišµuÔÈ÷‘W´-)Ëço*v2Í\àÌ-9¢"L&#ùUZLD'(¥RÈüʯó‰ßy?'V—Y^Ydq>ïçb“f£N½^ÍSXÆÕ5 buÇt{ö÷;œ:µÂæÆÏ>ô†í6?úþ>à¼ðÖŠ<…ǹ2kQV -å€ËXPǺ²0"ˆ¸æ-±º~úü¼ë‡ï*#rRˆÂç$ }Ÿ RáþZ‹ú^L?¯—5=GŒòÙU5ö÷öètû ‡Ãl ÖÊ)³ÐSš( Y.u'œ.`Ö“Ýî6½þpz‡7p;ßK¡f¡¬0ÖiÇø$É>Ã8!IÒ)æý”IXøA”¯øÜ—γ±Ýa®Vræà;®F€ô°Ã6ÞÆ4j>K++¬žXduu™Õ“Ë,/-0??G­V&Šò²žÛI¶èO𦠇‰[¹:vvZÜ\hИ[£ru‹¯nibÔíô˜#ŸËÀ(ί÷XmîP®Ö ” /zÄ›±_ IGÏ‘&.B’¦fä4¼çô®eÔ®µî}àËË ¬ž\buu…+‹,-6i4j”˹Ièë—QÚ$I ‡t»=öZmæ,,6xþ™ó|åÃáoþýÿÓ`•»«r ’›ó¶Šk0Tnºw¶Ê k×_dqg‡z½2ª¥îÀé0h9-KD'ë‹^Bk 8^’BÐòæhuÖéu{ .pT°Íǹ‹Î Õ~À‰ªBKÂàø]¦;Hô -­°6쨅‰¯”Diå²B‰€>qjH’x XÌû)ÀBXºÝ>Ÿþ³¨|RxD8â©!Á$è­§hT}Nœ<ÉéÓ+œ;»ÊéÓ'8yb‰…ùµz^©0¯¹Tø¯ ÉŒ­^ý~Æ\¹ºÓÈÊåóâ6¯k¬¼{b¦”‚—ö%ë›{¬HE¤¿Û\·!,]£ß$ƹ oF+ÄëAm°ÖÇ_í¡fpsnÛ} ‰HH“8­³ç±›h5Garþª¢ZDfŠþÚñµJkZÑ[ÛXiuèÏÕ¨”SŒñøˆÆc柮Fû¹J¹k°rt«ÆÇZÈtÈ~"èvy3MŠ7ú@›®Ý¹’¦î ÙºH¡°–LGt3Å0×°’tºÔÌädrf¡b¹âªÓk0̘°RÒ‰=I­,gº´›'ž»ÎN«O¥ä5ufLÒ#%7{”Ó-N,±zr‘{î9Źs§8{æ$KKóÌÍÕœÏÊÓH5»²à¨EëüZk|Oãå/· þFü/µ´–·™™"\tÍÍŽál«CSK| ù8þÖ­YŒôx)iòÝí.Ý^ŸažÈï–×ùJe´Ñ†?üÆMvû)õHO˜ƒÓ€4ó Š?¬Al_Á÷ šm¡Ä­Fò~¬é{_û$õ²Ï‰“+œ=}’sçNsîžS¬žX¢1?Gµ<.x°ØÛ­+mº~%IÆóùî+“€$&ŠâóØŸ5Ú—CTòù“?ø Ãv›ûßþN¯.sîÜ)ÎÝsšÕÕ%šTóýò<­·ØdD©|Ñ×O{øž—/ÆíiÒ8æú3ú”‹.b'€©ˆ¼1ú,„«ŒWØÞxŠVkŸ^¯AœT ˜m“ã(¤£è0äDYPV±Í¢'î™°£<ÚÖ§ßé¸@ÁŒÅ¶0ç„ö¨Ešù`Àú<‘+RÓË4¦iJ–S¦û殩¨šª»CH…fh\àj¤aA {@ÃJ’„Ǿu•45S!éÙr´áu¯Ò¨E,//rúô Μ>ÉéS+,åf@©”ƒ•T#gÞQ"„À vZ˜Dæ~®ÌZÈÞµy…Ï=Ÿ:NËT´äöR˜Û±Ï^{ŸJ% ÒƒLÞXO ‘µ- kY—vw¹g¿E§»@½ži®º¿6faf †Ç^ÜãÓOïPö‡wpë„”˜~ÝÞ"X È€}B$æ÷ôÜ…vuñIÔ ,Ÿ;Íê‰EΜ9ÉÙ3Î0Ÿƒ@9Pkà߉XkÖ°ÕM¸Ö1ø#q»ÛŒwŸØM\,Q)äêó/ò?ýsN=Åòò M–WXYYda¡ÁÜ\…RåÔ…»3F¡m­‰B—àÙhÎqßé%ÎÎû·MÌ>Rá–Ä*:YÂRç¡ü[˜>G|oËfðéK°›—Mîvû¹óÕù³ŠÊ¯ZZ~åÓ/òÅö¨øêpÛõ»)±›/z’R0T©3þív`R‘uZè½ë4]tíä‰E––æi4êT*¥©ñ¯t8Ÿ¦ÛÞj«—12— ßW¶™ª|a”Ò]—ÖšawÀågžeqižÅ…&++K,//äµìK#ͪhû•È$­À÷=Êåõz•'—‰lJÚïºÝ¦&´§Éþ¦ ®C`‘žÏP…tö;ôz…cû(?ÑøÚ•RDÇbhffrXÒZÐS—Z§d©a²>Ö(Â'%R+ªþ¤QcA(Ré‘Ä1qê|`ã|ÚÙãä{*÷¿ºçÇ‘¯ °kXÖZdáØ–B°¶±Ï^{GëîäÎL¬%0mêõ* ó –›,Ì;žM”ƒUaÖÝ­ŒÃµÎ‘X­UX^jò¶³5—@}7mZK*|)ÄiÆÙrLYgG¤èܪ!÷f€PÁ3»{¦C{g‹V«M§Û•͸›€FfMÂ÷ï{è2ïÿê j‘Îsüî´¯c€A*L¿ƒ¼ö-Êwªt¼¶b-xÙÍËDYùÅy,.4i6X…¯¬Æ§±“±Þue¹ ­ÐPò©Þ½üÀcóúu¶®^eie‰fsnĵªä‘·‚^q·ýEé´Û¼\ŽhÌ7¨E>É~ ©Õ\‹@A´ÅuM2ýÉSÂ’º½ý±}›ùä4Y…ïij¾˜™fZH*=†±#m&iŠ13À0ï“TŠª?ó)ò6â$#I’QøÃ5çÆhh·d±'DŠÄC𙉽"òkB[³ì¶zôÉ8½ãŽ%?Ød”tF­^¥Ñ¨ÓlÔ©Õrê‚ïÝ‘àNE*‰çiJQH¥Zá܉:þ!Ò÷ßHÄXÒÌÒŒàžrLbî,wr–ëÀ^÷}u›­-zín?ϱ:\íôÈÞYK’™Q>Ü¿ýäþùÇ_$òî€ÎqTóÖ"¼€ôÊ3øý]ªsu¢RD¦|ŽVÒ8'Ôö•PÓhÌÑlÔóíÂ&AàÕÍÇ{ÊØí˜ÿ3Ñ‹Ñç¢KLî÷àã›W¯¡€f£Î|³žóÁʹ¹úêÀªIM+È“†çjd< 0¥Gµâ'Áu¢ÑN;"¿Bk±^Ho Ý¥Ù(çnv?Æù…ß=#‡tè|l2'©£¤GE¸ URñ'[rŸRé9°Éû5kžî•/ßmÛZmOqõé̇±ø÷Q”¬+‰’e!g³½o)ŽËùŠjµB­æ^åri´1âk1 `¼âÕ £RÄB£‚¯%IÎ]9<<3šú55ÎWÚã-ÍŒ:cï´¬…@ >9¥¬ñÈò¶\XÝål:J‡:äËsý0Öº°·p¾šÍÝ.¿ò©øÞ yãÐô,𸉉ðÒ7©Ö*Ôk*Õ2VèìÂ;½^c\%…Š»÷õZõPÉ«½ý6ON¤‹ºû,¦>2-ùqÐíº:èõ*µZ•Jµ”ƒfmQõê¤pc„GµV¡—Ieb¢o“§ŽŒ`¥f0WqäJ÷ v¼çÿ-J+*ÞÑ×%p`˜¤†4;¼‹(Ýd›R:§yÕ—‘Mï'¸÷T_F눯bb?¸ÂP–Öþ~ó¿ýÅ—yy{@-òòÕèn;e~‰ôê³í›48Ç\½J¥RÁöòøÑ+½WùÓ%“¥’Ûv¼R‰ÆÌp©F¾š»•bµ5Æm ð<õ |`'±!aÐé8£R¦R)QЦK¿Vsu’!îyžKòï¥$y‡FšÓþ×ÉHáèû˜µO’¤#³­Ð‚fiXE†…ÌKö„Ú$Ó:k}ë[ߢ\øµ^Ÿ;ÅÓIK¡G ü«×7ùöÕ=~õJ•_¹É—/שÄqàú:”Œ%ÃDñÉÃ=^»ºÅß\ÛâÃø®]À¬fã1¥£«ñˆôÃW©TbF•Jµ Cûì“Ƈ*%qUjÎ0a†;¦A÷3®xzõfR÷,’ ¾æg.SQx»¤Ä÷\ÂÀ' <\C`~ÖÑd×ê$#pF0:vˆ/$çËZÆ(I“v{VÿMA¢Ç´5i2%M$IIL‹Pi#ÇQcytT„=LL4s§„SƒS$šQ›‘qõß«“<¬§z؃çyø®‹ãdm Ïî‰uâ#M•¡?†iZÆ” IDATQrùæ3¶¥Rë{.~TâKK6¿¼sÀ÷Yøî,‰•‹[–Ú•|›µƒ„?}gƒ?w‹FÉ¡{Ô"JäÒKv޶ÆlŒégQuä“>9›wÚ”D%o}ƒ`çÍ/~V£F³Q§Rޱ7>{AÀvôMé{®~XM)/<Õ¦õò•îÝsÄŒÒÀ(Æ$}Ö¼¡¸Z!3ñ8KÍŸ>e=Í4P˳ØÞÉQ§þPxÙ¼®FÃ)é)Ïè¥5N\ £öLÚæTS©&û,qrô=­8ÉÖjâhrÀ}–3‡:J'˵UjÄ ø•ÒÆA`¼×éf/nº|êvo±ëþy˜Þ®ž?x4LuµóB$ÇYÛS)ÂL)Å!D¿ñ˜[»#Ö†®éa6ÓO-a¸6R ö)Û‡R¥L¯›V“ô‹ÀÓ¼4‘ïS)J!¼€ñ£[¨¾Kc±C§Õ ÓmÒlV)—"Í‹:ë28çÐZBä NQ× žrñÅ%(\!ÉRˆ|]ÙïÕäu5õwÙï%a©Œëy9gÊÖTôð<,‹´ôdeÖ±ï¯õø½.ãaÞ®’ß³çݻپ™Ÿº‡J0§ó·+OÄ9‚lš‘ŽÔÔþÌ^š`$'ï± ]æ­²í89ùáœ^ĬeÓŽwÝ?kSènöͽ!G£„0pgѧÜe&Á×׌ÄR˲q]‡8 pAÊon=àÿ½š¢°ŸÉ-§f¬»Ñw…¾a,&7šñ©<íê:Ísœf”” Þø*õЦÓíÐí6évš4êUÏAˆQ¾…'¾FíÕQ(Û5ºíÓk+ú‡ì^=~Ø”9äNáIm¤N¦qSk“éIÌ*£Í[ŒLQŠ{jÖç9ƒTŠ$°”‘Œšmâèi¨Ýš¨;NÏ#{–-W*ÅÛ×7HRUxš? +`«Ûv´ðYP­ÆÔM¾|¥Î¯vFŒÒ“Г/zú‡ì"•J§{:?¤|bËÖ+%Â8úá·ðwnÓ^Zb±Ûdq¡M»Ý¤R-â8’=÷õ,Ä×8sVÏh_Èáijnšc­E—“ÝÒD. •GÙ1MÆ Í¥•v‹áp˜cbEÉéçaJIRc××75L­-¿ž û“9„, –E²·…@ŸGmçäì´;bFìIò‰á‘¡+W2íx²g©}Lð5óïtåó¬Ê$$ÆùP^­ü+Š( ŸOXœ’ÛfËžáu¶ìyûÚ:®cóø4!°Ò!cl×Áuõ|·8Ži6ª´º]~óÅ€/•táéË9í‡ ¼þôvꖥƭúᅧzÿÛ´{=zÝ&½¥®îïkÔ(•b<Ïã—Ä)V<ç9àûŒRÁlû‹†§¦ª]Ç#uÊk A2NJe:—.sxp8°ŸãÀ¥*• ±[®ÆwLÊ” _ÍÖŒ"ÿ]ö0ËÖŸ&’do ;3ÌXü節T*vFºyr7M\:8@{5Ç÷¯ì ©B`%É£:Fš>&úoR)ºTl=N_;"ÿÌYûöTö¬+‚§Y"%¾kñÆÇ¸ñ`À³Ï¼àfÿJãv2ÀµŽ«ÁbßóòžÅV«ÎâÒ"ÿ²àŠ·ÃHY9¯ç™ûŸöÉ÷<3LS¬ Äàú[Œßø÷´ºm:,/-²¼Ô¥ÓiR­•‰ß(^da翞U¦ž¥åÎв(9Šˆ„DM²¹šgñ+‹Zó( j/ýüÏ3NRúý~Þ÷vQ2ï“Xµ%IžpI”žt&ÍÿJfï™DU ©} ›tØ'ÙÝÄ5ƒ0l#ÍÃ)…-UØV*;ÃÓO‹²bp¨åwì¬3k»Æ³î %9jaB^{t”÷ žçø„0Vjæa)"²Ï§€Ý^Ä1_Ø>/ @[Æ9êùý¯}À(ÜŸÀ ïw[¸®N]×Áõ|ß#ŽCõ*n‹åÕ%þÞJ‚½ËP9gß§ÁKÏÐÎõ[Rb…ew?døÚÑlÖèõX]^È…å8Âó½\OÿÉ–}HÿŒÍ „M̓¦;f,uõVbn~œö•*Fƒ /¿Lue•­mÃM .>+ÓÎ2å0UìY>H™G:º2QV(®9Ou-‹Ñþ.éÖc‚0Ì¥–2§rÚ寔Ɔ†©dc`ŽáÌÝX£C-phÛ¹šÆñbDF4O¥bw8 ¯ d‚5îcÙNîð&?{}I*uJ˜qք̃ýùVÎI»€ÃúüS›óM‘$šóõ×?å»ïÞ§¹ >–gŸ½Ëo¼KpàyZÏ(ð}*åíVƒ^¯ËåËËüæÂ‹l0VYüIŽÅS†g~TV1Sdcµú·ÞcðÝC£Ó[² Õ4—h·šT vå8¶ÁœNû¼ç1_Ä2î]äÙ,8C²‘YEJ*•ßàÙÏù_ø?5säW~ùWØÛ?dOOzÑN+åYaYYi?Y“#3Î+UŠTÖ'O:)Iá5©Àv9º {Ü'ˆ¢œçèØNž>–ÙX(Ö%ÛC=„ãØ*uAfãÆ,1Ü'G„Q;'¼feù¬¥¤Z-‘ŒÇ¹Fò&¯?ìsK.iB#¢ÿ™Ld‰éVýf™”à¸Ûáàío‘¼ûMšÍ*K+K\^YäÊ•.]ÒªŸõF…( plÆGŸ{Úg^Âå=­eM·Žë²êòF_"¹#š¤€bêfW–0t”ec1Œ¨¯\bðò+<~øˆf«®Ç´éÓÂ9)³6Rl*”$‘z iî¤Ô‰HP*rêBžÒ"8ºsϱˆâ0ÐV˱rm¹Y&•ÂBòøH²;ïøí¢4OŒñë`w1ÖÔ¤ŒCw,:Òׇ¢?–ìäÒ;`;%ƒ±Ù&-< )¥Ya‡CÉv_O-·UŠOÂ7éà4úSç°’D“;‡Ã!ÿèŸý®ßÛ¦¤ê a¼âMu¬V¬„{ð˜Ð‘DqLøínÛ("릒'¥æLq“èÁU®ªË¤n [eÃÈűùœ»úÔ__Ę÷˜0Ýò#Òá{¯þÖíÑ]\`q©Ë¥•E^¸²ÊåK=zF¡ Gx®b0N.ÀDŸµ1upyž‘·}Ó$„ë³$ÔèÓW%l¥û>§¢’©TP}RyH•–%éWXã5ߨ{_ÍŒIÿã“;®ÌѦlî¤.RbœUÆâή'íT¥:þ5‰²”e3ØÝáðÆû4kU¢00 (.®íœY)T ’»{)#©•D’u ‡8£}¼ ‰ç9§je$óþX²=PØT@¤c\9Â5ЊãXy5sÖ1²Pl% ¥§ü¨.)"svÇZ¥¦çþmçÜtÏÙÆIŠk[ †#þÑ?ý&ßyûµJ@’êÉ>ŸIÍSŽq·tOY>wÎsÌÓË÷=s9Zt–íÜ"¾{Oú]öƒ®ÅžË:ËÎshÇýÀÔ¯=î”Bx>›áý«½ù5‚ÃÇ´/¯²¸Ðbue‘Ë—–¹|i‰¥¥.­vƒJ9ÎoÌlÎ^¯:{Ÿ>'„@ û×l×¥9¬Xû¼/K„“›_è¯tê{=ì!•™fº ’Db[6Í/ÿ {þµ‡òëèèãIUQ3gµ3qcè0’BOç–ŠDB"•޲SWšfƒTÉ/ÓáEìÝ|vש\ú¥8$Ž‚é^ÝYk1ëI“”6S¦º×òªËCí­ã!ó¶SÐÚ/ÞWyÄv°3Ð2Õc³Ãl•âx“¿Ÿ¥W,*<Úëm ŸŽp…qX&`(û‹EXŸsØ?õÑ Ó* p›Ç{ü7¿ýuþ¿7nQ/z<ûy6ëFS Ö‰Ò=ÊÕ+TÊÚi…¡Ÿ³´‹=‹¾ïM6i’Žçàù÷(ß¾ÇÍÝ<òVF]=ýE%ž=ÙóD3{1ḭ: á¸à¸Œ×ïqôþ_a=üˆj)¢óâ ,t[³Z]bey‘ÅÅ­–­–ɨX–¾IÎ^Ò1g¥0³'òoø¼<–äÂxAñ²·ÆÇÉí`åÑ”Iû$¤Ùl?©´Ó™ÃR$„ˆqŠï”þÎ/³sçÞƒG¹Ff§5EË{æL¸>L¹1´é§z’y"ÃLiªlƽK•þ>û_¢0‰ I%›ï¼FúTª•\¶É3J(§µ¿)©Ë£Ã„ë;RÏ=˜qª”eÃðÍØ½âЩ}TzÂͧÛcF)Dn–vÛ¸‡›¸$¸®g",çD””Yîøö Ÿ®PØ–Sp˜“c?Ãauå^ìfSÅ/5)­^IJü8û;„–Ÿ°-É·_¿Îÿø¿‡ÝX§^Ÿ ¯nƺ(îæuJ¡K­ZÑ#ÄJ1AäÕ—‰Ã„vZ–Säº.çDå»÷h>øˆ{wÙ.]! `¹$¨tòÁ³Wtb­'ÞcIÂvÀÑäÃñÞ:ÿGúé;D®¢¾´H»Õ`q¡ÅÒ’®.÷ºt»M–bÂ`2töñÉÒÌY‹,âWEgõùT ³‡‡çظaÄj¤X<Ü䡳ˆ#RR)̸÷ Ž¥–qí¨DQÔO F žëà\ù"Ûk[ÈñY«è¢„ëh•ÍSȰúz• %ƒqÊáäÞØb,µ³§:’JR¥£,¥&¬ð%Md%³ï¥7`ûê‡]û½—.Q­ÄTʹ³¶õTiɧ; ŽžÍÉ{QT2F¬ß&ãšÑkÎŒö:¥$*•ÜÚ“È¥p·pm Ï®&=š³ƒ†$•¬Žó«*R#=8ÃÕS¾·÷=; Kˆ¢‡*LôÏqXÌ1)^ŽF#Þúø1¿÷§oñçß½ŠDP+é40ë%œy럙Â(­e~°IØDua™Z­L­®gú¾‡c;S¡hNγm|ß3%[sø¾G”J1åjÚƒûÛ»‡¸þ>ƒabH‰gGCR*ö‡ììØÚðö'ùî;wøàÓ vGTc!,Òô´iÔO–·Ú? y4uZ͵j¥ºÎcõž‰ê)¸ž§5æã8¢VÕ:óÍÇë¬?zÌÆÆM¶î~ž(1\üETuN`\ÅÔêØ±5NYX.ãí5Þý6jó.^rH…TZuêÍ:ÍZ•V»¡{»m»MÚíõz6P!0zúŸ¡úu^¤•ÿüù˜Âây„• Wâuz;xè^Æ‘ ©™F“ Ej¢ãTiÇd™ö*Ë\BV¾Ùj”à8ûÕúéˆítLé0!¤f`‚……@ A*#%ØKûRЗ¶¦V˜t/Q§”êž¹,ºÊp¬©H+癩>ðCÖôû¾ÉK/]¦Q¯ÐlÔ¨VJ¡oä›Ná+=+pëhÌŒqŠ8\Ž_)-m½q•p|H\^! 5½Ç-L¸žâ`)ÅÞ áînŠ“?ˆd„7ØÁ/k9lß/:¬ãk¡‡Ã„õýDVŠH ¶ãØ“Je¡Jyz„õ„‹P’Dx¼»Ýä£ïíá¾ù.¶ó¾n3lÙ¬<™](:–Ô0ž8’ F GÄá8Á5JµR É¢g¥¥Ê8jó&Qÿ!ÞeÚ­:­fƒz­L‡xçLJB˜ß»ù%Ïó£r9¦^«ÐjÖXï´ØØØbcc›­‡÷ùt¼ÅžX2ñãyéu1ÍRÇeôèÉÕïÓ\Z¡Þ\¥Q¯éÖ¡f•f«A»Ý ÓjÐjÕ©yê8Ê*LïÓÌ}ËVŠî碑ÖçgyZèºÄ‘ÖJÿÒÖ}Ö‡M’ ÆN%©°H… ±–„Dè)H©ˆÔ S³õÔ(K UªZ.‰í±/ÀR;;Í•®J¦è9ƒš<•aRG”EU©„±’Úi¥Y¤%'ïQÚ©¥æu%,†G}>ý‹?¤Z iµ´[u3}¨¤qÈS¬@>·òƒµ!7w%]TWÈN¶¹¡×>%tq¹D‡æçb[Ó8©$6ŠO·†l ä„ÓeÙˆýu\9›†TTšvªJIŠõƒ´ãsH ÔËvò*cÆåÊöñÜ”pêú<³r¥Â(µ& Cs8ô…¡/‘]q“ƒ9.!Lºü}Ï!Ê”5T¦…²{G)®éoš½ÇT2ÔèçÑ»ÔëÚí펾ɳ`ÇfÖPaYú½™TúÚiÕ+´[u¶¶š¬mî°Õ©±»^b;I§ÊÀùâf@HS» ®Ð\X`qi‘Ó¸Üí4iµ4›UõµZ™rvÑ‘aÎË'ÍEm"Ø29îªxÏ›^ÈôÔèYY–º®K”k5Vk\^»Æuï—p,ÒDW©"A;+ ÁØÐ0R_Où‰˜< ”­B •ÀVK‚-„®6š‡lŽF0a®§NUæx¤iîMe†cMÚQ2\+•z˜qˆ*Üú‹ß'}t‹î/|‘v«N§ÓÒS³K¡‘Ÿ\§Å*y–Zi«w‡ SðM¤9Ù_Â"öáþ'”*JqD‡Fƒß.àvf›,•òî£!GcEÕ×ÕOeÛ8‡xŒ  ðM„•1æ'çO™TÝŠ«kŽÆ©~ø¤ûDŒ°œ2¾7˜œ·÷p.†u‘4ðØÅ„šT,„@ëârA‡5¹qu)Veqû´×œy3œêß+a¡ný€ª;¦Ý]e¡Ûb¡ÛÎ'»hüêbÜ›ì=®«ólǶñ\—0ÔxV­V¡Ù¬Ólí±Õ(ññ¸Ü”ˆY’Ë =0öÄ>¨üx8ŽM½^¡·Øæ…Ëˬ¬,ÐíêqêÕjYKýFï›'”–^ÎZ–.ìDr@ª˜ªžæQ‹£#NþyÏËoit›À÷(—"j.¯l^ekã*{ _Ä’CUY!uÉ|,tƒ­E1Ÿx\=¹XW«R¡p +¬ãNÕÔÞŸ€nÍ ™aP‰ù9Ü ØÕ8U+sV VXâÞ›¯qï[ÂêÊV=Ç#«Õ2Qæt}觯(©ttu{{È_ßãÛ3”t¤¦Ã¤w?ÁÝ{Lyù%Ê¥ˆRá>®ãäÑMþ‡Jr8Lxçá Pq $îö=­â…zþ¨wº<ºTšÃqu}@’‚ç ÊêO¤Ø¦Ÿ×u2còw3ÖÓ‡÷*»ÐOlOMÀù3mžó´‹Rà¤÷Þ¡Ü@ëÊ%Zô;t;MêõŠž›ø„dÁŒ…kÛ*ƒs]GOI‰â8&*ÅTc—ò‡Èõ1yQáÔðµø’q¶ ǦV­Ðé´XZÖ-6ÝN‹jµBøž—¶°ŒÄÏd>P§¿祸fi‚5bÙþ…zÈžÖ²(+ŽCêõ* K‹|áãk¼³]cÔXDÈ!–´5Àn€õDß¿I*(%8æÆÖ•FØ›Á&æ¹jþù(JÚdÌúŒ¬zÜae©â8-¤jÂÓ¼÷øè‡F­ÌÂb‡Þb‡Å…6­f]†<ïT6¾R:Zó”ä7¸ ©zz=*_µù_X¤·Þ§êZš.QŽs7›”g)õ˜±{Ûnl €¯·!FGx»kq†~Ã'Ö'ì®® puµ§4M!Ëɯåg]%¼ sæ{ŸÅ6ò÷N¢0…Æ×ðBÒÇW Ö? Õ[ÐS©¼J«U§\.áûþ™ØÕYVlÍÄù]×5•%›ÀR„ÁJNÛÂɂ޶eQŠCjµ ­FV³A³Y§\Šñ}7¯¨<{ÑÄbT[Œº²—Øìm`pÜf¡LpöÉúl–gDZ|Ÿj9¦Õn²º·Çî§?àºó$•:"#°&¥Í#X¡ò‚m;(¡WîxLT• %U1`*©7ßN˜éu„ HŸÆ³&Î*-—¤ŠQ’‚íq´·ÇþÅÿJè­¼Doq2•Z?XƒšeOjmÈ×out5½dýƒe“ö>¦Z¯R-džßääٌơLõÒAòþ£[}I슼ÇÑÚ»‡?Þ'ˆW‰¢À``žÁÀާ„z}ûCnoq, [¥TÔ–cáù.ïê"Ñ1 í|‡õ$Žä‰Þp•}ZŠWü³B UÄ©Ž/$ݸ…{ïuš½^‡•åV–èv[ÔkCÂsLOÖg¿Á¦—² ID2ÂqÝ)VùÌ]=±Û*ÿϲôÉÌ*’± ½3P}¢›þ´bv´7³ ‡µ~wt„†CcŸ©"ð,,ìÇ!Z…£ÅE÷8ºñWÜùï#ÊD2ÒiªPu!}SÒö ˆ5kOm¹‰mãû>åJL{Tc0\¥ß¿Æè£o²ñÊ?ÀªÔÉ(´²}SæDè¨ÊÒQ [a§DJ®Ë4Á°²^ol¢Ãu¬‘ÙTÿ2RhRø9ÝÇã¼€þÁ!oþîÿÂèÖG¬þÜK,-vX]]byy!OýuteŸH³ò³b˜íwü?âæCL:˜…ƒB“EÓ_£\ ó¡·å²®>ºS@½Š»;CÞz0À· [šàîÜ#ŒBJea 9b¦Å);ÿnXÂõõ>ý±¢Jò_pܘ00×›EX“s}A‡e®Øg–¶‰S zÓQ×ä‰>kcÅõÃR²Céø$>Á¹ÿ&­fÞÒ"—V¹ty‰••Eº-¯&óó©n´„ÄdßNïò¨qæ!Óœx~)œ€ã'5;)ùÕª ã×ñ]%þ• ñЛ€¦Ï+ÊÊ(&ÙðZ½Âx4f4ºÂh|•ô‡ÎöÏÿgÐîAÒG`¡¬É)P l%p„4UAY¥B7Kk°]aI½ßÖÔ9›Xº3qZ†•ÓÃŒeœ×hœ Â{óúïüc’û7YyéEVz._ZæÒ¥žÖ-kÖ(•¢ÀYÿ”ÆË/P«•Íôë(ï„(Né–Já"yíöRJ®fðc¹XûëøG„‹-ÊfÎcæôìc÷TÆ¿êÞ}p„kiZH]îcYºg7ÈF:Î –ü ‡u|<Às,,•RSûX¶¸‰'÷x•òÌëIRµb9þ‰¼Ñ>ï”È®(k’¥Wp|TŸôÎë„Ã5Z¦ÂryµÇåË+\¹¼ÄR¯C³™á§³Ú¼6k§?¥¦>f )„ÄÙÒdŠˆ*ôßúîÚu¯üZE§¥ÈÜ`…A ÏÑ´ÓÒ4UŠó!œº)÷Sn¼÷U¶6îãýâoà ’Ê(eú‚ެdÖ,-tj˜št$Ý¡¼O?K2ZÃtZ¨òKfÔ†4ÏGaóñ×þ„ÿÝÿE9pXzéE–{®\^æ…++¬®ôèv[: ÷N[™Ž,õIDAT¦½%“1¿÷Ö.ÛEÉ3ÝqSoÔÑÕèÞ5¬{Òxa™f£B³Y£R)ýIt%q,Å;ú¼÷x@èhœOWûx¯D•r)'+ûSç^L­ÑAòÆí}6"ߥ$©©p=ÂÀ' %Â` E{Â*¡Á-N¤sDZNôÅÎÚÎñoÏÿ}ñÉ^LS@¸Zþ%y| ëñ»T]Ike™Å…—Vz\º¼ÄåÕ%z½ŽybÅF^åówV“°ê¼Ýÿ\£ª 8åx#@¦Xq…£«o“¼þVz´ZuÚíz•¸ã{~Gó|oÖ ®{ }ã@ôÁ³خǧŸ¼Ãã»$_þMÂÅK(9F& ÒØB;®¬QÚ¶4K njW:ÌxŽSÌ,ªÊˆ¤Y*˜&)ÊvQ…­»·øàßýßl¼ù*í… ½.Ë‹._^âÒ¥%V—év[Ôjeâ0ÄqÜ™‘j†“&RâZ’?úpoÝRòL/»€ žUIÅà¯QlšÍIgD¹œ©xØ9~•Um™ò­k{Œ?«zX·ð7(]Z¦R)Q)—#_ÍJ]•R Fc^»¹§+°:j×Ó3 }]!4*¸¯ž]‹iͪf‚W§oè´–6Ÿ Bî­!¾O0\§V¯Òê´µ”ØW–s, R.™Êš«~nvÂü)ȩ䟗ãRǾÇ~)JXØa‰£OÞdðWH§Y§kxBÝn‹z½JG¦êúüé·¼}ÃqƒÉkšfbãùþÕÜýö°³úe¢/ÿ'„µ&‰#“1©¶¥‹#©Tf‚ÙFF³/Éœ¡&´†L /•lGôwv¸þÕ?áæ_þ1ΨÏê‹—évš,÷º¬¬.ri¥ÇòR—V»iÖ\o/î/èHÎp}­Ï?kß™4µOcW)–Ñÿø ¬ûÑ|ùEÚÍ­VƒzMŸ³iö¼È[qîmøÎÍC‡L4%À}ðgS.—©UË”³¾U÷䚥TØ(®oôyÿ‘I‘tä²tõ; óë8¥NqXêÔ«öxæ~üæâb¿?;FJçÄ]š9)aë2º’¤{aóîá#J‘G}u9—WY^ê²¼¼ÈR¯«É¡åÒ„÷¹àVgÙÔ11;¯8ÆxÏÈçjM¢+%A ØX~„”’½ï•ñ›_¥Õn°´¼ÈÊR—å%]ͪÕt5ëÇqŒu"q E”ŒïyzÞd|ûwnþÍ›?bÿÊ—)ý¯·@(T:&M¶gòÀ*×%ÏÓÂã)až ª<õÁvÀóÁ²9X[ãößü{î|ç/­ß£ÑnÓ^X¥Û®³Ô˦-°¸Ø¦ÕjP©ÄšÈlðU˲r't\õ@IE’&üî;<>’9Ð~"º²,ÒÑ€Á›_§U-›VŸ&­fjµdÒÎiö|šjÿ«íp{7¡â ­‡o;X‡Ûx›Ÿ׫ԫ%jµr¸;Î4v©ÐŽÛEòæí=6Æ:TGTÕ>¸.QGZúyà'VvcLÕüå³÷ãå÷Ïb3£1oZ6ºÅÇB©ȃÇX{÷ñFÛDKµ×¡Ñ¬Óiê†^¯C¯×a¡Û¢ÕÒí ™¼ÊY‚gÏÓ„(j0eefÎ a³ôFß0…Þ±g¿ÂIé>/ÛÇÅrlähÄÑõw¼ûœõ,,vµüòjK—2€Ø0±ýÙ)ÁçaÙÍí8‚0ÐzVI Ÿ8Ð"x÷ïÞçþõ¿aó£°Õ{‰è¥¿Cyõ%‚Z]¯”D¦)ÈÒ4wX“=24žc›©O£¿·Ëæïóðíï±öî댷S­×YyåeZNƒÞb‡^¯Ëâb›N;Ó,‹L¨Íy ëR)!yûá¯ÞPò„™*~lÔ¼La‰ÃïýÞö=Ú_|™n»I§3ùLßàFÅÈÍFò`»ÏŸ}°‡ïh¶;J‚íc?ú˜@¨ÔzÔkjÕ²‰ÒÜ“¸°qèýQ«7öY:(wðÔ××°( ñÍÈ9k† Åì”ð‰î„‚£ ,Ýð\x PJä“€³3¯²“ÇÚÓß+%µ°˜<„Ñ!ô·°‡;X£=|+%ŠBJµZ™f£F»•É«´èv›´[ ›—âI÷ù`ן7N‡cIX(Óè-òÁkæXqüx˜›ÂöŽ‹‹eXäFØìaCíL)ôS- †°Ã’ÝuÆo3¾õ¬ß¢RŠh¾p™n§ÅÊr—K—4>¸¸ÐΣ+÷”òÏË&˜–s,ÊÒÀn©Q­–i¶[fëê›l†5ìöAï å•+ø•^©‚FO¡ø\8¬¤?`°³Åpo‡í[×Ùºþ1{·¯3x|ÇRTk5ê_z…z­LÓŒ[è¶Yè¶ètš4U*µ qäÀ,iáã&¥ÂV)W7ŒSu’Õ®$Âõ=¸Éø¯Òëvè´,,´è´Ôje¢0Ìåd2KS‰'þÁŸîŒ©ø–No-xwDT*Q«–©7ª9K>;÷Óë”8Bqcý÷á›t°­v@ˆœ e²ÏölšÑ€îgZ °,Ôøˆñöí|pƒe‰¼¬9Å*€°ùzÌ)dª°Jr„#l9Â)žcãGA«¥ÅòÊ1µj™F½J³U§ÝjÐn×i6êÔëº/*ÊÇ!M+ˆ~^¦Kâ‚T ~ùR‰ƒÝv·¶XÛØfoï€ÁpH’øÉȬ<«~yA@ÕvJøFÉÑz†û"Líþ\r¸,ÖØ~ø€G÷ï³ýà.Gë÷qÇ#JAHåÅËÔkv/¶Y^Z`i©ËâB;ç M*¯ÏdiOµOúy`œ–îS‹âˆj¥L£^¡Õª³Ð[dsc‹õu¶]eÿÚ;l¦ éE?Ä.U°‚Ëñ°|$ÃéhHrtÀpg›ñÑ>ãýñ\‡¸TbáÅUÊå2•rD­ª«qmSœhµÚQU+”ã »q/œdrÅIšNJ8êyFÓþã¿¡âBw¡ÃbWãÍf-¯–gU½lÛ¶P<Üéó'ìêèJ)]¹έ· ú[”;—©×+Ôk•h? 袀îmüöÕmÖbß¡¤ÔÒ=„é»-—"Â(ÌÀ̪ˆžâ°N ±NM u‰SûØ;׈J:ömO³±sðlFº9eæ&E䊞Ž[Âq]|C××yn@¹T¢Z+Q«U¨×ª4êUêµ ÕL^% s²ÉÎÿx0+K€‚_{±FGìñÁGë\ÛßàÁx݃}Ã24;d•˜4ø–KÝÅõÆ%ÝMïëŧ¥ A¾ ÇqøO/‡,§ðÑ~Ÿ 8¢Tó–.á8Žž„].ÑhTétšt;:šmµ5h[2T†bÚÛ&lx ËÒׂkÄÿâ8ÔQV£Æv{—­­&Û»]vvØÛÝgŸý½}ŽŽèoÝe41§È´0!Ú8ÂÈqð"¿¹dd†uµ«‡T*%jÕ õzÕ|é¼R)çDM/Ú𤀢?Jy±”Ò `k¾ÐT ü§X¥{¯þ)îãkt^þ9Ýôßëè©ßUìëML võ•÷·¹µEWJãÇü;ïÆ%êu}üjõŠ)´LKÞ`–aë{¾öÁž-H±è©M;ÂB÷ ú¾K©ÓhÔ贔ʸiO¥ØŸÅ2YÏu©”#Æí¶%¨UË ‡CÝ?î:„Q@)Ö‹¸GQø™:äƒÓvqËÒkËšôwæ@|R­•iö‡i'ut4 ßïÓ ÇŒ†#ÆIB’$“qöæZÕWÏsµ# <‚0 õC64*|#4ÒkÛOý;B`óåêÿܽÇßlG슈´Èðæ{¤|‡N«ÊÒò"ËËݼj® ©º-­]Iûúß_çÑ~2Á®„Ã#¼ÛïÅÚÉ·š £%ã﮸-‰Ç|åÝM„P$ج°Iœaùå8¢\Šó¡Ýhæ¾^ìœGÈÒ^9´4ëU––º\¹¼ÌB·EµZ"ðý™ºÎ'Mä)J–f³É\#êåyŽ#oÆÉ›HjN?‡Fà§4!´ÔL…º'Ëu©VË úÝ¥Ÿ¥³ÿÛD›¾ïÇårD©›Á³¶-Ë2ª CŸfsH’$9·)»³^/¿Æü$D²µ©~LÛÊ÷- ÆqÂx\f4ëp¤§}Gcã¬&)¡Ä+L$«Dz®‹ã:ù5ê{.®çrÇ\ÓÖÔ:žf_,ÛÖŸ•y¹²†|ø7ƒ'=¤LISI*S’D’¦)©qRiš¢ŒnûÔv¬ÉõšÇ1“Ã-+¿Ž-M7ÕÆ§;fy&bØýµJL£Ûei˜"F½Êhœø.F•¥¥V—õýY©”M“ó4ïJ(ÅöÁ€ßûÞz®òɉýuüß'*Wh6«tÚZ­Z-å`ûÉVI’&üÙ»ë Çá»tÙ¥&wÁñrŒ/Îø[öl…ÒÌ>SkÎñv%lüt‡r$¨V«†bP§Õª‡åŸ ¢Í8 F±8áŒ,!òÑ×Y4–¼ŸdBä)€çº„i@6¯ñÜhYÔi¢ÇâÅÿ¬œEæX3ÀÓs=M+!;“HBˆé(å§ÙŠ× Ëe¤f²¯lŒ¼’JeÈ æ>È®SQ8O¢pn²cû<Š>™“ô}rµÌ¢™PŽCööH’Ïw©VÊtÚœêçôƒÉz©ð,ż¹Á‡  ¶»Ÿ|_ö©5:tZ Úí¦Ñ’‹¦(C™I©p„⣇û¼z}ȳH\b !¼(¦\1ý‡qhTT3ѴÚâŠÎÒ?þfº©”Rò˜r©BµR2@¬¦é— ‘l ìŒÓ@±¨XÜŸU´âš5>eáº@X]„ø6Ö‹ ŽÏ”Ö­ÑØö‰w<óÏüI³)çUxˆœöýäEŽ]§š?¡î<ßã¥Óyr¬ÉÅr¨ÐïHS™Kï”+º85™=N’¤φÜØå_½½EìYS@»ýà#ü‡Rjµh·ª†çX× ÓSÑÕä8¥Râ*É¿}{­£„8p©©>íd i¹”MG·ó„…V®Ó÷õlZÃ9•B”BÙnÿ>L¥¦§¶T+%-âèªÁgÓšÊ.ŒŸ›ãøÍ09OvL4Îû|ŽI¾Æœ¼ú·×f=$aY?9wnòWÏw…µiÈÁ4äàÈa<ç’®«ñG=àt¢¬+„®âY(ÖvúüãÿðÁXdòÇÐþñwŸf³F·Ó¦ÓiR¯W –êÚhô~§RáZðö;òÞ&±g‘(Áeµ†qB]ݯVtÕ42!‚³Ýì!3+…&æÊ©TJ7tw(ïSívh6t©»R)F¦Äù™+G?»wÌgw8Ï_¦engÛiÎüÇyZ&¤Xý¿ëhjE6ÇÓYK·»Ø…t5Ky-•ð[ß~ÈÕõ!Uß")íîÕWñרö–s†|»Õ R-Mp°cãÆ””¤2áÿüÞC‡)çÒ‡,§H…CÕpàªUí°<3pâ¼{cÚaåôŸ hÊò`K+ ªd@|x•z3¤ÝnÒi7iµL#ep¶~ÏÜæ6·ggY¤eY6ʶp”3á‹øÚñH?M%®­øýï­ñïR l)AI”à<¾ŽwóÄÕ:íV…‚"GÉ`WŽ3Ár©¾õÞ&ÿñ“]bÏf¤àb'Cì@óÒjµŠæj&ô<™Ãš|ì)oϺ¾=í^¥Qqè.èé3‹‹ÚÍåJ)×ÕùYÅ;æ6·Ÿ4;Žïªc•Ìã&¥&Ò¾~c—ÿíµõi…RËÁêïâ½û5סÙÒ‘Uo±cWAàcÛ'+ƒB)‡ü¯=D*Í»êY{tÇkŒ…K5u³t¥\)v1zÎéH¸*~“uî;(a#í½O«l³Ð[deyå•z‹šßQŽc#‹ñÓÃÍ™ÛÜ~Öì¬JnæÌúý1¿óÚ‡#=É&§Ø÷î_â ¶©µš,tôz]º¹NWh*Êbj›Iªp,øó­óæ"ÏB øI‚ïûT«%êJÞ!q\áô,›­‡eœ¬&±;&C”XÃuÜá#bëˆz§F·Ûauy+——¸´Òca¡Euª[î¨æ6·ŸD“¦¹ùöfŸëBWW…’H/ÄûäU¼‡Qêté¶ëô–è-´iæ3 Ø•ðsò>ã$%ŒµÈ_½^5mAa.NðKó8úÄW^êòOþÛ¿ÇÚ£Çܹ}‡Ç³·»C’ŒqÝ(nS­Vh5kt»-–zI#e«N¥›ivN”›ÛÜæö“iãTØð_}Ñ⟿9f/±(oß ¾ö°â€fKßã+Ë Z>¨‘õO«JèTPó¸¾ýþÿú5J¾ÅPÚü‚ýˆh°GâxT+1zMöåøÔ)@g™£Ì:j7ÊüÚ/®òIbÉ>+8hTQ@à{”K‘ix¬Óî4è´5y¬V¯P6’2O’ÎmnsûüMå|>$Š—k‚ÿúò.ï]»ËæÖmJ.^°ÀB§ÉêJÏLnL ©(¦‚Riýûµí#~ë›wQh ½mòRr‘„r) VÓ]0•J™(È|Å“aÜz¿cãz.~Òj5'ŠJ¥Âh4BÝ [*•4`fF9Uè¼î²öLoÛóA7·¹ÍíéL&¾@+¢Ç£‡¬4bœtúpD…t»-V–ètZ“ÃÎIÜJJ…+Rþé·ïpm­O%°I•âïŠÛ0àzµJ‰f³¦…þJ¾ï?v•™qXàØaèS¯WEö¤Iš÷*EQH…ÄqH‡FÏÇËûÏæ‘ÕÜæöÓczø¬C©ÒlÖHÓ”¨“¦)qÒlÖi·êZ-§*ÿ™%©Äµá/ÞYãßÙ Xô¥Å/9©6ècÓ,G4š5šå©Ù‡OÎ p´·´Œš€ªèØr¹”Sû3q7Ï3Ô~Oë¹?…Ò"s›ÛÜ&ìxÏõ(Å1²¥ð<—f³ŽRŠ ðrò’ÿuü>OS‰mÁÝõ~û[wq •MÏ>à…ä.ý1Ä¥€F½:™}XŠÌ€’ÏÜä–VSpB« ÄQ¨E»Ð Ö Y—Ÿfi‘¹Íío³e*(Yc´eYA@’$€–Cò}Ϩ÷žÔ™—Rj„Lø­oÜæÞö’o#TÊßå6ãáÇ÷©Õ´f~«©‹r¡ÿvš¢èy–cX@îˆÇFJ—¬EgZ&cšÞ?·¹Íí§Ó´4…0zu¾ïåÊ·™Dùd ô4n•J…k+þÙ7î𵶨ùGÒâל»Dý-úØ4Ê­–žfU­eÃu³èê³­9'ŽK”–e'ôEò÷Ímnsûé·I "°,7ï=#5#8Qh ƒëþøõüîwPñ-ޤÍe{‡Õñ=ŽR4.Ö¨Ñ6³+å åéi²²L÷¹CšÛÜþöØi`Ç“Ö×|ÿê&ÿÓ×o8‚±²hÙ}~IÝd0áõZE‹!4ëfîa05û³Ú*ŽÎmnsûYµ³É$ „›÷øïÿì:£±Âq,R~]ÜD =¯ ÓÖ3Bµºi4³ÂøYlNššÛÜæv®)¥°-ØÞïóßýéuìŒðqÿºs ·¿ÉXØTÊ1­V]O³nÖ©”ã¼_ðY ‡™;¬¹ÍmngšR ©`<ó?|å&oß= ì[ •ů:¨÷ÑOå8 Ù¬³ÐmÑn7ò1b®ë|fÃq›;¬¹ÍmngZ*Ž ÿöõG|õƒMš¡Íajñ‹Þ:—Æ÷8A45º-º™|r<{8ÅÓØÜaÍmns;Ó”y¿u0d”(vÆ_öùEîq0L z£ÊÂB›……FÍDOŽ­Z›ƒîs›ÛÜÎ4ô ÿðKen”ØÙÙãÖÙÝ’A@µVb¡Ûf¡Û¢ÙªS©”ó&égÝ®7wXs›ÛÜNµl6c’¦xü—¿pçî6ŸÞ·‰J1¾çÒjÖY\lÓn7¨U+DFµåyP¤ækns›Û©65À²â"üÈLÇ*E!ˆê­‘КÍ<|Ö6wXs›ÛÜÎ4!–Ql©Vb”L‰C…" õ¨ùJµLeÓ›Ÿßð™¹ÃšÛÜæv¦ !p›À÷åDZUJø¾K„AWŸëZÔÌÙÛs›ÛÜæ¦-éKÓ”ñ8aœ$È4¡]×1ïÖ3!‡žes‡5·¹Íí\“RšiZ)•é;Ôê-ÚIY9Öõ PYRO - Python Remote Objects
Pyro logo

Pyro: Python Remote Objects

version 3.14 (2011-05-25)

© Irmen de Jong (irmen@razorvine.net)

Pyro is an advanced and powerful Distributed Object Technology system written entirely in Python, that is designed to be very easy to use. It 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! Pyro is released under the MIT license, see the file "LICENSE".

Contents

1 Introduction
Introduces Pyro
2 Pyro Concepts
Explanation of Pyro concepts
3 Installation and Configuration
How to install Pyro, how to configure it
4 Pyro Usage
How to use Pyro and get your system running
5 Pyro Name Server
How the Name server and Pyro object naming works
6 Pyro Event Server
Event Service
7 Features and Guidelines
Rebinding, mobile code, naming, guidelines, etc.
8 Example
A full example
9 Security
Security issues and features, SSL, validators, etc.
10 Errors and Troubleshooting
Description of Pyro errors and troubleshooting tips
11 Implementation
Technical overview of the Pyro implementation, possible issues
12 Change history
Changes since the previous versions
Pyro-3.14/docs/pyromanual_print.css0000644000076500000240000000111711517657576017720 0ustar irmenstaff00000000000000body { font-family: Tahoma,helvetica,sans-serif; font-size: 10pt; } div.nav { display: none; } h1,h2,h3,h4,h5,h6,h7 { font-family: "Trebuchet MS",Tahoma,helvetica,sans-serif; color: #444; background-color: #eee; text-decoration: underline; } table,pre { border: solid #888 1px; padding: 0.2em; } table{ border: none; border-collapse: collapse; } table.noborder { border: none; } th,tr,td { padding: 0.2em; border: solid #888 1px; } table.noborder th,table.noborder tr,table.noborder td { border: none; } tt,code { font-family: "Bitstream Vera Sans Mono", monospace; }Pyro-3.14/examples/0000755000076500000240000000000011567263337014462 5ustar irmenstaff00000000000000Pyro-3.14/examples/agent2/0000755000076500000240000000000011567263337015642 5ustar irmenstaff00000000000000Pyro-3.14/examples/agent2/agent/0000755000076500000240000000000011567263337016740 5ustar irmenstaff00000000000000Pyro-3.14/examples/agent2/agent/__init__.py0000644000076500000240000000002211517657577021053 0ustar irmenstaff00000000000000## just a package Pyro-3.14/examples/agent2/agent/ShoppingAgent.py0000644000076500000240000000304411517657577022071 0ustar irmenstaff00000000000000 # this module contains the ShoppingAgent code class ShoppingAgent(object): def __init__(self, name): self.name=name self.productsToBuy=[] self.productsBought={} self.boughtObjects=[] self.cash=0 self.shopsVisited=0 def shoppingList(self, products): self.productsToBuy=products def cashLimit(self,cash): self.cash=cash def visitShop(self, shop): print self.name,'is visiting',shop.name self.shopsVisited+=1 goods = shop.getStock() for p in self.productsToBuy[:]: if goods.has_key(p): price = goods[p] if price<=self.cash: self.cash-=price # buy an object from the shop object=shop.buy(self,p) # The object is "the real thing"; an instance. # store it in our pockets. We will return with # this to the client. self.boughtObjects.append(object) self.productsBought[p]=shop.name self.productsToBuy.remove(p) def result(self): print 'My name is',self.name print ' I have bought:' for p in self.productsBought.keys(): print '\t',p,'('+self.productsBought[p]+')' print ' I couldn\'t buy:', for p in self.productsToBuy: print p, print '\n Cash left:',self.cash, print 'after visiting',self.shopsVisited,'shops' def describeObjects(self): print "Now describing the objects I've bought:" for obj in self.boughtObjects: # notice that these use the actual objects # transferred from the server to the client! print ' ',obj.getName(),'-',obj.getDescription() if not self.boughtObjects: print "I've got nothing..." def __str__(self): return self.name Pyro-3.14/examples/agent2/client.py0000644000076500000240000000252611517657577017507 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os from Pyro.errors import * import Pyro.core sys.path.insert(0,os.pardir) # to find testclient.py Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code import testclient import agent.ShoppingAgent # Get a proxy with attrs mall = testclient.getproxy('ShoppingMall',True) import sys, os, string import Pyro.core try: # just to show what happens: try to supply some bogus code print 'Supplying bogus code to server... see what happens:' mall.remote_supply_code('crash','this is no python code',333) print 'Server ACCEPTED!!! You should not see this!!!' except PyroError,x: print 'remote_supply_code failed:',x print Harry = agent.ShoppingAgent.ShoppingAgent('Harry') Harry.cashLimit(500) Harry.shoppingList(['tv', 'mouse', 'bananas', 'boots', 'snowboard', 'goggles']) Joyce = agent.ShoppingAgent.ShoppingAgent('Joyce') Joyce.cashLimit(3200) Joyce.shoppingList(['bananas','mouse','computer','cd','spices','apples','boots']) try: print 'Harry goes shopping...' Harry=mall.goShopping(Harry) # note that agent returns as result value Harry.result() Harry.describeObjects() print print 'Joyce goes shopping...' Joyce=mall.goShopping(Joyce) # note that agent returns as result value Joyce.result() Joyce.describeObjects() print except Exception,x: import Pyro.util print ''.join(Pyro.util.getPyroTraceback(x)) Pyro-3.14/examples/agent2/Readme.txt0000644000076500000240000000266511517657577017621 0ustar irmenstaff00000000000000This test shows a real (2-way!) mobile 'agent' system. That is, the server doesn't have the agent code (python module) available and will download it from the client! The server is in the "serv" directory, and you can see that there is no ShoppingAgent.py module there. Only the client has it. HOW TO START: 1. start Pyro NS 2. cd into agent2/serv directory and start server.py 3. in another shell, cd into agent2 directory and start client.py As soon as the client sends a ShoppingAgent to the server, the server reports back that it doesn't have the code. The client then uploads the module to the server and the program continues just as if the module could be loaded after all. Notice that this happens IN PYRO; the server only has to enable the mobile code feature. In this case it also installs a codeValidator because the default codeValidator accepts ALL INCOMING CODE. The client doesn't have to do anything special to make this work. FURTHERMORE: the agent brings back actual objects from the server that were previously unknown on the client! The mobile code feature works both ways. The client can *download* code from the server too! What you see when the client "describes the objects she's bought" are the descriptions that the objects downloaded from the server return. (you can see those objects in the serv/objects directory. The client has no access to this module, just as the server has no access to the client's 'agent' module). Pyro-3.14/examples/agent2/serv/0000755000076500000240000000000011567263337016621 5ustar irmenstaff00000000000000Pyro-3.14/examples/agent2/serv/objects/0000755000076500000240000000000011567263337020252 5ustar irmenstaff00000000000000Pyro-3.14/examples/agent2/serv/objects/__init__.py0000644000076500000240000000003011517657577022364 0ustar irmenstaff00000000000000# to make this a module Pyro-3.14/examples/agent2/serv/objects/apples.py0000644000076500000240000000022211517657577022114 0ustar irmenstaff00000000000000 class apples(object): def __init__(self): pass def getName(self): return "apples" def getDescription(self): return "Juicy green apples" Pyro-3.14/examples/agent2/serv/objects/bananas.py0000644000076500000240000000022411517657577022235 0ustar irmenstaff00000000000000 class bananas(object): def __init__(self): pass def getName(self): return "bananas" def getDescription(self): return "six yellow bananas" Pyro-3.14/examples/agent2/serv/objects/bindings.py0000644000076500000240000000023311517657577022427 0ustar irmenstaff00000000000000 class bindings(object): def __init__(self): pass def getName(self): return "bindings" def getDescription(self): return "Flow snowboard bindings" Pyro-3.14/examples/agent2/serv/objects/boots.py0000644000076500000240000000023011517657577021755 0ustar irmenstaff00000000000000 class boots(object): def __init__(self): pass def getName(self): return "Boots" def getDescription(self): return "Burton snowboard softboots" Pyro-3.14/examples/agent2/serv/objects/cd.py0000644000076500000240000000024211517657577021220 0ustar irmenstaff00000000000000 class cd(object): def __init__(self): pass def getName(self): return "CD" def getDescription(self): return "The new album of the Red Hot Chili Peppers" Pyro-3.14/examples/agent2/serv/objects/computer.py0000644000076500000240000000023111517657577022466 0ustar irmenstaff00000000000000 class computer(object): def __init__(self): pass def getName(self): return "Computer" def getDescription(self): return "Alienware game system" Pyro-3.14/examples/agent2/serv/objects/goggles.py0000644000076500000240000000023311517657577022261 0ustar irmenstaff00000000000000 class goggles(object): def __init__(self): pass def getName(self): return "goggles" def getDescription(self): return "Yellow Oakley ski-goggles" Pyro-3.14/examples/agent2/serv/objects/mouse.py0000644000076500000240000000022411517657577021762 0ustar irmenstaff00000000000000 class mouse(object): def __init__(self): pass def getName(self): return "Mouse" def getDescription(self): return "Optical wireless mouse" Pyro-3.14/examples/agent2/serv/objects/shoes.py0000644000076500000240000000023011517657577021750 0ustar irmenstaff00000000000000 class shoes(object): def __init__(self): pass def getName(self): return "Shoes" def getDescription(self): return "Pair of brown casual shoes" Pyro-3.14/examples/agent2/serv/objects/snowboard.py0000644000076500000240000000024211517657577022630 0ustar irmenstaff00000000000000 class snowboard(object): def __init__(self): pass def getName(self): return "snowboard" def getDescription(self): return "Option 'Signature' snowboard" Pyro-3.14/examples/agent2/serv/objects/spices.py0000644000076500000240000000022411517657577022120 0ustar irmenstaff00000000000000 class spices(object): def __init__(self): pass def getName(self): return "spices" def getDescription(self): return "Various spicy spices" Pyro-3.14/examples/agent2/serv/objects/tomatoes.py0000644000076500000240000000024711517657577022472 0ustar irmenstaff00000000000000 class tomatoes(object): def __init__(self): pass def getName(self): return "tomatoes" def getDescription(self): return "four very big and very red tomatoes" Pyro-3.14/examples/agent2/serv/objects/tv.py0000644000076500000240000000021611517657577021264 0ustar irmenstaff00000000000000 class tv(object): def __init__(self): pass def getName(self): return "TV" def getDescription(self): return "Full-HD LCD television" Pyro-3.14/examples/agent2/serv/objects/wax.py0000644000076500000240000000022311517657577021430 0ustar irmenstaff00000000000000 class wax(object): def __init__(self): pass def getName(self): return "wax" def getDescription(self): return "canister of quick-ski-wax" Pyro-3.14/examples/agent2/serv/server.py0000644000076500000240000000360411517657577020514 0ustar irmenstaff00000000000000#!/usr/bin/env python # This server code is nearly the same as server.py from the agent example. # except for the path insert because it has to look 1 dir higher, # the fact that it enables PYRO_MOBILE_CODE, # and that it sets a codeValidator for our Pyro object. # NOTE that there is no ShoppingAgent module available here!!! # It will be downloaded from the client. import sys, os import Pyro.core sys.path.insert(0,os.path.join(os.pardir,os.pardir)) # to find testserver.py import testserver import shop Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code s1=shop.Shop("Fry's") s1.setStock( { 'tv':2000, 'computer':3000, 'mouse':10, 'cd': 19 } ) s2=shop.Shop("Fred's groceries") s2.setStock( { 'apples':5, 'tomatoes':9, 'bananas':4, 'spices': 3 } ) s3=shop.Shop("Rockport store") s3.setStock( { 'shoes':150, 'boots':190 } ) s4=shop.Shop("Snow world") s4.setStock( { 'snowboard':400, 'bindings': 150, 'goggles':80, 'wax':12 } ) # start the mall. We cannot start the testserver in delegation mode # directly because we have to call setCodeValidator from the core.ObjBase! # So create a subclass from ObjBase and our Mall. class MallObj(Pyro.core.ObjBase, shop.Mall): def __init__(self): Pyro.core.ObjBase.__init__(self) shop.Mall.__init__(self) mall=MallObj() mall.addShop(s1) mall.addShop(s2) mall.addShop(s3) mall.addShop(s4) def codeValidator(n,m,a): # This codevalidator only accepts ShoppingAgent uploads # and object.* downloads. # As an example, to accept all modules in the agent package: # change it to return n.startswith('agent.') if m and a: return n=='agent.ShoppingAgent' # client uploads to us else: return n.startswith("objects.") # client downloads from us # set a custom codeValidator because the default validator # will accept ALL incoming code (HAZARDOUS). mall.setCodeValidator(codeValidator) # finally, start the server. testserver.start(mall,'ShoppingMall') Pyro-3.14/examples/agent2/serv/shop.py0000644000076500000240000000173611517657577020163 0ustar irmenstaff00000000000000# The actual shop. # Shopping agents come here to fill their pockets. class Mall(object): def __init__(self): self.shops=[] def addShop(self,shop): self.shops.append(shop) def goShopping(self, shopper): for shop in self.shops: shopper.visitShop(shop) return shopper # !!! return agent to client !!! def __call__(self): return self # hack for testserver's delegation init class Shop(object): def __init__(self,name): self.name=name self.stock={} def setStock(self,stock): self.stock=stock def getStock(self): return self.stock def buy(self,shopper,product): if self.stock.has_key(product): print self.name+':',shopper,'buys',product del self.stock[product] # Create a "true" object that was bought. # The shopping agent will put this object # in his inventory, so it will travel back # to the client (also by using mobile code). exec("import objects."+product) object=eval("objects."+product+"."+product+"()") return object Pyro-3.14/examples/agent3/0000755000076500000240000000000011567263337015643 5ustar irmenstaff00000000000000Pyro-3.14/examples/agent3/agent/0000755000076500000240000000000011567263337016741 5ustar irmenstaff00000000000000Pyro-3.14/examples/agent3/agent/__init__.py0000644000076500000240000000002211517657577021054 0ustar irmenstaff00000000000000## just a package Pyro-3.14/examples/agent3/agent/ShoppingAgent.py0000644000076500000240000000051011517657577022065 0ustar irmenstaff00000000000000class ShoppingAgent(object): def __init__(self, name): self.name=name self.visited=[] def result(self): print 'My name is',self.name print 'I have visited',self.visited def visit(self, name): self.visited.append(name) def __str__(self): return self.name Pyro-3.14/examples/agent3/client.py0000644000076500000240000000136011517657577017503 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os from Pyro.errors import * import Pyro.core import Pyro.util sys.path.insert(0,os.pardir) # to find testclient.py import testclient import agent.ShoppingAgent Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code # Get a proxy with attrs mall = testclient.getproxy('Shop1',True) Harry = agent.ShoppingAgent.ShoppingAgent('Harry') Joyce = agent.ShoppingAgent.ShoppingAgent('Joyce') try: print 'Harry goes shopping...' Harry=mall.goShopping(Harry) # note that agent returns as result value Harry.result() print print 'Joyce goes shopping...' Joyce=mall.goShopping(Joyce) # note that agent returns as result value Joyce.result() print except Exception,x: print ''.join(Pyro.util.getPyroTraceback(x)) Pyro-3.14/examples/agent3/Readme.txt0000644000076500000240000000161211517657577017611 0ustar irmenstaff00000000000000This test shows another mobile 'agent' system, where the agent travels from server to server (three, in this example). The anwser in the end travels back from 3 to 2 to 1 and is then returned to the client. ("true" agent travel, where the answer would be returned from the final destination of the agent directly back to the calling client, is not yet available in Pyro). This test shows that Pyro can send along the agent's module bytecode. It used to load it from the module's source file, but that is not possible if you're passing the agent on to other servers (the dynamically loaded bytecode no longer has a file associated). Also see 'agent2' for a more thorough mobile code example and explanation. ** running this example ** 1. cd into the serv/ directory 2. start the three servers (separate processes) 3. cd back into the main directory 4. start the client program and observe the result. Pyro-3.14/examples/agent3/serv/0000755000076500000240000000000011567263337016622 5ustar irmenstaff00000000000000Pyro-3.14/examples/agent3/serv/shop1.py0000644000076500000240000000147111517657577020241 0ustar irmenstaff00000000000000import sys, os import Pyro.core sys.path.insert(0,os.path.join(os.pardir,os.pardir)) # to find testserver.py import testserver Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code class MallObj(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def goShopping(self, shopper): print "shop1 goshopping:",shopper shopper.visit("Shop 1") try: shop2=Pyro.core.getProxyForURI("PYRONAME://Shop2") except Exception,x: print "ERROR FINDING SHOP 2!!",x else: shopper=shop2.goShopping(shopper) # hop to next shop return shopper def __call__(self): return self # hack for testserver's delegation init mall=MallObj() # finally, start the server. testserver.start(mall,'Shop1') Pyro-3.14/examples/agent3/serv/shop2.py0000644000076500000240000000147111517657577020242 0ustar irmenstaff00000000000000import sys, os import Pyro.core sys.path.insert(0,os.path.join(os.pardir,os.pardir)) # to find testserver.py import testserver Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code class MallObj(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def goShopping(self, shopper): print "shop2 goshopping:",shopper shopper.visit("Shop 2") try: shop2=Pyro.core.getProxyForURI("PYRONAME://Shop3") except Exception,x: print "ERROR FINDING SHOP 3!!",x else: shopper=shop2.goShopping(shopper) # hop to next shop return shopper def __call__(self): return self # hack for testserver's delegation init mall=MallObj() # finally, start the server. testserver.start(mall,'Shop2') Pyro-3.14/examples/agent3/serv/shop3.py0000644000076500000240000000112511517657577020237 0ustar irmenstaff00000000000000import sys, os import Pyro.core sys.path.insert(0,os.path.join(os.pardir,os.pardir)) # to find testserver.py import testserver Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code class MallObj(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def goShopping(self, shopper): print "shop3 goshopping:",shopper shopper.visit("Shop 3") return shopper def __call__(self): return self # hack for testserver's delegation init mall=MallObj() # finally, start the server. testserver.start(mall,'Shop3') Pyro-3.14/examples/AllInOne/0000755000076500000240000000000011567263337016123 5ustar irmenstaff00000000000000Pyro-3.14/examples/AllInOne/allinone_ownloop.py0000644000076500000240000001322011517657600022044 0ustar irmenstaff00000000000000#!/usr/bin/env python # # This application creates a Name Server, Event Server, # Pyro server, and clients, and uses a custom event loop to keep them # all running in parallel. # The custom loop runs in its own server thread otherwise we # can't run client invocations, obviously. # The main loop calls Pyro objects to set some artificial # properties. Those objects publish those events on a ES channel, # on which an event listener is subscribed. That listener prints # the events that it receives. # import time import random import string import Pyro.naming import Pyro.EventService.Server from Pyro.EventService.Clients import Publisher, Subscriber from Pyro.errors import * import Pyro.util import select from threading import Thread ####################### EVENT SERVER LISTENER & PUBLISHER ################# class PropertyChangePublisher(Pyro.core.ObjBase, Publisher): def __init__(self, name): Pyro.core.ObjBase.__init__(self) Publisher.__init__(self) self.name=name def setProperty(self, property, value): print self.name,"sets",property,"to",value self.publish(self.name+"."+property, value) class PropertyChangeListener(Subscriber): def __init__(self): Subscriber.__init__(self) def event(self,event): # event.msg, subject, time print "Listener got Event: %s=%s"%(event.subject, event.msg) ################ Multi-purpose monolithic server. ##################### # handles all socket events from NS, ES, Pyro daemon. class Server(Thread): def __init__(self): Thread.__init__(self) self.setDaemon(1) self.ns_starter=None self.ns_sockets=[] self.es_starter=None self.es_sockets=[] self.pdaemon=None # daemon sockets are dynamic... self.listener=None self.listener_sockets=[] def setNameServerStarter(self, starter): starter.waitUntilStarted() self.ns_starter=starter self.ns_sockets=starter.getServerSockets() def setEventServerStarter(self, starter): starter.waitUntilStarted() self.es_starter=starter self.es_sockets=starter.getServerSockets() def setPyroDaemon(self, pdaemon): self.pdaemon=pdaemon def setEventListener(self, listener): self.listener=listener self.listener_sockets=listener.getDaemon().getServerSockets() def run(self): Pyro.core.initServer() while 1: all_sockets = self.ns_sockets + self.es_sockets + self.listener_sockets daemon_sockets=[] if self.pdaemon: # daemon sockets are dynamic. daemon_sockets = self.pdaemon.getServerSockets() all_sockets.extend(daemon_sockets) ################### CUSTOM EVENT LOOP #################### if all_sockets: ins,outs,exs=select.select(all_sockets,[],[],1) else: # windows doesn't like empty select. Just wait a while. time.sleep(1) continue ########################################################## # check for Name Server sockets... for ns_sock in self.ns_sockets: if ns_sock in ins: self.ns_starter.handleRequests(timeout=0) break # check for Event Server sockets.... for es_sock in self.es_sockets: if es_sock in ins: self.es_starter.handleRequests(timeout=0) break # check for Daemon Server sockets.... for d_sock in daemon_sockets: if d_sock in ins: self.pdaemon.handleRequests(timeout=0) break # check for Event listener sockets... for l_sock in self.listener_sockets: if l_sock in ins: self.listener.getDaemon().handleRequests(timeout=0) break ############################# MAIN LOOP ############################# def main(): if not Pyro.config.PYRO_MULTITHREADED: print "Sorry, this example requires multithreading." print "Either your Python doesn't support it or it has been disabled in the config." return Pyro.core.initClient() server = Server() server.start() # We are starting the different servers in a separate thread (here), # otherwise the custom server thread cannot handle the concurrent # invocations (for instance, the ES needs the NS when it starts...) print "STARTING NAME SERVER" starter = Pyro.naming.NameServerStarter() # no special identification starter.initialize() server.setNameServerStarter(starter) print "NAME SERVER STARTED ON PORT",starter.daemon.port print "STARTING EVENT SERVER" starter = Pyro.EventService.Server.EventServiceStarter() # no special identification # use port autoselect es_port=0 starter.initialize(port=es_port, norange=(es_port==0)) server.setEventServerStarter(starter) print "EVENT SERVER STARTED ON PORT",starter.daemon.port print "CREATING PYRO SERVER OBJECTS AND PYRO DAEMON" # use port autoselect port=0 daemon = Pyro.core.Daemon(port=port, norange=(port==0)) daemon.useNameServer(Pyro.naming.NameServerLocator().getNS()) daemon.connect(PropertyChangePublisher("publisher1"), "publisher1") daemon.connect(PropertyChangePublisher("publisher2"), "publisher2") daemon.connect(PropertyChangePublisher("publisher3"), "publisher3") server.setPyroDaemon(daemon) print "PYRO SERVER ACTIVATED ON PORT",daemon.port listener = PropertyChangeListener() listener.subscribeMatch("^publisher.\\..*$") server.setEventListener(listener) print "EVENT LISTENER ACTIVATED" print "ALL SERVERS WERE STARTED!" time.sleep(1) p1 = Pyro.core.getProxyForURI("PYRONAME://publisher1") p2 = Pyro.core.getProxyForURI("PYRONAME://publisher2") p3 = Pyro.core.getProxyForURI("PYRONAME://publisher3") try: while True: print "MAIN LOOP CHANGES PROPERTIES..." p1.setProperty(random.choice(string.uppercase), random.randint(0,1000)) p2.setProperty(random.choice(string.uppercase), random.randint(0,1000)) p3.setProperty(random.choice(string.uppercase), random.randint(0,1000)) time.sleep(1) except Exception,x: print "".join(Pyro.util.getPyroTraceback(x)) if __name__=="__main__": main() Pyro-3.14/examples/AllInOne/allinone_threads.py0000644000076500000240000001027411517657600022007 0ustar irmenstaff00000000000000#!/usr/bin/env python # # This application creates a Name Server, Event Server, # Pyro server, and clients, and uses threads to keep them # all running in parallel. # The main loop calls Pyro objects to set some artificial # properties. Those objects publish those events on a ES channel, # on which an event listener is subscribed. That listener prints # the events that it receives. # # Thread 1: Pyro Name Server # Thread 2: Pyro Event Server # Thread 3: our own Pyro Daemon # Thread 4: our own Event listener loop # Thread 5: Main Loop, calling our Pyro object to set properties. # import time import random import string import Pyro.naming import Pyro.EventService.Server from Pyro.EventService.Clients import Publisher, Subscriber from Pyro.errors import * import Pyro.util from threading import Thread ####################### EVENT SERVER LISTENER & PUBLISHER ################# class PropertyChangePublisher(Pyro.core.ObjBase, Publisher): def __init__(self, name): Pyro.core.ObjBase.__init__(self) Publisher.__init__(self) self.name=name def setProperty(self, property, value): print self.name,"sets",property,"to",value self.publish(self.name+"."+property, value) class PropertyChangeListener(Subscriber): def __init__(self): Subscriber.__init__(self) def event(self,event): # event.msg, subject, time print "Listener got Event: %s=%s"%(event.subject, event.msg) ####################### NS, ES, DAEMON, EVENT LISTENER THREADS ############ class NameServer(Thread): def __init__(self): Thread.__init__(self) self.setDaemon(1) self.starter = Pyro.naming.NameServerStarter() # no special identification def run(self): print "Launching Pyro Name Server" self.starter.start() def waitUntilStarted(self): return self.starter.waitUntilStarted() class EventServer(Thread): def __init__(self): Thread.__init__(self) self.setDaemon(1) self.starter = Pyro.EventService.Server.EventServiceStarter() # no special identification def run(self): print "Launching Pyro Event Server" # we're using the OS's automatic port allocation es_port=0 self.starter.start(port=es_port, norange=(es_port==0)) def waitUntilStarted(self): return self.starter.waitUntilStarted() class PyroServer(Thread): def __init__(self): Thread.__init__(self) self.setDaemon(1) self.ready=0 def run(self): Pyro.core.initServer() print "Creating Pyro server objects and Pyro Daemon" # we're using the OS's automatic port allocation port=0 daemon = Pyro.core.Daemon(port=port, norange=(port==0)) daemon.useNameServer(Pyro.naming.NameServerLocator().getNS()) daemon.connect(PropertyChangePublisher("publisher1"), "publisher1") daemon.connect(PropertyChangePublisher("publisher2"), "publisher2") daemon.connect(PropertyChangePublisher("publisher3"), "publisher3") self.ready=1 daemon.requestLoop() class EventListener(Thread): def __init__(self): Thread.__init__(self) self.setDaemon(1) def run(self): Pyro.core.initServer() listener = PropertyChangeListener() listener.subscribeMatch("^publisher.\\..*$") print "EVENT LISTENER ACTIVATED" listener.listen() ############################# MAIN LOOP ############################# def main(): if not Pyro.config.PYRO_MULTITHREADED: print "Sorry, this example requires multithreading." print "Either your Python doesn't support it or it has been disabled in the config." return nss=NameServer() nss.start() nss.waitUntilStarted() # wait until the NS has fully started. ess=EventServer() ess.start() ess.waitUntilStarted() # wait until the ES has fully started. EventListener().start() server=PyroServer() server.start() while not server.ready: time.sleep(1) p1 = Pyro.core.getProxyForURI("PYRONAME://publisher1") p2 = Pyro.core.getProxyForURI("PYRONAME://publisher2") p3 = Pyro.core.getProxyForURI("PYRONAME://publisher3") try: while 1: print "MAIN LOOP CHANGES PROPERTIES..." p1.setProperty(random.choice(string.uppercase), random.randint(0,1000)) p2.setProperty(random.choice(string.uppercase), random.randint(0,1000)) p3.setProperty(random.choice(string.uppercase), random.randint(0,1000)) time.sleep(1) except Exception,x: print "".join(Pyro.util.getPyroTraceback(x)) if __name__=="__main__": main() Pyro-3.14/examples/AllInOne/Readme.txt0000644000076500000240000000205411517657600020055 0ustar irmenstaff00000000000000The purpose of this example is to show how to build a single application that starts up everything it nees from Pyro itself (NS, ES, what not). The application creates a Name Server, Event Server, Pyro server with event publisher, and an event listener. The main loop calls Pyro objects to set some artificial properties. Those objects publish those events on a ES channel, on which an event listener is subscribed. That listener prints the events that it receives. allinone_threads.py: uses THREADS to run everything concurrently. allinone_ownloop.py: uses a CUSTOM EVENT LOOP for everything. NOTE: no Name Server must be running. NOTE2: this example doesn't show what a good solution might be to run everything in a monolithic application!!! For instance, if you want publish-subscribe notifications inside your own application, and it is not distributed, why even use Pyro's Event Server? Just build your own 'event' dispatch object as a regular Python class and everything will be much easier. You only *need* Pyro's ES if you want it to be distributed... Pyro-3.14/examples/attributes/0000755000076500000240000000000011567263337016650 5ustar irmenstaff00000000000000Pyro-3.14/examples/attributes/client.py0000644000076500000240000000176111517657600020500 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os import Pyro.util sys.path.insert(0,os.pardir) # to find testclient.py import testclient # Get a proxy with attrs test = testclient.getproxy('attributes',True) name = raw_input('Enter a name: ') # new way of doing things: # direct attribute access! print 'current sum=',test.sum print 'last changed by',test.changedby test.sum = test.sum+1 test.changedby = name print 'new sum=',test.sum # creating some new attributes test.newattr1 = 'new_one' test.newattr2 = 'new_two' print 'new attr 1=',test.newattr1 print 'new attr 2=',test.newattr2 print 'Getting nested attribute from person object' print 'name=',test.person.name print ' age=',test.person.age print 'Getting nested attrs using methods from person object' print 'name=',test.person.getName() print ' age=',test.person.getAge() print '(the next attribute access should raise an exception)' try: print 'not existing=',test.notexisting except Exception,x: print "".join(Pyro.util.getPyroTraceback(x)) Pyro-3.14/examples/attributes/Person.py0000644000076500000240000000031111517657600020456 0ustar irmenstaff00000000000000####### Special object class Person(object): def __init__(self, name, age): self.name=name self.age=age def getName(self): return 'My name is '+self.name def getAge(self): return self.age Pyro-3.14/examples/attributes/Readme.txt0000644000076500000240000000101111517657600020572 0ustar irmenstaff00000000000000This test shows the possibility of direct attribute access. Notice that the client no longer uses getter and setter methods, it directly accesses the object's attributes by using normal Python syntax. Note that the nested attributes from the person object can only work because the Person.py module (that contains the Person class) is available to the client !!! If the Person.py wasn't in this directory (and hence, not available to the client) the client will crash with an error such as "No module named Person") Pyro-3.14/examples/attributes/server.py0000644000076500000240000000152211517657600020523 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os import Pyro.core from Person import Person sys.path.insert(0,os.pardir) # to find testserver.py import testserver ######## testclass object (subclassed from ObjBase) class testclass(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.sum=0 self.changedby='' # the following only works because Person is in a separate module # that is also available to the client: self.person=Person("Irmen de Jong","30") ######## testclass object (standalone - delegate approach) class testclass2(object): def __init__(self): self.sum=0 self.changedby='' # the following only works because Person is in a separate module # that is also available to the client: self.person=Person("Irmen de Jong","30") ######## main program testserver.start(testclass,'attributes') Pyro-3.14/examples/authenticate/0000755000076500000240000000000011567263337017140 5ustar irmenstaff00000000000000Pyro-3.14/examples/authenticate/client.py0000644000076500000240000000140311517657601020762 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.naming, Pyro.core import Pyro.errors Pyro.core.initClient() ident = raw_input('Enter authentication ID for NS ("s3cr3t"): ') locator = Pyro.naming.NameServerLocator(identification=ident) # note the ID print 'Searching Naming Service...', ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'port',ns.URI.port print 'binding to object' try: URI=ns.resolve(':test.authentication') print 'URI:',URI except Pyro.core.PyroError,x: print 'Couldn\'t bind object, nameserver says:',x raise SystemExit ident = raw_input('Enter authentication ID for Server ("s3cr3t"): ') obj=Pyro.core.getProxyForURI(URI) obj._setIdentification(ident) result=obj.method('foo bar') print "Result from method call: ",result Pyro-3.14/examples/authenticate/NSSecEx.py0000644000076500000240000000146011517657601020757 0ustar irmenstaff00000000000000#!/usr/bin/env python # Name Server security plugins. ACCEPTED_ID = 's3cr3t' #----- required global funcs that return validator objects ------ def BCGuard(): return None # no special broadcast server guard def NSGuard(): v=NSnewConnValidator() v.setAllowedIdentifications([ACCEPTED_ID]) return v #----- validator object implementation -------- import Pyro.protocol # NS Pyro Daemon newConnValidator class NSnewConnValidator(Pyro.protocol.DefaultConnValidator): def acceptIdentification(self, tcpserver, conn, hash, challenge): print conn.addr[0],'SENDS IDENTIFICATION...' (ok,reason)=Pyro.protocol.DefaultConnValidator.acceptIdentification(self, tcpserver, conn, hash, challenge) if not ok: print 'Connection denied! Make sure the identification is "'+ACCEPTED_ID+'"' return (ok,reason) Pyro-3.14/examples/authenticate/Readme.txt0000644000076500000240000000233211517657601021072 0ustar irmenstaff00000000000000Example of connection Authentication. FIRST: Starting the NS with authentication enabled You need to use authentication on the Name Server too:-- Either start 'ns' with a custom authentication ID option ("s3cr3t"), or use a custom security plugin (supplied). To use the security plugin, which prints the authentication process, make sure the current directory ('.') is part of your PYTHONPATH, because otherwise the security module cannot be loaded from this directory (this is correct -safe- behavior of Python). Start the nameserver with: "ns -v -s NSSecEx" It will show you that it's using your security plugins. (If you don't use the -v flag, you won't see this on the console) Using a custom connection validator enables you to have more control over the authentication process, and to specify multiple allowed authentication IDs. Running the server and client: The server tells Pyro that it wants to use a limited set of allowed authentication IDs. Pyro will now refuse any new client connection that does not supply one of the allowed IDs. The client asks for the ID. You can deliberately enter a wrong ID to see what happens. (first id is for connecting to the Name Server, second is for connecting to the test server). Pyro-3.14/examples/authenticate/server.py0000644000076500000240000000153111517657601021014 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.naming import Pyro.core from Pyro.errors import PyroError,NamingError class testobject(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self,arg): print 'Method called with',arg return 'An interesting result' # initialize the server Pyro.core.initServer() # locate the NS print 'Searching Naming Service...' locator = Pyro.naming.NameServerLocator(identification='s3cr3t') # note the ident string ns = locator.getNS() try: ns.createGroup(":test") except NamingError: pass daemon = Pyro.core.Daemon() daemon.useNameServer(ns) daemon.setAllowedIdentifications(['s3cr3t']) # connect new instance, but using persistent mode daemon.connectPersistent(testobject(),':test.authentication') # enter the service loop. print 'Server started.' daemon.requestLoop() Pyro-3.14/examples/autoreconnect/0000755000076500000240000000000011567263337017333 5ustar irmenstaff00000000000000Pyro-3.14/examples/autoreconnect/client.py0000644000076500000240000000116211566312345021154 0ustar irmenstaff00000000000000# Autoreconnect client # uses the Name Server import time import Pyro.naming, Pyro.core locator = Pyro.naming.NameServerLocator() print 'Searching Naming Service...', ns = locator.getNS() URI=ns.resolve(':test.autoreconnect') obj = Pyro.core.getAttrProxyForURI(URI) while True: print 'call...' try: obj.method(42) print 'Sleeping 1 second' time.sleep(1) #obj._release() # experiment with this #print 'released' #time.sleep(2) except Pyro.errors.ConnectionClosedError,x: # or possibly even ProtocolError print 'Connection lost. REBINDING...' print '(restart the server now)' obj.adapter.rebindURI() Pyro-3.14/examples/autoreconnect/client_no_ns.py0000644000076500000240000000064511566312345022355 0ustar irmenstaff00000000000000# Autoreconnect client # doesn't use the Name Server import time import Pyro.core import Pyro.errors obj = Pyro.core.getProxyForURI("PYROLOC://localhost/:test.autoreconnect") while True: print 'call...' try: obj.method(42) print 'Sleeping 1 second' time.sleep(1) except Pyro.errors.ConnectionClosedError,x: print 'Connection lost. REBINDING...' print '(restart the server now)' obj.adapter.rebindURI() Pyro-3.14/examples/autoreconnect/Readme.txt0000644000076500000240000000315211566312345021263 0ustar irmenstaff00000000000000This is an example that shows the auto reconnect feature. Start the server and the client. You can stop the server while it's running. The client will report that the connection is lost, and that it is trying to rebind. Start the server again. You'll see that the client continues. The server_no_ns.py and client_no_ns.py show how to do this without a name server. NOTES: 1- your server has to be prepared for this feature. It must not rely on any transient internal state to function correctly, because that state is lost when your server is restarted. You could make the state persistent on disk and read it back in at restart. 2- your server MUST register the objects with the new connectPersistent method of the PyroDaemon. Otherwise the reconnect feature DOES NOT WORK because the client tries to access an invalid URI. 3- the NS does NOT have to be the persistent version on disk, as long as it keeps running. All that is needed is that the URI of the objects concerned stay available in the NS, so the server can reuse those URIs when it gets back up. 4- the client is responsible for detecting a network problem itself. It must call the 'hidden' rebindURI() method of the object's protocol adapter itself if this is the case. 5- Examine the example code provided here, and Pyro's internal adapter code, to see how the rebind feature works. 6- Why isn't this transparent???? Because you HAVE to have control about it when a network problem occurs. Furthermore, only YOU can decide if your system needs this feature, and if your system can SUPPORT this feature (see point 1 and 2 above). Pyro-3.14/examples/autoreconnect/server.py0000644000076500000240000000135711566312345021212 0ustar irmenstaff00000000000000# Autoreconnect server # uses the Name Server import time import Pyro.naming import Pyro.core from Pyro.errors import NamingError class testobject(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self,arg): print 'Method called with',arg print 'You can now try to stop this server with ctrl-C' time.sleep(1) print 'Searching Naming Service...' daemon = Pyro.core.Daemon() locator = Pyro.naming.NameServerLocator() ns = locator.getNS() try: ns.createGroup(":test") except NamingError: pass daemon.useNameServer(ns) # connect new instance, but using persistent mode daemon.connectPersistent(testobject(),':test.autoreconnect') # enter the service loop. print 'Server started.' daemon.requestLoop() Pyro-3.14/examples/autoreconnect/server_no_ns.py0000644000076500000240000000075411566312345022406 0ustar irmenstaff00000000000000# Autoreconnect server # doesn't use the Name Server import time, Pyro.core class testobject(Pyro.core.ObjBase): def method(self,arg): print 'You can now try to stop this server with ctrl-C' time.sleep(1) daemon = Pyro.core.Daemon() object=testobject() fixed_guid="a9fe57de0a3862c9b31b925b97a98d8a" # use some fixed random GUID object.setGUID(fixed_guid) daemon.connect(object,':test.autoreconnect') print 'Fixed object guid =',fixed_guid print 'Server started.' daemon.requestLoop() Pyro-3.14/examples/Bank2/0000755000076500000240000000000011567263337015417 5ustar irmenstaff00000000000000Pyro-3.14/examples/Bank2/banks.py0000644000076500000240000000550311517657602017067 0ustar irmenstaff00000000000000# Bank and accounts code. # NOTE that the Account and Bank classes are directly derived # from the Pyro.core.ObjBase base class. This is required to # support the 1.2+ attribute access feature. # Note that an account object is not returned as-is, # rather, a PROXY for it is returned. # Also note that the proxy must support attribute access, so # we use getAttrProxy(). import Pyro.core # the bank uses this exception to say there's something wrong: class BankError(Exception): pass # Unrestricted account. class Account(Pyro.core.ObjBase): def __init__(self,name,owner): Pyro.core.ObjBase.__init__(self) self.balance=0.0 self.name=name self.bank=owner def _gotReaped(self): print 'Account reaped, sorry your cash is lost:',self.name,self.balance self.bank._gotReaped(self) def withdraw(self, amount): self.balance-=amount def deposit(self,amount): self.balance+=amount # Restricted withdrawal account. class RestrictedAccount(Account): def withdraw(s, amount): if amount<=s.balance: s.balance=s.balance-amount else: raise BankError('insufficent balance') # Abstract bank. class Bank(Pyro.core.ObjBase): def __init__(s): Pyro.core.ObjBase.__init__(s) s.accounts={} def createAccount(s, name): pass # must override this! def _gotReaped(self, account): del self.accounts[account.name] print 'Bank removed reaped account',account.name def deleteAccount(s, name): try: # find account, disconnect from daemon, delete it acc = s.accounts[name] s.getDaemon().disconnect(acc) del s.accounts[name] except KeyError: raise BankError('unknown account') def findAccount(s, name): try: # find account, return proxy for it return s.accounts[name].getAttrProxy() except KeyError: raise BankError('unknown account') def allAccounts(s): # list all accounts, return list of proxies for them accs = s.accounts.values() proxies = [] for a in accs: proxies.append(a.getAttrProxy()) return proxies # Special bank: Rabobank. It has unrestricted accounts. class Rabobank(Bank): def __init__(s): Bank.__init__(s) s.name = 'Rabobank' def createAccount(s,name): if s.accounts.has_key(name): raise BankError('Account already exists') # create account object, connect to daemon, return proxy for it acc = Account(name,s) acc_URI = s.getDaemon().connect(acc) s.accounts[name]=acc print 'created account',name return acc.getAttrProxy() # Special bank: VSB. It has restricted accounts. class VSB(Bank): def __init__(s): Bank.__init__(s) s.name = 'VSB bank' def createAccount(s,name): if s.accounts.has_key(name): raise BankError('Account already exists') # create account object, connect to daemon, return proxy for it acc = RestrictedAccount(name,s) acc_URI = s.getDaemon().connect(acc) s.accounts[name]=acc print 'created account',name return acc.getAttrProxy() Pyro-3.14/examples/Bank2/BankServer.py0000644000076500000240000000212111517657602020024 0ustar irmenstaff00000000000000#!/usr/bin/env python # # The bank server. # import sys import Pyro.naming import Pyro.core from Pyro.errors import PyroError,NamingError import banks group = ':banks' # the namespace group for all test servers # initialize the server and set the default namespace group Pyro.core.initServer() Pyro.config.PYRO_NS_DEFAULTGROUP=group # locate the NS print 'Searching Naming Service...' locator = Pyro.naming.NameServerLocator() ns = locator.getNS() # make sure our namespace group exists try: ns.createGroup(group) except NamingError: pass daemon = Pyro.core.Daemon() daemon.useNameServer(ns) cleanupAge=20 daemon.setTransientsCleanupAge(cleanupAge) print '>>>The maximum account inactivity age is',cleanupAge,'seconds<<<' # connect a new object implementation (first unregister previous one) try: ns.unregister('Rabobank') ns.unregister('VSB') except NamingError: pass # bank class is direct subclass of Pyro.core.ObjBase daemon.connect(banks.Rabobank(),'Rabobank') daemon.connect(banks.VSB(),'VSB') # enter the service loop. print 'Banks are ready for customers.' daemon.requestLoop() Pyro-3.14/examples/Bank2/client.py0000644000076500000240000000712311517657602017247 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Bank client. # # The client searches the two banks and performs a set of operations. # (the banks are searched simply by listing the :banks namespace!) # import sys import Pyro.naming, Pyro.core from banks import BankError group = ':banks' # the default namespace group # initialize the client and set the default namespace group Pyro.core.initClient() Pyro.config.PYRO_NS_DEFAULTGROUP=group # locate the NS locator = Pyro.naming.NameServerLocator() print 'Searching Naming Service...', ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port # List the banks. # This is done by simply looking in the :banks namespace, to see what # banks have registered. The filter is for removing any groups that could # be in the namespace (the type of real names is 1). banknames = filter(lambda x: x[1]==1, ns.list(group)) banknames = map(lambda (x,y): x, banknames) # keep only the object name if not banknames: raise RuntimeError('There are no banks to do business with!') banks={} # banks (proxies) print for name in banknames: print 'Found a bank: ',name try: URI=ns.resolve(name) except Pyro.core.PyroError,x: print 'Bank can\'t be found:',x raise SystemExit # create a proxy for the bank object banks[name] = Pyro.core.getAttrProxyForURI(URI) def selectBank(): i = 1 banknames=banks.keys() for b in banknames: print i," ",b i=i+1 b = input("Select a bank: ") return banks[banknames[b-1]] def createAccount(): print "\nCreate Account." bank = selectBank() name = raw_input("Enter name: ") a = bank.createAccount(name) amount = input("Initial deposit: ") a.deposit(amount) print "Balance:", a.balance def removeAccount(): print "\nRemove Account." bank = selectBank() name = raw_input("Enter name: ") bank.deleteAccount(name) def viewBalance(): print "\nView Balance." bank = selectBank() name = raw_input("Enter name: ") ac = bank.findAccount(name) print ac.balance def deposit(): print "\nDeposit." bank = selectBank() name = raw_input("Enter name: ") ac = bank.findAccount(name) amount = input("Amount: ") ac.deposit(amount) print "New balance:", ac.balance def withdraw(): print "\nWithdraw." bank = selectBank() name = raw_input("Enter name: ") ac = bank.findAccount(name) amount = input("Amount: ") ac.withdraw(amount) print "New balance:", ac.balance def listAll(): print "\nList all accounts." for (bankname,bank) in banks.items(): print bank.name accs = bank.allAccounts() if not accs: print " No accounts." for a in accs: print " ",a.name, a.balance going = 1 while going: print "\n---- menu ----" print "1: create account" print "2: remove account" print "3: view balance" print "4: list all accounts" print "5: deposit money" print "6: withdraw money" print "0: exit" print try: choice = input("Choice: ") if choice==0: going=0 elif choice==1: createAccount() elif choice==2: removeAccount() elif choice==3: viewBalance() elif choice==4: listAll() elif choice==5: deposit() elif choice==6: withdraw() except SyntaxError,x: print "Input problem:",x except BankError,x: print 'Problem:',x except StandardError,x: print 'Try again (input incorrect?)' raise Pyro-3.14/examples/Bank2/Readme.txt0000644000076500000240000000274011517657602017355 0ustar irmenstaff00000000000000This is a simple electronic banking example. It looks a lot like the other banking example (BankExample) but it has some important differences: 1. It uses the Pyro 1.2+ feature to directly access object attributes; 2. It uses the Pyro 1.2+ feature to create new objects on the server and to pass proxies to those objects back to the client. The creation of accounts is realised this way. Clients get a real account object on which they can call deposit and withdraw methods. 3. The client is interactive. 4. It uses the Pyro 3.0 'transient object timeout reaping' feature. There are two banks:- Rabobank and VSBBank (don't ask - I'm from Holland) Their services are started with BankServer.py. The client starts an interactive loop in which you can -create accounts -delete accounts -deposit money -withdraw money -inquire balance The VSBBank will not allow you to overdraw and have a negative balance, the RaboBank will. The bank has a maximum account inactivity time (say 20 seconds). If an account has not been accessed for that time, it will be reaped on the server. The transient account object will be deleted after it has notified the bank that it must be removed. See the server console for messages indicating that this took place. Currently the only thing lacking that would make this really useful is persistence: if the bank offices are closed (i.e. the servers are shut down), all account information is lost. This is a nice starting point for your own Pyro projects. Pyro-3.14/examples/BankExample/0000755000076500000240000000000011567263337016651 5ustar irmenstaff00000000000000Pyro-3.14/examples/BankExample/banks.py0000644000076500000240000000345511517657602020325 0ustar irmenstaff00000000000000# the bank uses this exception to say there's something wrong: class BankError(Exception): pass # Unrestricted account. class Account(object): def __init__(s): s._balance=0.0 def withdraw(s, amount): s._balance=s._balance-amount def deposit(s,amount): s._balance=s._balance+amount def balance(s): return s._balance # Restricted withdrawal account. class RestrictedAccount(Account): def withdraw(s, amount): if amount<=s._balance: s._balance=s._balance-amount else: raise BankError('insufficent balance') # Abstract bank. class Bank(object): def __init__(s): s.accounts={} def name(s): pass # must override this! def createAccount(s, name): pass # must override this! def deleteAccount(s, name): try: del s.accounts[name] except KeyError: raise BankError('unknown account') def deposit(s, name, amount): try: return s.accounts[name].deposit(amount) except KeyError: raise BankError('unknown account') def withdraw(s, name, amount): try: return s.accounts[name].withdraw(amount) except KeyError: raise BankError('unknown account') def balance(s, name): try: return s.accounts[name].balance() except KeyError: raise BankError('unknown account') def allAccounts(s): accs = {} for name in s.accounts.keys(): accs[name] = s.accounts[name].balance() return accs # Special bank: Rabobank. It has unrestricted accounts. class Rabobank(Bank): def name(s): return 'Rabobank' def createAccount(s,name): if s.accounts.has_key(name): raise BankError('Account already exists') s.accounts[name]=Account() # Special bank: VSB. It has restricted accounts. class VSB(Bank): def name(s): return 'VSB bank' def createAccount(s,name): if s.accounts.has_key(name): raise BankError('Account already exists') s.accounts[name]=RestrictedAccount() Pyro-3.14/examples/BankExample/BankServer.py0000644000076500000240000000222311517657602021261 0ustar irmenstaff00000000000000#!/usr/bin/env python # # The banks server # import sys import Pyro.naming import Pyro.core from Pyro.errors import PyroError,NamingError import banks group = ':banks1' # the namespace group for all test servers # avoid network port trouble with the VSB bank server Pyro.config.PYRO_PORT=Pyro.config.PYRO_PORT+1 # initialize the server and set the default namespace group Pyro.core.initServer() Pyro.config.PYRO_NS_DEFAULTGROUP=group # locate the NS print 'Searching Naming Service...' daemon = Pyro.core.Daemon() locator = Pyro.naming.NameServerLocator() ns = locator.getNS() # make sure our namespace group exists try: ns.createGroup(group) except NamingError: pass daemon.useNameServer(ns) # connect a new object implementation (first unregister previous one) try: ns.unregister('Rabobank') ns.unregister('VSB') except NamingError: pass # use Delegation approach for object implementation obj1=Pyro.core.ObjBase() obj1.delegateTo(banks.Rabobank()) daemon.connect(obj1,'Rabobank') obj2=Pyro.core.ObjBase() obj2.delegateTo(banks.VSB()) daemon.connect(obj2,'VSB') # enter the service loop. print 'Banks are ready for customers.' daemon.requestLoop() Pyro-3.14/examples/BankExample/client.py0000644000076500000240000000562511517657602020506 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Bank client. # # The client searches the two banks and performs a set of operations. # (the banks are searched simply by listing the :banks namespace!) # import sys import Pyro.naming, Pyro.core from banks import BankError group = ':banks1' # the default namespace group # A bank client. class client(object): def __init__(self,name): self.name=name def doBusiness(self, bank): print print '***',self.name,'is doing business with',bank.name(),':' print 'Creating account' try: bank.createAccount(self.name) except BankError,x: print 'Failed:',x print 'Removing account and trying again' bank.deleteAccount(self.name) bank.createAccount(self.name) print 'Deposit money' bank.deposit(self.name, 200.00) print 'Deposit money' bank.deposit(self.name, 500.75) print 'Balance=', bank.balance(self.name) print 'Withdraw money' bank.withdraw(self.name, 400.00) print 'Withdraw money (red)' try: bank.withdraw(self.name, 400.00) except BankError,x: print 'Failed:',x print 'End balance=', bank.balance(self.name) print 'Withdraw money from non-existing account' try: bank.withdraw('GOD',2222.22) print '!!! Succeeded?!? That is an error' except BankError,x: print 'Failed, as expected:',x print 'Deleting non-existing account' try: bank.deleteAccount('GOD') print '!!! Succeeded?!? That is an error' except BankError,x: print 'Failed, as expected:',x # initialize the client and set the default namespace group Pyro.core.initClient() Pyro.config.PYRO_NS_DEFAULTGROUP=group # locate the NS locator = Pyro.naming.NameServerLocator() print 'Searching Naming Service...', ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port # List the banks. # This is done by simply looking in the :banks namespace, to see what # banks have registered. The filter is for removing any groups that could # be in the namespace (the type of real names is 1). banknames = filter(lambda x: x[1]==1, ns.list(group)) banknames = map(lambda (x,y): x, banknames) # keep only the object name if not banknames: raise RuntimeError('There are no banks to do business with!') banks=[] # list of banks (proxies) print for name in banknames: print 'Found a bank: ',name try: URI=ns.resolve(name) except Pyro.core.PyroError,x: print 'Bank can\'t be found:',x raise SystemExit # create a proxy for the bank object bank = Pyro.core.getProxyForURI(URI) banks.append(bank) # Different clients irmen = client('Irmen') suzy = client('Suzy') # Try the different banks for bank in banks: irmen.doBusiness(bank) suzy.doBusiness(bank) # List all accounts print for bank in banks: print 'The accounts in the',bank.name(),':' accounts = bank.allAccounts() for name in accounts.keys(): print ' ',name,':',accounts[name] # Pedantic cleanup del irmen del suzy del banks del ns Pyro-3.14/examples/BankExample/Readme.txt0000644000076500000240000000144511517657602020610 0ustar irmenstaff00000000000000This is a simple electronic banking example. There are two banks:- Rabobank and VSBBank (don't ask - I'm from Holland) Their services are started with BankServer.py. The client runs some transactions on both banks (if found), like:- -creating accounts -deleting accounts -deposit money -withdraw money -inquire balance The VSBBank will not allow the client to overdraw and have a negative balance, the RaboBank will. Currently the only thing lacking that would make this really useful is persistence: if the bank offices are closed (i.e. the servers are shut down), all account information is lost. This is a nice starting point for your own Pyro projects. See also the Bank2 example which is more advanced, and has an interactive client to create accounts, and to deposit and withdraw money. Pyro-3.14/examples/benchmark/0000755000076500000240000000000011567263337016414 5ustar irmenstaff00000000000000Pyro-3.14/examples/benchmark/bench.py0000644000076500000240000000154011517657600020040 0ustar irmenstaff00000000000000class sub1(object): def meth1(s,arg): return 'Dit is sub1.meth1!' class sub2(sub1): def meth2(s,arg): return 'Dit is sub2.meth2!' class sub3(sub2): def meth3(s,arg): return 'Dit is sub3.meth3!' class sub4(sub3,sub2): def meth4(s,arg): return 'Dit is sub4.meth4!' class bench(sub4): def length(self, string): return len(string) def timestwo(self, value): return value*2 def bigreply(self): return 'BIG REPLY'*200 def bigarg(self,arg): return len(arg) def manyargs(self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15): return a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15 def noreply(self, arg): pass def varargs(self, *args): return len(args) def keywords(self, **args): return args def echo(self, *args): return args def oneway(self, *args): # oneway doesn't return anything pass def mapping(self, mapping): return mapping Pyro-3.14/examples/benchmark/client.py0000644000076500000240000000426211566251755020250 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys,os,time import Pyro.protocol sys.path.insert(0,os.pardir) # to find testclient.py import testclient import bench object = testclient.getproxy('benchmark') object._setOneway('oneway') def f1(): void=object.length('Irmen de Jong') def f2(): void=object.timestwo(21) def f3(): void=object.bigreply() def f4(): void=object.manyargs(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) def f5(): void=object.noreply(99993333) def f6(): void=object.varargs('een',2,(3,),[4]) def f7(): void=object.keywords(arg1='zork') def f8(): void=object.echo('een',2,(3,),[4]) def f9(): void=object.meth1('stringetje') def fa(): void=object.meth2('stringetje') def fb(): void=object.meth3('stringetje') def fc(): void=object.meth4('stringetje') def fd(): void=object.bigarg('Argument'*50) def fe(): void=object.oneway('stringetje',432423434) def ff(): void=object.mapping( {"aap":42, "noot": 99, "mies": 987654} ) funcs = (f1,f2,f3,f4,f5,f6,f7,f8,f9,fa,fb,fc,fd,fe,ff) print '-------- BENCHMARK REMOTE OBJECT ---------' print 'Pay attention to the "fe" test -- this is a Oneway call and is *fast*' print '(if you are running the server and client on different machines)' begin = time.time() iters = 1000 for f in funcs: print iters,'times',f.__name__, sys.stdout.flush() voor = time.time() for i in range(iters): f() print '%.4f' % (time.time()-voor) duration = time.time()-begin print 'total time %.4f seconds' % duration amount=len(funcs)*iters print 'total method calls',amount avg_pyro_msec = 1000.0*duration/amount print 'avg. time per method call: %.4f msec (%d/sec)' % (avg_pyro_msec,amount/duration) print '-------- BENCHMARK LOCAL OBJECT ---------' object=bench.bench() begin = time.time() iters = 200000 for f in funcs: print iters,'times',f.__name__, voor = time.time() for i in range(iters): f() print '%.4f' % (time.time()-voor) duration = time.time()-begin print 'total time %.4f seconds' % duration amount=len(funcs)*iters print 'total method calls',amount avg_normal_msec = 1000.0*duration/amount print 'avg. time per method call: %.4f msec (%d/sec)' % (avg_normal_msec,amount/duration) print 'Normal method call is %.4f times faster than Pyro method call.'%(avg_pyro_msec/avg_normal_msec) Pyro-3.14/examples/benchmark/connections.py0000644000076500000240000000222711566251755021313 0ustar irmenstaff00000000000000import Pyro.core import Pyro.naming import time ns_uri=Pyro.naming.NameServerLocator().getNS().URI print "Name server location:",repr(ns_uri) print "Timing raw connect speed (no method call)..." p=Pyro.core.DynamicProxy(ns_uri) p.ping() begin=time.time() ITERATIONS=2000 for loop in xrange(ITERATIONS): if loop%500==0: print loop p._release() p.adapter.rebindURI() duration=time.time()-begin print "%d connections in %s sec = %f conn/sec" % (ITERATIONS, duration, ITERATIONS/duration) del p print "Timing proxy creation+connect+methodcall speed..." ITERATIONS=2000 begin=time.time() for loop in xrange(ITERATIONS): if loop%500==0: print loop p=Pyro.core.DynamicProxy(ns_uri) p.ping() duration=time.time()-begin print "%d new proxy calls in %s sec = %f calls/sec" % (ITERATIONS, duration, ITERATIONS/duration) print("Timing proxy methodcall speed...") p=Pyro.core.DynamicProxy(ns_uri) p.ping() ITERATIONS=10000 begin=time.time() for loop in range(ITERATIONS): if loop%1000==0: print(loop) p.ping() duration=time.time()-begin print("%d calls in %s sec = %.2f calls/sec" % (ITERATIONS, duration, ITERATIONS/duration)) Pyro-3.14/examples/benchmark/Readme.txt0000644000076500000240000000116111517657600020344 0ustar irmenstaff00000000000000This test is to find out the average time it takes for a remote PYRO method call. Also it is a kind of stress test because lots of calls are made in a very short time. The oneway method call test is very fast if you run the client and server on different machines. If they're running on the same machine, the speedup is less noticable. There is also the 'connections' benchmark which tests the speed at which Pyro can make new proxy connections. It tests the raw connect speed (by releasing and rebinding existing proxies) and also the speed at which new proxies can be created that perform a single remote method call. Pyro-3.14/examples/benchmark/server.py0000644000076500000240000000053711517657600020274 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys,os sys.path.insert(0,os.pardir) # to find testserver.py import testserver import Pyro.core Pyro.config.PYRO_MULTITHREADING=0 import bench class benchimpl(Pyro.core.ObjBase, bench.bench): def __init__(self): Pyro.core.ObjBase.__init__(self) ######## main program testserver.start(benchimpl, 'benchmark') Pyro-3.14/examples/callback/0000755000076500000240000000000011567263337016216 5ustar irmenstaff00000000000000Pyro-3.14/examples/callback/b2_client.py0000644000076500000240000000225211517657602020427 0ustar irmenstaff00000000000000#!/usr/bin/env python import time, random import Pyro.core import Pyro.naming from Pyro.errors import * from threading import Thread import bouncer2 abort=0 def PyroLoop(daemon): global abort print 'Pyro daemon loop thread is running.' daemon.requestLoop(lambda: not abort) print 'Pyro daemon loop thread is exiting.' def main(): global abort Pyro.core.initServer() Pyro.core.initClient() daemon = Pyro.core.Daemon() NS = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(NS) server = NS.resolve(':test.bouncer2').getProxy() bounceObj = bouncer2.Bouncer("Client") daemon.connect(bounceObj) # callback object # register callback obj on server server.register(bounceObj.getProxy()) # register server as 'callback' on own object bounceObj.register(server) # create a thread that handles callback requests thread=Thread(target=PyroLoop, args=(daemon,)) thread.start() print 'This bounce example will deadlock!' print 'Read the manual or Readme.txt for more info why this is the case!' print 'Calling server...' result = server.process(["hello"]) print 'Result=',result abort=1 thread.join() print 'Exiting.' if __name__=='__main__': main() Pyro-3.14/examples/callback/b2_server.py0000644000076500000240000000122611517657602020457 0ustar irmenstaff00000000000000#!/usr/bin/env python # this is exactly the same server code as "bounce_server.py" # but this one uses "bouncer2" as Pyro object. import Pyro.naming import Pyro.core from Pyro.errors import NamingError import bouncer2 as bouncer Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) try: ns.createGroup(':test') except NamingError: pass daemon.connect(bouncer.Bouncer('Server'),':test.bouncer2') # enter the service loop. print 'This bounce example will deadlock!' print 'Read the manual or Readme.txt for more info why this is the case!' print 'Bouncer started' daemon.requestLoop() Pyro-3.14/examples/callback/bounce_client.py0000644000076500000240000000171011517657602021375 0ustar irmenstaff00000000000000#!/usr/bin/env python import time, random import Pyro.core import Pyro.naming from Pyro.errors import * from threading import Thread import bouncer abort=0 def PyroLoop(daemon): global abort print 'Pyro daemon loop thread is running.' daemon.requestLoop(lambda: not abort) print 'Pyro daemon loop thread is exiting.' def main(): global abort Pyro.core.initServer() Pyro.core.initClient() daemon = Pyro.core.Daemon() NS = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(NS) bounceObj = bouncer.Bouncer("Client") daemon.connect(bounceObj) # callback object server = NS.resolve(':test.bouncer').getProxy() # create a thread that handles callback requests thread=Thread(target=PyroLoop, args=(daemon,)) thread.start() print 'Calling server from main (a single call)...' result = server.process(["hello"], bounceObj.getProxy()) print 'Result=',result abort=1 thread.join() print 'Exiting.' if __name__=='__main__': main() Pyro-3.14/examples/callback/bounce_server.py0000644000076500000240000000064411517657602021432 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.naming import Pyro.core from Pyro.errors import NamingError import bouncer Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) try: ns.createGroup(':test') except NamingError: pass daemon.connect(bouncer.Bouncer('Server'),':test.bouncer') # enter the service loop. print 'Bouncer started' daemon.requestLoop() Pyro-3.14/examples/callback/bouncer.py0000644000076500000240000000171411517657602020225 0ustar irmenstaff00000000000000import Pyro.core # a message bouncer. Passes messages back to the callback # object, until a certain limit is reached. class Bouncer(Pyro.core.CallbackObjBase): def __init__(self, name): Pyro.core.ObjBase.__init__(self) self.name=name self.count=0 def process(self,message,callback): if len(message)>=100: print "Back in",self.name,", message is large enough... stopping!" return ["complete at "+self.name+':'+str(self.count)] print "I'm",self.name,", bouncing back..." message.append(self.name) try: # note that we can use the callback proxy directly because it has # been passed in as a parameter to this method. That means that # Pyro has already taken care of transfering control to the current thread. result=callback.process(message, self.getProxy()) self.count+=1 result.insert(0,"passed on from "+self.name+':'+str(self.count)) return result except Exception,x: print "Error occurred in callback object:",x Pyro-3.14/examples/callback/bouncer2.py0000644000076500000240000000160711527042455020303 0ustar irmenstaff00000000000000from __future__ import with_statement import Pyro.core import Pyro.util # a message bouncer. Passes messages back to the callback # object, until a certain limit is reached. class Bouncer(Pyro.core.CallbackObjBase): def __init__(self, name): Pyro.core.ObjBase.__init__(self) self.name=name self.count=0 self.callbackMutex=Pyro.util.getLockObject() def register(self, callback): self.callback=callback def process(self,message): print 'in process',self.name if len(message)>=3: print "Back in",self.name,", message is large enough... stopping!" return ["complete at "+self.name+':'+str(self.count)] print "I'm",self.name,", bouncing back..." message.append(self.name) with self.callbackMutex: result=self.callback.process(message) self.count+=1 result.insert(0,"passed on from "+self.name+':'+str(self.count)) print 'returned from callback' return result Pyro-3.14/examples/callback/bouncer_cberror.py0000644000076500000240000000116111517657602021737 0ustar irmenstaff00000000000000import Pyro.core # a message bouncer. Raises an error in the callback function! # it is subclassed form CallbackObjBase, # so the error is also thrown on the client. # If you change this to the regular ObjBase, # you'll see that the error is silently passed back # to the server, and the client never knows about it. class Bouncer(Pyro.core.CallbackObjBase): def __init__(self, name): Pyro.core.ObjBase.__init__(self) self.name=name self.count=0 def process(self,message,callback): print 'This is',self.name print 'I\'ll throw an exception...' raise ValueError("Some error in the callback function") Pyro-3.14/examples/callback/cberror_client.py0000644000076500000240000000214711517657602021565 0ustar irmenstaff00000000000000#!/usr/bin/env python import time, random import Pyro.core import Pyro.naming from Pyro.errors import * from threading import Thread import bouncer_cberror abort=0 def PyroLoop(daemon): global abort print 'Pyro daemon loop thread is running.' daemon.requestLoop(lambda: not abort) print 'Pyro daemon loop thread is exiting.' def main(): global abort Pyro.core.initServer() Pyro.core.initClient() daemon = Pyro.core.Daemon() NS = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(NS) bounceObj = bouncer_cberror.Bouncer("Client") daemon.connect(bounceObj) # callback object server = NS.resolve(':test.bouncer').getProxy() # create a thread that handles callback requests thread=Thread(target=PyroLoop, args=(daemon,)) thread.start() print '1.Calling server from main (a single call)...' result = server.process(["hello"], bounceObj.getProxy()) print '1.Result=',result print '2.Calling server from main (a single call)...' result = server.process(["hello"], bounceObj.getProxy()) print '2.Result=',result abort=1 thread.join() print 'Exiting.' if __name__=='__main__': main() Pyro-3.14/examples/callback/Readme.txt0000644000076500000240000000544011517657602020154 0ustar irmenstaff00000000000000 This directory contains 4 callback examples: Shout, Bouncing, Bouncing2 and BounceError. SHOUT CALLBACK EXAMPLE ---------------------- This example shows how to use callbacks from server to client. It also makes use of the oneway call that was introduced in Pyro 2.4. The callback call is made oneway. This way the server can't be blocked by slow or buggy clients that fail to process the callback quickly. As you will see, you have to create your client not only as a client, but also as a Pyro server, because it will have to receive incoming Pyro calls. Also have a close look at the server. You need to take several things into account when dealing with subscribed callback objects. They may raise exceptions, they may get killed (no connection anymore) and many other things. This example may have some race conditions in the server because the 'shout' method may be called concurrently from different threads. You should use a thread lock on the list of subscribers. Server: shout_server.py Client: shout_client.py separate shouter: shout.py BOUNCING CALLBACK EXAMPLE ------------------------- This example shows how multiple Pyro objects could call back to each other, effectively bouncing a message around. (up to a certain limit, in this example: 100 invocations). This is sometimes called a conversation between objects. The client calls the server, which calls back to the client, which calls back to the server, which calls back to the client.... The server is just a regular Pyro server that serves a single "bouncer" object. The client also serves its own "bouncer" object, but has to run a separate thread to process the Pyro daemon loop. Because both client and server pass a new callback proxy at each method call, a new socket connection and thread is used for the callback. This makes sure that all calls get processed, and that no deadlock occurs. Server: bounce_server.py Client: bounce_client.py bouncer object: bounce.py BOUNCER2: broken code that triggers deadlock --------------------------------------------- The (broken!) bouncer2 code triggers a deadlock problem that is present in Pyro: a conversation between objects using a *single* registered callback proxy deadlocks because Pyro is still waiting for the other process to answer the callback invocation, instead of invoking the object again! Server: b2_server.py Client: b2_client.py BOUNCE_ERROR ------------ Server: bounce_server.py Client: cberror_client.py This callback example shows the use of CallbackObjBase as a base class for your callback objects, instead of ObjBase. The callback function throws an error and you'll see that it is not only returned to the server (who called the callback object), but it is also rethrown on the client, because the client usually is most interested in errors that occur in its own callback objects. Pyro-3.14/examples/callback/shout.py0000644000076500000240000000055011517657602017727 0ustar irmenstaff00000000000000#!/usr/bin/env python import time import Pyro.core import Pyro.naming from Pyro.errors import * def main(): Pyro.core.initClient() locator = Pyro.naming.NameServerLocator() NS = locator.getNS() server = Pyro.core.getProxyForURI(NS.resolve(':test.callback')) print 'Shouting something' server.shout('somebody there?') if __name__=='__main__': main() Pyro-3.14/examples/callback/shout_client.py0000644000076500000240000000233611517657602021271 0ustar irmenstaff00000000000000#!/usr/bin/env python import time, random import Pyro.core import Pyro.naming from Pyro.errors import * from threading import Thread class Listener(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def callback(self, message): print 'GOT CALLBACK: ',message abort=0 def shouter(objectURI): global abort object=objectURI.getProxy() # we get our own proxy object because we're running in our own thread. print 'Shouter thread is running.' while not abort: print 'Shouting something' object.shout('Hello out there!') time.sleep(random.random()*3) print'Shouter thread is exiting.' def main(): global abort Pyro.core.initServer() Pyro.core.initClient() daemon = Pyro.core.Daemon() locator = Pyro.naming.NameServerLocator() NS = locator.getNS() daemon.useNameServer(NS) listener=Listener() daemon.connect(listener) serverURI=NS.resolve(':test.callback') server = serverURI.getProxy() server.register(listener.getProxy()) thread=Thread(target=shouter, args=(serverURI,)) thread.start() while not abort: print 'Waiting for notification...' try: daemon.handleRequests() except KeyboardInterrupt: abort=1 thread.join() print 'Exiting.' if __name__=='__main__': main() Pyro-3.14/examples/callback/shout_server.py0000644000076500000240000000176211517657602021323 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0,os.pardir) # to find testserver.py import testserver import Pyro.core, Pyro.util ######## object that does the callbacks class CallbackThing(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.clients=[] def register(self, client): print 'REGISTER',client self.clients.append(client) #client._setOneway('callback') # don't wait for results for this method def shout(self, message): print 'Got shout:',message # let it know to all clients! for c in self.clients[:]: # use a copy of the list try: c.callback('Somebody shouted: '+message) # oneway call except Pyro.errors.ConnectionClosedError,x: # connection dropped, remove the listener if it's still there # check for existence because other thread may have killed it already if c in self.clients: self.clients.remove(c) print 'Removed dead listener',c ######## main program testserver.start(CallbackThing,'callback') Pyro-3.14/examples/chatbox-ES/0000755000076500000240000000000011567263337016417 5ustar irmenstaff00000000000000Pyro-3.14/examples/chatbox-ES/client.py0000644000076500000240000000456311517657600020252 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.EventService.Clients import Publisher, Subscriber from server import CHAT_SERVER_NAME from threading import Thread import Pyro.core from Pyro.errors import NamingError, ConnectionClosedError # Chat client. # Uses main thread for printing incoming event server messages # (the chat messages!) # and another to read user input and publish this on the chat channel. # Logon/logoff is performed using the Chat server, which gives us a chat channel # event server topic. The actual chatting is done fully trough # this channel (the ES), the chat server is not needed for this! class Chatter(Publisher, Subscriber): def __init__(self): Publisher.__init__(self) Subscriber.__init__(self) self.chatbox = Pyro.core.getProxyForURI('PYRONAME://'+CHAT_SERVER_NAME) def event(self, event): (nick,line)=event.msg if nick!=self.nick: print '['+nick+'] '+line def chooseChannel(self): nicks=self.chatbox.getNicks() if nicks: print 'The following people are on the server: ',', '.join(nicks) channels=self.chatbox.getChannels() channels.sort() if channels: print 'The following channels already exist: ',', '.join(channels) print self.channel=raw_input('Choose a channel or create a new one: ') else: print 'The server has no active channels.' self.channel=raw_input('Name for new channel: ') self.nick=raw_input('Choose a nickname: ') (self.eventTopic, people)=self.chatbox.join(self.channel,self.nick) self.subscribe(self.eventTopic) print 'Joined channel',self.channel,'as',self.nick print 'People on this channel:',', '.join(people) self.inputThread=Thread(target=self.handleInput) self.inputThread.start() try: self.listen() except KeyboardInterrupt: print 'Shutting down... (press enter)' self.abort() self.inputThread.join() def handleInput(self): print 'Ready for input! Type /quit to quit' try: try: while not self.abortListen: line=raw_input('> ') if line=='/quit': break if line: self.publish(self.eventTopic,(self.nick,line)) except EOFError: pass finally: # need to get new chatbox proxy because we're in a different thread chatbox = Pyro.core.getProxyForURI('PYRONAME://'+CHAT_SERVER_NAME) chatbox.leave(self.channel, self.nick) self.abort() print 'Bye!' def main(): chatter=Chatter() chatter.chooseChannel() if __name__=="__main__": main() Pyro-3.14/examples/chatbox-ES/Readme.txt0000644000076500000240000000112711517657600020351 0ustar irmenstaff00000000000000Chat box example using the Event Server This chat box example is constructed as follows: - a Chat Server (Pyro object) handles the login/logoff process and hands out Event Server topics for the desired channels - the Event Server is used to do the chatting itself. If the chat server dies, all people currently chatting can continue to do so because the chatting relies only on the Event Server. The chat client uses two threads, one for incoming chat messages, and one for reading and publishing the user's input. You'll have to start the Event Server ofcourse before running this example. Pyro-3.14/examples/chatbox-ES/server.py0000644000076500000240000000431011517657600020270 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.EventService.Clients import Publisher from Pyro.errors import NamingError import Pyro.core import sys CHAT_SERVER_GROUP = ":ChatBox-ES" CHAT_SERVER_NAME = CHAT_SERVER_GROUP+".Server" # Chat box administration server. # Handles logins, logouts, channels and nicknames. # The actual chat is fully performed by the Event Server! # (Yes, this also means that if this Chatbox server dies, the people # currently chatting can continue to do so without problems!) class ChatBox(Pyro.core.ObjBase, Publisher): def __init__(self): Pyro.core.ObjBase.__init__(self) Publisher.__init__(self) self.channels={} # registered channels { eventTopic->nick list } self.nicks=[] # all registered nicks on this server def getChannels(self): return self.channels.keys() def getNicks(self): return self.nicks def join(self, channel, nick): if nick in self.nicks: raise ValueError,'this nick is already in use' if not self.channels.has_key(channel): print 'CREATING NEW CHANNEL',channel self.channels[channel]=('ChatBox.Channel.'+channel,[]) self.channels[channel][1].append(nick) self.nicks.append(nick) print nick,'JOINED',channel self.publish(self.channels[channel][0],('SERVER','** '+nick+' joined **')) return self.channels[channel] # return the eventTopic for this channel def leave(self, channel, nick): if not self.channels.has_key(channel): print 'IGNORED UNKNOWN CHANNEL',channel return self.channels[channel][1].remove(nick) self.publish(self.channels[channel][0],('SERVER','** '+nick+' left **')) if len(self.channels[channel][1])<1: del self.channels[channel] print 'REMOVED CHANNEL',channel self.nicks.remove(nick) print nick,'LEFT',channel def main(): Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) # make sure our namespace group exists, and that our object name doesn't try: ns.createGroup(CHAT_SERVER_GROUP) except NamingError: pass try: ns.unregister(CHAT_SERVER_NAME) except NamingError: pass uri=daemon.connect(ChatBox(),CHAT_SERVER_NAME) # enter the service loop. print 'Chatbox open.' daemon.requestLoop() if __name__=='__main__': main() Pyro-3.14/examples/chatbox-non-ES/0000755000076500000240000000000011567263337017207 5ustar irmenstaff00000000000000Pyro-3.14/examples/chatbox-non-ES/client.py0000644000076500000240000000444511517657600021041 0ustar irmenstaff00000000000000#!/usr/bin/env python from server import CHAT_SERVER_NAME from threading import Thread import Pyro.core from Pyro.errors import NamingError, ConnectionClosedError # Chat client. # Uses main thread for printing incoming event server messages (the chat messages!) # and another to read user input and publish this on the chat channel. class Chatter(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.chatbox = Pyro.core.getProxyForURI('PYRONAME://'+CHAT_SERVER_NAME) self.abort=0 def message(self, nick, msg): if nick!=self.nick: print '['+nick+'] '+msg def chooseChannel(self): nicks=self.chatbox.getNicks() if nicks: print 'The following people are on the server: ',', '.join(nicks) channels=self.chatbox.getChannels() channels.sort() if channels: print 'The following channels already exist: ',', '.join(channels) print self.channel=raw_input('Choose a channel or create a new one: ') else: print 'The server has no active channels.' self.channel=raw_input('Name for new channel: ') self.nick=raw_input('Choose a nickname: ') people=self.chatbox.join(self.channel,self.nick,self.getProxy()) print 'Joined channel',self.channel,'as',self.nick print 'People on this channel:',', '.join(people) self.inputThread=Thread(target=self.handleInput) self.inputThread.start() def handleInput(self): print 'Ready for input! Type /quit to quit' # we need to get a new chatbox proxy because we're running in a different thread chatbox = Pyro.core.getProxyForURI('PYRONAME://'+CHAT_SERVER_NAME) try: try: while not self.abort: line=raw_input('> ') if line=='/quit': break if line: chatbox.publish(self.channel,self.nick,line) except EOFError: pass finally: chatbox.leave(self.channel, self.nick) self.abort=1 print 'Bye! (from input thread)' def main(): Pyro.core.initServer() Pyro.core.initClient() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) chatter=Chatter() daemon.connect(chatter) chatter.chooseChannel() try: daemon.requestLoop(lambda: not chatter.abort) except KeyboardInterrupt: print 'Shutting down chatter... (press enter)' chatter.abort=1 chatter.inputThread.join() print 'Exiting.' if __name__=="__main__": main() Pyro-3.14/examples/chatbox-non-ES/Readme.txt0000644000076500000240000000146411517657600021145 0ustar irmenstaff00000000000000Chat box example without using Event Server This chat box example is constructed as follows: - a Chat Server (Pyro object) handles the login/logoff process and keeps track of all chat channels and clients that are subscribed to each channel - The Chat Server itself implements the chatting and distributing of chat messages to all subscribers. It uses Pyro's oneway calls to improve performance and to avoid blocking. The chat client uses two threads, one for incoming chat messages, and one for reading and publishing the user's input. See also the 'callbacks' example. Many of the details shown there also apply here (oneway callback method, handling of killed callback clients, threading issues). [Your conclusion should be: unless you want more flexibility, use the Event Server if it can do the job!] Pyro-3.14/examples/chatbox-non-ES/server.py0000644000076500000240000000563311517657600021071 0ustar irmenstaff00000000000000#!/usr/bin/env python from threading import Thread from Pyro.errors import NamingError import Pyro.core, Pyro.naming, Pyro.util import sys CHAT_SERVER_GROUP = ":ChatBox" CHAT_SERVER_NAME = CHAT_SERVER_GROUP+".Server" # Chat box administration server. # Handles logins, logouts, channels and nicknames, and the chatting. class ChatBox(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.channels={} # registered channels { channel --> (nick, client callback) list } self.nicks=[] # all registered nicks on this server def getChannels(self): return self.channels.keys() def getNicks(self): return self.nicks def join(self, channel, nick, callback): if nick in self.nicks: raise ValueError,'this nick is already in use' if not self.channels.has_key(channel): print 'CREATING NEW CHANNEL',channel self.channels[channel]=[] self.channels[channel].append((nick,callback)) self.nicks.append(nick) callback._setOneway('message') # don't wait for results for this method print nick,'JOINED',channel self.publish(channel,'SERVER','** '+nick+' joined **') nicks=[] for (n,c) in self.channels[channel]: nicks.append(n) return nicks def leave(self,channel,nick): if not self.channels.has_key(channel): print 'IGNORED UNKNOWN CHANNEL',channel return for (n,c) in self.channels[channel]: if n==nick: self.channels[channel].remove((n,c)) break self.publish(channel,'SERVER','** '+nick+' left **') if len(self.channels[channel])<1: del self.channels[channel] print 'REMOVED CHANNEL',channel self.nicks.remove(nick) print nick,'LEFT',channel def publish(self, channel, nick, msg): # This must be performed in its own thread otherwise a thread deadlock may occur # when the publish is performed from within a call of a subscriber! Thread(target=self._do_publish, args=(channel, nick ,msg)).start() def _do_publish(self, channel, nick, msg): if not self.channels.has_key(channel): print 'IGNORED UNKNOWN CHANNEL',channel return for (n,c) in self.channels[channel][:]: # use a copy of the list try: c.message(nick,msg) # oneway call except Pyro.errors.ConnectionClosedError,x: # connection dropped, remove the listener if it's still there # check for existence because other thread may have killed it already if (n,c) in self.channels[channel]: self.channels[channel].remove((n,c)) print 'Removed dead listener',n,c def main(): Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) # make sure our namespace group exists, and that our object name doesn't try: ns.createGroup(CHAT_SERVER_GROUP) except NamingError: pass try: ns.unregister(CHAT_SERVER_NAME) except NamingError: pass uri=daemon.connect(ChatBox(),CHAT_SERVER_NAME) # enter the service loop. print 'Chatbox open.' daemon.requestLoop() if __name__=='__main__': main() Pyro-3.14/examples/circle/0000755000076500000240000000000011567263337015723 5ustar irmenstaff00000000000000Pyro-3.14/examples/circle/chain.py0000644000076500000240000000133411517657600017353 0ustar irmenstaff00000000000000import Pyro.core # a Chain member. Passes messages to the next link, # until the message went full-circle: then it exits. class Chain(Pyro.core.ObjBase): def __init__(self, name, next): Pyro.core.ObjBase.__init__(self) self.name=name self.nextName=next self.next=None def process(self,message): if self.next is None: self.next=Pyro.core.getProxyForURI("PYRONAME://:test.chain_"+self.nextName) if self.name in message: print "Back at",self.name,"; we completed the circle!" return ["complete at "+self.name] else: print "I'm",self.name,", passing to ",self.nextName message.append(self.name) result=self.next.process(message) result.insert(0,"passed on from "+self.name) return result Pyro-3.14/examples/circle/client.py0000644000076500000240000000026211517657600017546 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.core Pyro.core.initClient() proxy=Pyro.core.getProxyForURI("PYRONAME://:test.chain_A") print "Result=",proxy.process(["hello"]) Pyro-3.14/examples/circle/Readme.txt0000644000076500000240000000105711517657600017657 0ustar irmenstaff00000000000000Create a chain of objects calling each other: client --> A --> B ^ | | v | `----- C I.e. C calls A again. A detects that the message went full circle and returns the result (a 'trace' of the route) to the client. (the detection checks if the name of the current server is already in the current trace of the route, i.e., if it arrives for a second time on the same server, it concludes that we're done). First start the three servers (servA,B,C) and then run the client. Pyro-3.14/examples/circle/servA.py0000644000076500000240000000074211517657600017353 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.naming import Pyro.core import chain from Pyro.errors import PyroError,NamingError Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() try: ns.createGroup(":test") except NamingError: pass daemon.useNameServer(ns) objName='A' nextName='B' daemon.connect(chain.Chain(objName,nextName),':test.chain_'+objName) # enter the service loop. print 'Server started obj',objName daemon.requestLoop() Pyro-3.14/examples/circle/servB.py0000644000076500000240000000074211517657600017354 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.naming import Pyro.core import chain from Pyro.errors import PyroError,NamingError Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() try: ns.createGroup(":test") except NamingError: pass daemon.useNameServer(ns) objName='B' nextName='C' daemon.connect(chain.Chain(objName,nextName),':test.chain_'+objName) # enter the service loop. print 'Server started obj',objName daemon.requestLoop() Pyro-3.14/examples/circle/servC.py0000644000076500000240000000074211517657600017355 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.naming import Pyro.core import chain from Pyro.errors import PyroError,NamingError Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() try: ns.createGroup(":test") except NamingError: pass daemon.useNameServer(ns) objName='C' nextName='A' daemon.connect(chain.Chain(objName,nextName),':test.chain_'+objName) # enter the service loop. print 'Server started obj',objName daemon.requestLoop() Pyro-3.14/examples/countingcars/0000755000076500000240000000000011567263337017161 5ustar irmenstaff00000000000000Pyro-3.14/examples/countingcars/Client.py0000644000076500000240000000227611517657602020755 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.EventService.Clients import Subscriber from Pyro.errors import NamingError class TrafficCounter(Subscriber): def __init__(self): self.subjPrefix="CARS.HEADING." Subscriber.__init__(self) self.patterns=['north','south','east','west'] self.currentPattern=None self.counter=0 self.subscribeNextPattern() def subscribeNextPattern(self): if self.currentPattern: self.unsubscribe(self.subjPrefix+self.currentPattern) try: self.currentPattern=self.patterns.pop() print 'I am now watching for cars heading',self.currentPattern self.subscribe(self.subjPrefix+self.currentPattern) except IndexError: print 'I watched all directions.' self.abort() # break from the event loop def event(self, event): (color,car)=event.msg print 'A',color,car,'went',event.subject[len(self.subjPrefix):] self.counter+=1 if self.counter>=4: self.counter=0 print "There were enough cars in that direction. Let's look somewhere else." self.subscribeNextPattern() try: counter=TrafficCounter() print 'Going to count cars.' counter.listen() print 'Stopped counting cars.' except NamingError: print 'Cannot find service. Is the Event Service running?' Pyro-3.14/examples/countingcars/Readme.txt0000644000076500000240000000127411517657602021120 0ustar irmenstaff00000000000000This example shows the use of the Event Service. There is a client that counts certain types of cars that are passing. The passing of cars in a particular direction is published by Server.py. Clients subscribe to certain directions, and notice the cars that pass that way. After a certain amount of cars, the client switches direction and counts the cars there. It stoppes counting cars from the previous direction (unsubscribe). If all compass directions have been counted, the client exits. Make sure that the event server (es) has been started in advance. If you start a second or third server, cars will pass by faster because there are multiple publishers! See also the stockquotes example. Pyro-3.14/examples/countingcars/Server.py0000644000076500000240000000146311517657602021002 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.EventService.Clients import Publisher from Pyro.errors import NamingError import random, time class VehicleProducer(Publisher): directions = ('north','east','south','west') cars = ('Ford','Toyota','Chrysler','Vauxhall','Honda','BMW') colors = ('red','green','white','black','blue','yellow') def nextVehicle(self): direction=random.choice(self.directions) car=random.choice(self.cars) color=random.choice(self.colors) print color,car,'heading',direction self.publish('CARS.HEADING.'+direction, (color,car)) def main(): try: producer=VehicleProducer() print 'Starting traffic.' while 1: time.sleep(random.random()) producer.nextVehicle() except NamingError: print 'Cannot find service. Is the Event Service running?' if __name__=='__main__': main() Pyro-3.14/examples/denyhosts/0000755000076500000240000000000011567263337016502 5ustar irmenstaff00000000000000Pyro-3.14/examples/denyhosts/client.py0000644000076500000240000000243411517657577020345 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Notice: this client doesn't use the base testclient.py code # because this one has to do special tests with creating proxies. # import Pyro.naming, Pyro.core import Pyro.errors from Pyro.protocol import getHostname Pyro.core.initClient() Pyro.config.PYRO_NS_DEFAULTGROUP=':test' # default namespace group Pyro.config.PYRO_TRACELEVEL=3 Pyro.config.PYRO_LOGFILE='client_log' print 'Check the logfile: client_log' locator = Pyro.naming.NameServerLocator() print 'Searching Naming Service...', ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port print 'binding to object' try: URI=ns.resolve('denyhosts') print 'URI:',URI except Pyro.core.PyroError,x: print 'Couldn\'t bind object, nameserver says:',x raise SystemExit try: print 'Trying to create proxy object' obj=Pyro.core.getProxyForURI(URI) print 'Calling method 5 times' print 'Result1=',obj.method('Hi there 1') print 'Result2=',obj.method('Hi there 2') print 'Result3=',obj.method('Hi there 3') print 'Result4=',obj.method('Hi there 4') print 'Result5=',obj.method('Hi there 5') except Pyro.errors.ConnectionDeniedError,x: # Server denied the new connection. print 'ConnectionDeniedError occured:',x Pyro-3.14/examples/denyhosts/Readme.txt0000644000076500000240000000143111517657577020447 0ustar irmenstaff00000000000000This test is for showing how you might implement host/IP-based access control for new connections. It uses the Pyro feature to set a custom newConnectionValidator. The validator will ask you to accept or deny any new connections that occur to the server. Try it out, start the client from various machines and answer yes or no on the server when it asks you about the new connections. Note that even if you accept the connection, the custom validator then calls the default validator. This one then still checks if the max. number of connections is not exceeded. You might want to skip that last step, or re-implement it yourself (but you'll have to look at the code of the default validator, Pyro.protocol.DefaultConnValidator) because it accesses some internal structures of the daemon. Pyro-3.14/examples/denyhosts/server.py0000644000076500000240000000435111517657577020375 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.naming, Pyro.core, Pyro.util, Pyro.protocol, Pyro.constants from Pyro.errors import PyroError,NamingError from Pyro.protocol import getHostname ######## Custom connections validator. ######## This validator asks the user to accept or deny a new connection. ######## Note that the old validator is still called when the user accepts. class hostCheckingValidator(Pyro.protocol.DefaultConnValidator): def __init__(self): Pyro.protocol.DefaultConnValidator.__init__(self) def acceptHost(self,daemon,conn): (ip, port)=conn.addr try: hostname=getHostname(ip) except: hostname='' print '\nNew connection from',ip,hostname a=raw_input('Do you want to accept this? y/n: ') if a=='y': # user accepts, but pass it on to the default validator, # which will check the max. number of connections... return Pyro.protocol.DefaultConnValidator.acceptHost(self,daemon, conn) else: Pyro.util.Log.msg('ConnValidator','User denied connection from',ip,hostname) return (0,Pyro.constants.DENIED_HOSTBLOCKED) # not ok ##### test object class testobject(Pyro.core.ObjBase): def method(self,arg): print 'method was called with',arg return 42 ##### main program. # initialize the server and set the default namespace group Pyro.core.initServer() Pyro.config.PYRO_TRACELEVEL=3 Pyro.config.PYRO_NS_DEFAULTGROUP=':test' Pyro.config.PYRO_LOGFILE='server_log' print 'Check the logfile for messages: server_log' # Construct the Pyro Daemon with our own connection validator daemon = Pyro.core.Daemon() daemon.setNewConnectionValidator(hostCheckingValidator()) # <-- mind this!!! # locate the NS locator = Pyro.naming.NameServerLocator() print 'searching for Naming Service...' ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port # make sure our namespace group exists try: ns.createGroup(':test') except NamingError: pass daemon.useNameServer(ns) # connect a new object implementation (first unregister previous one) try: ns.unregister('denyhosts') except NamingError: pass daemon.connect(testobject(),'denyhosts') # enter the service loop. print 'Server object "denyhosts" ready.' daemon.requestLoop() Pyro-3.14/examples/disconnect/0000755000076500000240000000000011567263337016613 5ustar irmenstaff00000000000000Pyro-3.14/examples/disconnect/disconnect.py0000644000076500000240000000320411517657600021310 0ustar irmenstaff00000000000000import Pyro.core import Pyro.naming import Pyro.errors import pprint class MyPyroObj(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self, arg): return arg*2 daemon=Pyro.core.Daemon() ns=Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) try: ns.deleteGroup(":disconnect") except Pyro.errors.NamingError: pass ns.createGroup(":disconnect") obj1=MyPyroObj() obj2=MyPyroObj() obj3=MyPyroObj() obj4=MyPyroObj() uri1=daemon.connect(obj1,":disconnect.testobject1") uri2=daemon.connect(obj2,":disconnect.testobject2") uri3=daemon.connect(obj3,":disconnect.testobject3") uri4=daemon.connect(obj4,":disconnect.testobject4") print "registered objects:" pprint.pprint(daemon.getRegistered()) print "objects in name server:" pprint.pprint(ns.list(":disconnect")) print print "Removing object 1 by calling daemon.disconnect(obj)" daemon.disconnect(obj1) print "registered objects:" pprint.pprint(daemon.getRegistered()) print "objects in name server:" pprint.pprint(ns.list(":disconnect")) print print "Removing object 2 by calling daemon.disconnect(uri2.objectID)" daemon.disconnect(uri2.objectID) print "registered objects:" pprint.pprint(daemon.getRegistered()) print "objects in name server:" pprint.pprint(ns.list(":disconnect")) print print "Removing all other objects by looping over the registered object ids" for objectID in daemon.getRegistered().keys(): daemon.disconnect(objectID) print "registered objects:" pprint.pprint(daemon.getRegistered()) print "objects in name server:" pprint.pprint(ns.list(":disconnect")) daemon.shutdown() Pyro-3.14/examples/disconnect/Readme.txt0000644000076500000240000000046211517657600020546 0ustar irmenstaff00000000000000This example shows the various ways that you can use to disconnect objects from the daemon. The usual way is just to disconnect the object itself that you previously connected. But it is also possible to pass in an object's UID (as a string). You need to have a Name server running to run this example. Pyro-3.14/examples/distributed-computing/0000755000076500000240000000000011567263340021001 5ustar irmenstaff00000000000000Pyro-3.14/examples/distributed-computing/cell/0000755000076500000240000000000011567263340021720 5ustar irmenstaff00000000000000Pyro-3.14/examples/distributed-computing/cell/__init__.py0000644000076500000240000000001011517657601024022 0ustar irmenstaff00000000000000#packagePyro-3.14/examples/distributed-computing/cell/dispatcher.py0000644000076500000240000001175511517657601024433 0ustar irmenstaff00000000000000import Pyro.core import Pyro.naming import Pyro.errors import itertools import sys,os,time,copy import sets Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code (for the tasks) # # The task dispatcher. # It receives a single computation Task, searches in the Pyro Namespace # for available cell Processors, and divides and distributes the Task # among the processors. # It lets them run (in parallel!) and gathers the result from each. # As soon as one found the 'solution' to the Task, the others are # signalled to abort their computation, and the result is returned # to the client program. # # NOTE2: notice that this module doesn't import or use # the actual task code implementation. It relies on Pyro's # mobile code feature to obtain the actual task data! # (which it then sends to the cell processor(s) to run). # class Dispatcher(Pyro.core.ObjBase): def __init__(self, nameServer): Pyro.config.PYRO_NS_DEFAULTGROUP = ":Distributed.Cells" Pyro.core.ObjBase.__init__(self) self.NSBase=nameServer # make sure that the Event Server is active: self.NSBase.resolve(Pyro.constants.EVENTSERVER_NAME) def process(self,task): print "received task:",task NS=copy.copy(self.NSBase) # copy proxy because of thread issue try: cells=NS.list(None) # search the namespace for processors print "cells:",cells cells = [ NS.resolve(name).getAttrProxy() for name,otype in cells ] if cells: print "splitting task" taskparts = task.split(len(cells)) print "-->",len(taskparts),"tasks" celliter = itertools.cycle(cells) # just loop over all processors print "dispatching" busy=sets.Set() # While there are still subtasks to compute, and the # solution has not been found, dispatch the next subtask # to an available cell. while taskparts and not task.getResult(): cell = celliter.next() if cell in busy: time.sleep(0.2) if cell.finished: # okay... a cell has become available again. # process its result and remove it from the busy-set task.join(cell.task) busy.remove(cell) else: continue # found an idle cell, give it a task to do busy.add(cell) subtask = taskparts.pop() # NOTE: we first submit the task to the cell using a normal # Pyro invocation. This is needed because of the mobile code feature: # the server cannot request code over a oneway invocation. # Only after the server has received the computation task, # we invoke a oneway method to actually start the cell processor. # Because is is oneway, we can continue, while the cell is running. cell._setOneway("process") cell.receivetask(subtask) cell.process() print "dispatched %s, %d remain" % (str(subtask), len(taskparts)) print "Task fully dispatched!" print "Gathering results..." # wait for and retreive the result from each cell while busy and not task.getResult(): for cell in sets.Set(busy): if cell.finished: print "got result, %d remain" % len(busy) busy.remove(cell) task.join(cell.task) if task.getResult(): break time.sleep(0.2) print "all done for task",task if task.getResult() and busy: print "There are still %d running tasks" % len(busy) for cell in busy: cell._setOneway("abort") # so that we can continue cell.abort() print "Returning final result!" return task.getResult() else: print "no cells in ns" raise RuntimeError("no computing cells found") except Pyro.errors.NamingError,x: print "No cells registered on the network",x raise RuntimeError("no computing cells found") Pyro.core.initServer() ns=Pyro.naming.NameServerLocator().getNS() try: ns.createGroup(":Distributed") except Pyro.errors.NamingError: pass try: ns.unregister(":Distributed.Dispatcher") except Pyro.errors.NamingError: pass daemon=Pyro.core.Daemon() daemon.useNameServer(ns) uri=daemon.connect(Dispatcher(ns),":Distributed.Dispatcher") print "Dispatcher ready." daemon.requestLoop() Pyro-3.14/examples/distributed-computing/cell/monitor.py0000644000076500000240000000237011517657601023765 0ustar irmenstaff00000000000000from Pyro.EventService.Clients import Subscriber import os # # The monitor application that shows a table of running # tasks and the progress of each task. # class CellSubscriber(Subscriber): def __init__(self): Subscriber.__init__(self) self.subscribeMatch(r'^Distributed\.cell\..*$') self.tasks={} self.counter=0 def event(self, event): task=event.subject.split("Distributed.cell.")[1] if event.msg=="START": self.tasks[task]=0.0 elif event.msg=="FINISHED": self.tasks[task]=1.0 else: self.tasks[task]=event.msg self.counter+=1 self.screen() def screen(self): if os.name=='nt' or 'win' in os.name: os.system("cls") else: os.system("clear") print "------Distributed Computing Monitor is listening (%d)-------" % self.counter print "TASK PROGRESS" for task,progress in self.tasks.items(): if progress>=1.0: progressstr="[done]" else: progressstr="[%s]" % (('*'*int(progress*30.0+0.5)).ljust(30)) print task.ljust(20), progressstr sub = CellSubscriber() sub.screen() sub.listen() Pyro-3.14/examples/distributed-computing/cell/processor.py0000644000076500000240000000401611517657601024314 0ustar irmenstaff00000000000000import Pyro.core import Pyro.naming import Pyro.errors import sys,os Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code (for the tasks) # # The cell processor. # It processes tasks that it receives. # NOTE: it is not particularly intelligent about # what tasks to run and when!! It depends on the # dispatcher to do this in the correct way! # # NOTE2: notice that this module doesn't import or use # the actual task code implementation. It relies on Pyro's # mobile code feature to obtain the task data and code to run! # class Cell(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.finished=False def receivetask(self,task): self.finished=False self.task=task print "received task: "+str(task) def process(self): print "running task..." self.task.run() print "task finished." self.finished=True def abort(self): print "ABORT!" self.task.abort=True # # Initialize the environment # Pyro.core.initServer() ns=Pyro.naming.NameServerLocator().getNS() try: ns.createGroup(":Distributed") except Pyro.errors.NamingError: pass try: ns.createGroup(":Distributed.Cells") except Pyro.errors.NamingError: pass daemon=Pyro.core.Daemon() daemon.useNameServer(ns) Pyro.config.PYRO_NS_DEFAULTGROUP = ":Distributed.Cells" # # Find the next available object name that we can use # to register this cell processor. # The dispatcher looks in the Pyro namespace to find # us and the other available cells. # i=1 object = Cell() while True: try: name="processor%d" % i uri=daemon.connect( object ,name) print "Connected",ns.fullName(name) print "Cell ready." try: daemon.requestLoop() except Exception: daemon.shutdown() print "clean shutdown." try: ns.unregister(name) except Pyro.errors.NamingError: pass break except Pyro.errors.NamingError,x: i+=1 Pyro-3.14/examples/distributed-computing/client.py0000644000076500000240000000266411517657601022643 0ustar irmenstaff00000000000000import Pyro.core import Pyro.errors import Pyro.util import tasks.md5crack import tasks.sorting import time Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code (for the tasks) Pyro.core.initClient() selected_task = raw_input("What task do you want to run (md5 or sorting; m/s): ") if selected_task in ('m','md5'): UIClass = tasks.md5crack.UserInterface TaskClass = tasks.md5crack.CrackTask elif selected_task in ('s','sorting'): UIClass = tasks.sorting.UserInterface TaskClass = tasks.sorting.SortTask else: raise ValueError("invalid task chosen") ui=UIClass() arguments = ui.begin() task = TaskClass(arguments) ui.info(task) choice=input("Do you want sequential/normal (1) or distributed processing (2) ? ") if choice==1: print "(using normal sequential local processing)" start=time.time() tasks=task.split(3) # just for the fun of it while tasks: t=tasks.pop() print "(local) running task",t t.run() if task.join(t): break print "(local) gathering result" result=task.getResult() duration=time.time()-start elif choice==2: print "(using distributed parallel processing)" dispatcher = Pyro.core.getProxyForURI("PYRONAME://:Distributed.Dispatcher") start=time.time() try: result=dispatcher.process(task) # the interesting stuff happens here :) except Exception,x: print "".join(Pyro.util.getPyroTraceback(x)) duration=time.time()-start ui.result(result) print "It took %.3f seconds." % duration Pyro-3.14/examples/distributed-computing/Readme.txt0000644000076500000240000001051511517657601022743 0ustar irmenstaff00000000000000Cell-based- or distributed computing: md5 'cracking' and sorting. An MIMD (multiple instruction-multiple data) parallel system. Uses Pyro's mobile code feature to transfer the tasks to the cell processors, so it's also very easy to feed them new, other kinds of tasks. *** Starting up *** - start the Name Server - start the Event Server - cd into the cell/ directory - start the Dispatcher (dispatcher.py) - start a Monitor (monitor.py) to monitor the progress - start one or more processing 'cells' (processor.py). For best results, start one of these on every machine/CPU in your network - finally, give the system a task to solve: start the client.py program. Choose what you like to do: md5 cracking or sorting a huge list of random letters. MD5: "frogs" is a nice string for it to solve. Sorting: a list size of 20000 (twenty thousand) is nice for starters. - Select '1' to see how your system works when it solves the task 'locally' (in a single Python process), select '2' to have fun and observe how the distributed system does it. On my 2 computers at home it guesses "frogs" in about 30 seconds (on a single computer it takes 60 seconds); it sorts a 50000-element list in 20 seconds (on a single computer the same task takes 40 seconds). - while the system solves the task, observe the monitor program to see how the different tasks are progessing. The effect will be better the more computers participating, ofcourse, but having 2 or 3 is usually enough already for interesting results. ** NOTE: starting/removing cells after the dispatcher has run will not work ** *** What the heck is this, anyway? *** This is a distributed task processor, with two specific tasks implemented: the 'cracking' or 'guessing' of strings that make up a given secure MD5 hash code. It does this by brute-force search: simply try all strings from aaaaa to zzzzz (example) and see if the MD5 hash is the same. Words longer than 5 letters take a long time to guess... The other task is distributed sorting of a huge list of random letters. The sorting algorithm is a dumb, slow one that works similar to insertion sort with a final single merge sort pass at the end. You enter the string to guess, a MD5 hashcode is generated from it, or with the sorting task, a long list of random letters is generated, and then the task is divided over the available cell processors. They each work trough a part of the search space in parallel, thus greatly improving computation power. The more computers you add in the network (and start a cell processor on), the faster it finds your "secret" string or sorts the list! - The Pyro Namespace is used to register and discover the set of available processing cells. The task dispatcher just asks the NS for a list of registered processors, to see where it can send the tasks to. The cells are called: :Distributed.Cells.processor1 :Distributed.Cells.processor2 :Distributed.Cells.processor3 and so on. - The Event Server is used to channel the processor's progress reports. Every cell processor emits progress reports during the calculation that it performs and the monitor script listens for these reports. It displays them in nice bar graphs. - The task implementation that is used here for the md5 cracker can be aborted. This is used to signal all processors to stop as soon as one of them has found the solution. - The way the Task and TaskPartition classes are designed should make it very easy to add your own specific task implementation that you would like to use in this system. Distributed PI computation, image processing, whatever. Any task that can be partitioned. It is NOT required that every partition is of equal size, and it is also NOT required to exactly have N partitions where N=number of cells. The dispatcher takes care of it all. Many smaller partitions are better than a few large ones though, because response time will be better. - Because the mobile code feature is used to transfer the tasks, the cell processors need not have the code for the computation task available. You can feed them any task you like :-) (as long as it uses the Task/TaskPartition API) - Because of a bug in Pyro 3.4 and older you can only run this example correctly on Pyro 3.5+ (older versions didn't process oneway method calls in the way that was intended, and froze on subsequent calls). Pyro-3.14/examples/distributed-computing/tasks/0000755000076500000240000000000011567263340022126 5ustar irmenstaff00000000000000Pyro-3.14/examples/distributed-computing/tasks/__init__.py0000644000076500000240000000001111517657601024231 0ustar irmenstaff00000000000000#package Pyro-3.14/examples/distributed-computing/tasks/md5crack.py0000644000076500000240000000741311517657601024200 0ustar irmenstaff00000000000000import tasks.task import array, time try: import hashlib md5=hashlib.md5 except ImportError: import md5 md5=md5.md5 # Determine how fast your cpu is (est.) # this is used to create a reasonable progress update rate. start=time.time() print "(benchmarking...)" CPU_SPEED=0 while time.time()-start < 1: void=md5("benchmark").digest() CPU_SPEED+=1 CPU_SPEED /= 2 # # The 'main' task of cracking (guessing) a MD5 hash. # It partitions the work in a way where each subtask # processes a small set of the possible search space. # (it is a brute-force search). # class CrackTask(tasks.task.PartitionableTask): def __init__(self, sourceText): tasks.task.PartitionableTask.__init__(self,"md5 guesser") self.source=sourceText # we will guess this self.md5hash=md5(self.source).digest() # we will crack this self.result=None def split(self, numPiecesHint): pieces=numPiecesHint*3 # number of pieces step=26.0/pieces # chunks from the alphabet begin=ord('a') # start with a taskparts=[] for i in range(1,pieces+1): end= ord('a')+int(i*step) taskparts.append(CrackTaskPartition(self.md5hash, begin, end, len(self.source))) begin=end return taskparts def join(self,task): if not self.result and task.result: # we found the solution! self.result=task.result return True return False def getResult(self): return self.result # generator to cycle trough all possible strings (letter combinations) def allStrings(size, begin, end): data=array.array('b') data.fromstring( chr(begin)+'a'*(size-1) ) # 'xaaaaaa...' def nextletter(pos): data[pos]+=1 if data[pos]>122: # ord('z') if pos==0: data[0]+=1 return data[pos]=97 # ord('a') nextletter(pos-1) while data[0]<=end: yield data.tostring() # increase letters nextletter(size-1) # # The subtask responsible for brute-forcing a certain part # of the search space (a limited range based on the first letter) # class CrackTaskPartition(tasks.task.TaskPartition): def __init__(self, md5hash, begin, end, size): tasks.task.TaskPartition.__init__(self,"md5 %s - %s" % (chr(begin),chr(end-1)) ) self.md5hash=md5hash self.begin=begin self.end=end self.amount=(self.end-self.begin) * 26.0**(size-1) self.size=size self.abort=False def work(self): strings=allStrings(self.size, self.begin, self.end) self.result=None counter=0 try: while not self.result and not self.abort: for i in range(CPU_SPEED): s = strings.next() if md5(s).digest() == self.md5hash: self.result=s # FOUND IT!! return counter+=CPU_SPEED yield counter except StopIteration: pass # we've processed all strings, no result. def progress(self, pos): return pos/self.amount # # The 'user interface' for the md5 task. # class UserInterface(object): def begin(self): print "MD5 'cracking'." code=raw_input("Enter a short word (4-5 lowercase letters; a-z) that is the key: ") return code def info(self, task): print "The md5 hash of your word is ",task.md5hash.encode("hex") print "Cracking the md5 hash with code length %d..." % len(task.source) def result(self,taskresult): if taskresult: print "\nI cracked the code! It was: '%s'" % taskresult print "(that string produces the same md5 hash as your original code word)" else: print "\nCode could not be cracked." Pyro-3.14/examples/distributed-computing/tasks/sorting.py0000644000076500000240000000607211517657601024174 0ustar irmenstaff00000000000000from tasks.task import PartitionableTask, TaskPartition import time import random import string # # The 'main' task of sorting a big list of random numbers. # It partitions the work in a way where each subtask # sorts a chunk of the original huge list, and after that, # all sorted chunks are merged using a single merge pass of O(n). # class SortTask(PartitionableTask): def __init__(self, listSize): PartitionableTask.__init__(self,"data sorter") self.size=listSize self.chunks=[] self.numchunks=0 self.result=[] def split(self, numPiecesHint): lst=[random.choice(string.lowercase) for i in range(self.size)] pieces=numPiecesHint*3 # number of pieces chunksize=self.size/pieces taskparts=[] for i in range(pieces+1): chunk = lst[0:chunksize] del lst[0:chunksize] if chunk: taskparts.append(SortTaskPartition(chunk, i+1)) else: break self.numchunks=len(taskparts) return taskparts def join(self,task): self.chunks.append(task.result) return False # not done yet, also join all other tasks def getResult(self): if len(self.chunks)!=self.numchunks: return None # not yet enough chunks received if self.result: return self.result assert sum([len(x) for x in self.chunks])==self.size, "length of combined chunks is incorrect" # use a single pass of the generic merge sort O(n) to get the final sorted list. # we sort in reverse order because removing elements from the end of a list is O(1) result=[] starttime=time.time() while len(result)largest_elt: largest_elt=chunk[-1] largest_chunk=chunk result.append(largest_elt) del largest_chunk[-1] result.reverse() self.result=result print "Chunks merged in %.03f seconds." % (time.time()-starttime) return self.result # # The subtask responsible for sorting a certain part of the full list # class SortTaskPartition(TaskPartition): def __init__(self, unsortedList, chunknumber): TaskPartition.__init__(self,"sort chunk %d" % chunknumber ) self.lst = unsortedList self.result=None self.size=len(self.lst) def work(self): prevprogress=time.time() result=[] while self.lst: elt=self.lst.pop() i=0 while i=elt: break i+=1 result.insert(i, elt) if (time.time()-prevprogress)>0.3: yield len(result) prevprogress=time.time() self.result=result def progress(self, pos): return pos/float(self.size) # # The 'user interface' for the sort task. # class UserInterface(object): def begin(self): print "Big data array sorting." size=input("Enter the size of the data array (>100): ") return size def info(self, task): print "Sorting the data array of length %d..." % task.size def result(self,taskresult): filename="sorted.txt" print "\nWriting the sorted data array to",filename out=open(filename,"w") while taskresult: print >>out, "".join(taskresult[:70]) del taskresult[:70] out.close() Pyro-3.14/examples/distributed-computing/tasks/task.py0000644000076500000240000000245211517657601023447 0ustar irmenstaff00000000000000import sys import Pyro.EventService.Clients import Pyro.constants # # The 'main' Task abstract baseclass. # class PartitionableTask(object): def __init__(self, name): self.name=name def split(self, numPiecesHint): pass # implement in subclass def join(self, task): pass # implement in subclass, return True to stop the whole task. def getResult(self): pass # implement in subclass def __str__(self): return "" % self.name # # The subtask baseclass. The main task is split into these # subtasks, which are distributed over the processors. # The progress of each subtask can be monitored on the ES # channels Distributed.cell.* (*=subtask name) # (use the monitor.py for this) # class TaskPartition(Pyro.EventService.Clients.Publisher): def __init__(self, name): Pyro.EventService.Clients.Publisher.__init__(self) self.name=name def run(self): self.publish("Distributed.cell."+self.name, "START") for pos in self.work(): self.publish("Distributed.cell."+self.name, self.progress(pos)) self.publish("Distributed.cell."+self.name, "FINISHED") def progress(self,pos): return 100.0*pos/100.0 # override in subclass def work(self): yield 100 # override this generator in subclass, here the actual work is done def __str__(self): return "" % self.name Pyro-3.14/examples/distributed-computing2/0000755000076500000240000000000011567263340021063 5ustar irmenstaff00000000000000Pyro-3.14/examples/distributed-computing2/client.py0000644000076500000240000000273011517657601022717 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import Pyro.errors import Queue import random from workitem import Workitem NUMBER_OF_ITEMS = 40 Pyro.core.initClient() numbers = {} def processResult(item): print "Got result: %s (from %s)" % (item, item.processedBy) numbers[item.data] = item.result def main(): print "\nThis program will calculate Prime Factorials of a bunch of random numbers." print "The more workers you will start (on different cpus/cores/machines)," print "the faster you will get the complete list of results!\n" dispatcher = Pyro.core.getProxyForURI("PYRONAME://:Distributed2.dispatcher") print "placing work items into dispatcher queue." for i in range(NUMBER_OF_ITEMS): number=random.randint(3211, 5000)*random.randint(177,3000)*37 numbers[number] = None item = Workitem(i+1, number) dispatcher.putWork(item) print "getting results from dispatcher queue." resultCount=0 while resultCount0: print "removing leftover results from the dispatcher" while True: try: item = dispatcher.getResult() processResult(item) except Queue.Empty: break print "\nComputed Prime Factorials follow:" for (number, factorials) in numbers.items(): print number,"-->",factorials if __name__=="__main__": main() Pyro-3.14/examples/distributed-computing2/dispatcher.py0000644000076500000240000000210111517657601023557 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import Pyro.naming from Pyro.errors import NamingError from Queue import Queue class DispatcherQueue(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.workqueue = Queue() self.resultqueue = Queue() def putWork(self, item): self.workqueue.put(item) def getWork(self, timeout=5): return self.workqueue.get(block=True, timeout=timeout) def putResult(self, item): self.resultqueue.put(item) def getResult(self, timeout=5): return self.resultqueue.get(block=True, timeout=timeout) def workQueueSize(self): return self.workqueue.qsize() def resultQueueSize(self): return self.resultqueue.qsize() ######## main program Pyro.core.initServer() ns=Pyro.naming.NameServerLocator().getNS() daemon=Pyro.core.Daemon() daemon.useNameServer(ns) try: ns.createGroup(":Distributed2") except NamingError: pass try: ns.unregister(":Distributed2.dispatcher") except NamingError: pass uri=daemon.connect(DispatcherQueue(),":Distributed2.dispatcher") print "Dispatcher is ready." daemon.requestLoop() Pyro-3.14/examples/distributed-computing2/Readme.txt0000644000076500000240000000303111517657601023020 0ustar irmenstaff00000000000000A bit easier distributed computing example as the first. This one uses a "pull" model instead of a "push" model: there is a single central work dispatcher/gatherer that is contacted by every worker you create. The worker asks the dispatcher for a chunk of work data and returns the results when it is done, ad infinitum. For simplicity sake, there is no mobile code involved in this example. There is no real-time monitor as well. For a more generic system that allows for different worker tasks, and with realtime monitoring, see the first distributed-computing example. *** Starting up *** - start the Name Server - start the Dispatcher (dispatcher.py) - start one or more workers (worker.py). For best results, start one of these on every machine/CPU in your network - finally, give the system a task to solve: start the client.py program. WHAT DOES THIS THING DO ANYWAY??? It computes Prime Factorials of a bunch of random numbers. The client program generates a list of random numbers and sends each number as a single work item to the dispatcher. One or more workers pick work items from the dispatcher and go to factorize the number. They place their result back into the dispacher. The client collects all results and prints a nice table afterwards. The dispatcher is really not more than 2 threadsafe queues wrapped in a Pyro object. The more workers you will start (one per cpu/core/machine) the faster the list of results will be produced, because every worker will do the factorization of its current work item in parallel to the others. Pyro-3.14/examples/distributed-computing2/worker.py0000644000076500000240000000215011517657601022746 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import Pyro.errors import Queue from workitem import Workitem import os, socket, sys from math import sqrt WORKERNAME = "Worker_%d@%s" % (os.getpid(), socket.gethostname()) Pyro.core.initClient() def factorize(n): def isPrime(n): return not [x for x in xrange(2,int(sqrt(n))+1) if n%x == 0] primes = [] candidates = xrange(2,n+1) candidate = 2 while not primes and candidate in candidates: if n%candidate == 0 and isPrime(candidate): primes = primes + [candidate] + factorize(n/candidate) candidate += 1 return primes def process(item): print "factorizing",item.data,"-->", sys.stdout.flush() item.result=factorize(int(item.data)) print item.result item.processedBy = WORKERNAME def main(): dispatcher = Pyro.core.getProxyForURI("PYRONAME://:Distributed2.dispatcher") print "This is worker",WORKERNAME print "getting work from dispatcher." while True: try: item = dispatcher.getWork() except Queue.Empty: print "no work available yet." else: process(item) dispatcher.putResult(item) if __name__=="__main__": main() Pyro-3.14/examples/distributed-computing2/workitem.py0000644000076500000240000000036711517657601023306 0ustar irmenstaff00000000000000class Workitem(object): def __init__(self, itemId, data): print "Created workitem",itemId # XXX self.itemId=itemId self.data=data self.result=None self.processedBy=None def __str__(self): return "" % str(self.itemId) Pyro-3.14/examples/exceptions/0000755000076500000240000000000011567263340016635 5ustar irmenstaff00000000000000Pyro-3.14/examples/exceptions/client.py0000644000076500000240000000214011517657601020464 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0,os.pardir) # to find testclient.py import testclient import Pyro.util from excep import MyError test = testclient.getproxy('exceptions') print test.div(2.0,9.0) try: print 2/0 except ZeroDivisionError,x: print 'DIVIDE BY ZERO',x try: print test.div(2,0) except ZeroDivisionError,x: print 'DIVIDE BY ZERO',x try: result=test.error() print repr(result),result except ValueError,x: print 'VALUERROR',x try: result=test.error2() print repr(result),result except ValueError,x: print 'VALUERROR',x try: result=test.othererr() print repr(result),result except MyError,x: print 'MYERROR',x try: result=test.othererr2() print repr(result),result except MyError,x: print 'MYERROR',x print '*** invoking server method that crashes, catching traceback ***' try: print test.complexerror() except Exception,x: print 'CAUGHT ERROR, printing Pyro traceback >>>>>>',x print ''.join(Pyro.util.getPyroTraceback(x)) print '<<<<<<< end of Pyro traceback' print '*** invoking server method that crashes, not catching anything ***' print test.complexerror() Pyro-3.14/examples/exceptions/excep.py0000644000076500000240000000077311517657601020324 0ustar irmenstaff00000000000000class MyError(Exception): pass class testclass(object): def div(s, arg1, arg2): return arg1/arg2 def error(s): raise ValueError('a valueerror! Great!') def error2(s): return ValueError('a valueerror! Great!') def othererr(s): raise MyError('my error!') def othererr2(s): return MyError('my error!') def complexerror(s): x=foo() x.crash() class foo(object): def crash(s): s.crash2('going down...') def crash2(s, arg): # this statement will crash on purpose: x=arg/2 Pyro-3.14/examples/exceptions/Readme.txt0000644000076500000240000000067111517657601020601 0ustar irmenstaff00000000000000This test is to show PYRO's remote exception capabilities. The remote object contains various member functions which raise various kinds of exceptions. The client will print those. Note the special handling of the Pyro exception. It is possible to extract and print the *remote* traceback. You can then see where in the code on the remote side the error occured! Also try to set PYRO_DETAILED_TRACEBACK to 1 for a very detailed traceback!Pyro-3.14/examples/exceptions/server.py0000644000076500000240000000053011517657601020515 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys,os sys.path.insert(0,os.pardir) # to find testserver.py import testserver import Pyro.core import excep ######## testclass object class testclass(Pyro.core.ObjBase, excep.testclass): def __init__(self): Pyro.core.ObjBase.__init__(self) ######## main program testserver.start(testclass, 'exceptions') Pyro-3.14/examples/factory/0000755000076500000240000000000011567263340016123 5ustar irmenstaff00000000000000Pyro-3.14/examples/factory/client.py0000644000076500000240000000125311517657601017756 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0, os.pardir) # to find testclient.py import testclient fact = testclient.getproxy('factory') print 'Local Python PID =', os.getpid() c1=fact.create('Opel') c2=fact.create('Ford') c3=fact.create('Honda') print 'Factory produced three cars (of three different brands).' print 'Each object\'s PID is printed after its name. The PID' print 'is different than the local Python PID above: its the PID' print 'from the server process (the factory) that produced them!' print '(look in the server window to see what its PID is).' print c1.name(), 'pid=',c1.pid() print c2.name(), 'pid=',c2.pid() print c3.name(), 'pid=',c3.pid() Pyro-3.14/examples/factory/factory.py0000644000076500000240000000123511517657601020147 0ustar irmenstaff00000000000000import os import Pyro.core class Opel(Pyro.core.ObjBase): def name(s): return "Opel Astra coupe" def pid(s): return os.getpid() class Honda(Pyro.core.ObjBase): def name(s): return "Honda S2000" def pid(s): return os.getpid() class Ford(Pyro.core.ObjBase): def name(s): return "Ford Mustang" def pid(s): return os.getpid() class CarFactory(Pyro.core.ObjBase): def pid(s): return os.getpid() def create(s, brand): if brand=='Ford': car = Ford() elif brand=='Honda': car = Honda() elif brand=='Opel': car = Opel() else: raise ValueError('unknown brand') s.getDaemon().connect(car) return car.getProxy() Pyro-3.14/examples/factory/Readme.txt0000644000076500000240000000057511517657601020072 0ustar irmenstaff00000000000000This example shows the 'factory' feature of Pyro: it shows how to create new objects on the server and pass them back to the client (actually, not the objects themselves but proxies are passed back). The example uses PID (process IDs) to show that the produced objects actually live on the server, not the client. The server runs single-threaded, this way all pids are the same. Pyro-3.14/examples/factory/server.py0000644000076500000240000000051711517657601020010 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0,os.pardir) # to find testserver.py import testserver import Pyro.core import factory ###### For educational purposes, disable threads Pyro.config.PYRO_MULTITHREADED=0 ######## main program print 'Server PID=', os.getpid() testserver.start(factory.CarFactory,'factory') Pyro-3.14/examples/filetransfer/0000755000076500000240000000000011567263340017140 5ustar irmenstaff00000000000000Pyro-3.14/examples/filetransfer/client.py0000644000076500000240000000467011517657601021001 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Client for the extremely simple 'file-server' using Pyro. # # NOTE: this is just an example to show how you _could_ send # large streams of data with Pyro (by transferring it in chunks). # THIS CODE CAN NOT COPE WITH MULTIPLE TRANSFERS AT THE SAME TIME! # (because the file server is statefull and only knows about # a single file transfer that is in progress). Also, no measures # are taken to protect the file transfer from other clients that # connect to the server and 'hijack' the download by calling # retrieveNextChunk. # import Pyro.core import sys,time class FileClient(object): def __init__(self): self.fileserver = Pyro.core.getProxyForURI("PYRONAME://fileserver") def menu(self): print "\nls : list directory" print "r : retrieve file at once" print "c : retrieve file in chunks" print "q : quit" def cli(self): while True: self.menu() cmd=raw_input("?>") if cmd=="ls": self.ls() elif cmd.startswith("r "): self.retrieveAtOnce(cmd[2:]) elif cmd.startswith("c "): self.retrieveChunks(cmd[2:]) elif cmd=="q": return else: print "invalid command" def ls(self): files = self.fileserver.listdir() files.sort() #for d in dirs: # print " %s [DIRECTORY]" % d for f in files: print f def retrieveAtOnce(self,file): print "Retrieving",file,"..." starttime=time.time() try: data=self.fileserver.retrieveAtOnce(file) except IOError,x: print "error: ",x else: duration=time.time()-starttime print len(data),"bytes received in",int(duration),"seconds", if duration>0: print "=",int(len(data)/duration/1024.0),"kb/sec" open(file,"wb").write(data) print "saved to",file def retrieveChunks(self,file): print "Retrieving",file,"..." starttime=time.time() try: size = self.fileserver.openFile(file) except IOError,x: print "error: ",x else: print "Filesize=",size total=0 file=open(file,"wb") while True: chunk=self.fileserver.retrieveNextChunk() sys.stdout.write(".") sys.stdout.flush() if chunk: file.write(chunk) total+=len(chunk) else: break self.fileserver.closeFile() file.close() duration=time.time()-starttime print total,"bytes received in",int(duration),"seconds", if duration>0: print "=",int(total/duration/1024.0),"kb/sec" def main(args): Pyro.core.initClient() client=FileClient() client.cli() if __name__=="__main__": main(sys.argv) Pyro-3.14/examples/filetransfer/Readme.txt0000644000076500000240000000016711517657601021104 0ustar irmenstaff00000000000000This shows a possible way to transfer files. It implements a very rudimentary server and file browser/download client. Pyro-3.14/examples/filetransfer/server.py0000644000076500000240000000437011517657601021026 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import Pyro.naming import os,sys CHUNK_SIZE = 500000 # # File server. # Serves files only from a single directory. # Supports two transfer methods: # - read and send whole file in once call (uses much memory!, but is fastest) # - read and send file in chunks (uses only bytes of memory, is slightly slower.) # # # NOTE: this is just an example to show how you _could_ send # large streams of data with Pyro (by transferring it in chunks). # THIS CODE CAN NOT COPE WITH MULTIPLE TRANSFERS AT THE SAME TIME! # (because the file server is statefull and only knows about # a single file transfer that is in progress). Also, no measures # are taken to protect the file transfer from other clients that # connect to the server and 'hijack' the download by calling # retrieveNextChunk. # class FileServer(Pyro.core.ObjBase): def __init__(self, rootdir): Pyro.core.ObjBase.__init__(self) self.rootdir=os.path.abspath(rootdir) print "File server serving from",self.rootdir def listdir(self): dirs=[] files=[] for a in os.listdir(self.rootdir): if os.path.isdir(os.path.join(self.rootdir,a)): dirs.append(a) else: files.append(a) return files # forget about the directories. def retrieveAtOnce(self, file): return open(os.path.join(self.rootdir,file),'rb').read() def openFile(self,file): if hasattr(self.getLocalStorage(), "openfile"): raise IOError("can only read one file at a time, close previous file first") file=os.path.join(self.rootdir,file) self.getLocalStorage().openfile=open(file,'rb') return os.path.getsize(file) def retrieveNextChunk(self): chunk= self.getLocalStorage().openfile.read(CHUNK_SIZE) if chunk: return chunk self.getLocalStorage().openfile.close() return '' def closeFile(self): self.getLocalStorage().openfile.close() del self.getLocalStorage().openfile def main(args): Pyro.core.initServer() daemon=Pyro.core.Daemon() daemon.useNameServer(Pyro.naming.NameServerLocator().getNS()) uri=daemon.connect(FileServer(args[1]), "fileserver") print "File server is running." daemon.requestLoop() if __name__=="__main__": if len(sys.argv)!=2: print "Please give the file server root path as an argument to this script." else: main(sys.argv) Pyro-3.14/examples/hugetransfer/0000755000076500000240000000000011567263340017151 5ustar irmenstaff00000000000000Pyro-3.14/examples/hugetransfer/client.py0000644000076500000240000000115411517657600021003 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0,os.pardir) # to find testclient.py import testclient obj = testclient.getproxy('hugetransfer') #if os.name!="java": # object._setTimeout(4) import time basesize = 500000 data='A'*basesize totalsize=0 begin=time.time() for i in range(1,15): print 'transferring',basesize*i,'bytes' size=obj.transfer(data*i) # print " reply=",size totalsize=totalsize+basesize*i duration=time.time()-begin print 'It took',duration,'seconds to transfer',totalsize/1024,'kilobyte.' print 'That is',totalsize/1024/duration,'k/sec. = ',totalsize/1024/1024/duration,'mb/sec.' Pyro-3.14/examples/hugetransfer/Readme.txt0000644000076500000240000000025311517657600021110 0ustar irmenstaff00000000000000This test transfers huge data structures to see how Pyro handles those. Also it uses the Delegate Pattern on the server side instead of subclassing the Pyro.core.ObjBase. Pyro-3.14/examples/hugetransfer/server.py0000644000076500000240000000053111517657600021031 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0,os.pardir) # to find testserver.py import testserver import Pyro.core ######## testclass object class testclass(object): def transfer(self,data): print 'received',len(data),'bytes' return len(data) ######## main program testserver.start(testclass, 'hugetransfer', delegate=1) Pyro-3.14/examples/inheritance/0000755000076500000240000000000011567263340016745 5ustar irmenstaff00000000000000Pyro-3.14/examples/inheritance/client.py0000644000076500000240000000062511517657600020601 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0,os.pardir) # to find testclient.py import testclient test = testclient.getproxy('inheritance') print "base1.meth1? -->",test.meth1() # should be base1.meth1 print "Fsub.meth2? -->",test.meth2() # should be Fsub.meth2 print "base2.meth3? -->",test.meth3() # should be base2.meth3 print "Fsub.meth4? -->",test.meth4() # should be Fsub.meth4 Pyro-3.14/examples/inheritance/inherit.py0000644000076500000240000000104611517657600020763 0ustar irmenstaff00000000000000from ftplib import FTP import Pyro.core class base1(object): def meth1(self): return 'base1.meth1' def meth2(self): return 'base1.meth2' class base2(object): def meth2(self): return 'base2.meth2' def meth3(self): return 'base2.meth3' class sub(base1,base2): def meth2(self): return 'sub.meth2 (overridden)' def meth4(self): return 'sub.meth4' class Fsub(base1,base2,FTP): def meth2(self): return 'Fsub.meth2 (overridden)' def meth4(self): return 'Fsub.meth4' class Gsub(base1, Pyro.core.ObjBase): def ding(self): pass Pyro-3.14/examples/inheritance/Readme.txt0000644000076500000240000000022211517657600020700 0ustar irmenstaff00000000000000This is for testing the case where the remote object is a subclass of some other class(es). The right member function should be called ofcourse. Pyro-3.14/examples/inheritance/server.py0000644000076500000240000000053111517657600020625 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0,os.pardir) # to find testserver.py import testserver import Pyro.core import inherit ######## testclass object class testclass(Pyro.core.ObjBase, inherit.Fsub): def __init__(self): Pyro.core.ObjBase.__init__(self) ######## main program testserver.start(testclass, 'inheritance') Pyro-3.14/examples/logging/0000755000076500000240000000000011567263340016102 5ustar irmenstaff00000000000000Pyro-3.14/examples/logging/logging.cfg0000644000076500000240000000153611517657602020221 0ustar irmenstaff00000000000000# Example logging configuration file for Pyro. # Pyro uses two loggers: # Pyro.system (pyro itself) # Pyro.user (user logger) [loggers] keys=root,systemLog,userLog [handlers] keys=rootLog_stderr,systemLogFile,userLogFile [formatters] keys=pyroFormatter [logger_root] level=NOTSET handlers=rootLog_stderr [logger_systemLog] qualname=Pyro.system level=DEBUG handlers=systemLogFile propagate=0 [logger_userLog] qualname=Pyro.user level=DEBUG handlers=userLogFile propagate=0 [handler_rootLog_stderr] class=StreamHandler level=NOTSET args=(sys.stderr,) [handler_systemLogFile] class=FileHandler formatter=pyroFormatter args=("custom_system.log","a") [handler_userLogFile] class=FileHandler formatter=pyroFormatter args=("custom_user.log","a") [formatter_pyroFormatter] format=CUSTOM!! %(asctime)s [%(process)d:%(thread)d] ** %(levelname)s ** %(message)s Pyro-3.14/examples/logging/Readme.txt0000644000076500000240000000036111517657602020043 0ustar irmenstaff00000000000000Abuse the two logging facilities of Pyro (the system log and the user log). Output is 2 files: Pyro_sys_log and Pyro_user_log. You can also try to use PYRO_STDLOGGING, with or without an appropriate configuration file. (see "logging.cfg") Pyro-3.14/examples/logging/testlogging.py0000644000076500000240000000405011517657602021004 0ustar irmenstaff00000000000000#!/usr/bin/env python # Test the logging facilities. # Set the logfiles import Pyro.util Pyro.config.PYRO_LOGFILE = 'Pyro_sys_log' Pyro.config.PYRO_USER_LOGFILE = 'Pyro_user_log' print 'Creating the logging objects.' SLog = Pyro.util.SystemLogger() ULog = Pyro.util.UserLogger() def tst(l, head): l.raw(head) l.error('test','Some error','#1') l.warn('test','Some warning','#1') l.msg('test','Some message','#1') l.error('test','error with numeric args',1,2,3,4) l.warn('test','warning with numeric args',1,2,3,4) l.msg('test','message with numeric args',1,2,3,4) l.error('test error without args') l.warn('test warning without args') l.msg('test message without args') print 'Logging various things...' Pyro.config.PYRO_TRACELEVEL = 0 Pyro.config.PYRO_USER_TRACELEVEL = 0 tst(SLog,'--- THIS IS THE SYSTEM LOG TEST ---\n') tst(SLog,'YOU SHOULDNT SEE ANY MESSAGES BECAUSE TRACING IS OFF\n') tst(ULog,'--- THIS IS THE USER LOG TEST ---\n') tst(ULog,'YOU SHOULDNT SEE ANY MESSAGES BECAUSE TRACING IS OFF\n') Pyro.config.PYRO_TRACELEVEL = 1 Pyro.config.PYRO_USER_TRACELEVEL = 1 tst(SLog,'YOU SHOULD ONLY SEE ERRORS (LEVEL 1)\n') tst(ULog,'YOU SHOULD ONLY SEE ERRORS (LEVEL 1)\n') Pyro.config.PYRO_TRACELEVEL = 2 Pyro.config.PYRO_USER_TRACELEVEL = 2 tst(SLog,'YOU SHOULD ONLY SEE ERRORS+WARNS (LEVEL 2)\n') tst(ULog,'YOU SHOULD ONLY SEE ERRORS+WARNS (LEVEL 2)\n') Pyro.config.PYRO_TRACELEVEL = 3 Pyro.config.PYRO_USER_TRACELEVEL = 3 tst(SLog,'YOU SHOULD SEE ALL (LEVEL 3)\n') tst(ULog,'YOU SHOULD SEE ALL (LEVEL 3)\n') Pyro.config.PYRO_TRACELEVEL = 4 Pyro.config.PYRO_USER_TRACELEVEL = 4 tst(SLog,'YOU SHOULD SEE ALL (LEVEL 4)\n') tst(ULog,'YOU SHOULD SEE ALL (LEVEL 4)\n') Pyro.config.PYRO_TRACELEVEL = -1 Pyro.config.PYRO_USER_TRACELEVEL = -1 tst(SLog,'YOU SHOULDNT SEE ANY MESSAGES BECAUSE TRACING IS -1\n') tst(ULog,'YOU SHOULDNT SEE ANY MESSAGES BECAUSE TRACING IS -1\n') print 'All done. See the logfiles!' print 'System log went to:',SLog._logfile() print 'User log went to:',ULog._logfile() print "(or other destinations if so configured when using PYRO_STDLOGGING)" Pyro-3.14/examples/maxclients/0000755000076500000240000000000011567263340016623 5ustar irmenstaff00000000000000Pyro-3.14/examples/maxclients/client.py0000644000076500000240000000373111517657603020463 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Notice: this client doesn't use the base testclient.py code # because this one has to do special tests with creating proxies. # import Pyro.naming, Pyro.core import Pyro.errors from Pyro.protocol import getHostname Pyro.core.initClient() Pyro.config.PYRO_NS_DEFAULTGROUP=':test' # default namespace group Pyro.config.PYRO_TRACELEVEL=3 Pyro.config.PYRO_LOGFILE='client_log' print 'Check the logfile: client_log' locator = Pyro.naming.NameServerLocator() print 'Searching Naming Service...', ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port print 'binding to object' try: URI=ns.resolve('maxclients') print 'URI:',URI except Pyro.core.PyroError,x: print 'Couldn\'t bind object, nameserver says:',x raise SystemExit print '---- trying to create a bunch of proxy objects' objects=[] for i in range(20): ok=0 while not ok: try: print 'creating proxy object',i obj=Pyro.core.getProxyForURI(URI) # Pyro 1.2+ notice: the following method call # actually binds the object. Just creating a proxy # doesn't. You can create many, many proxies without # a single connection to the server. Try commenting # the setname call out and notice that all proxies # are created successfully, and a crash occurs only # later on. (line 66, where a method call is made and # the object is connected). obj.setname(i) # remote name obj.__dict__['localname']=i # local (proxy) name objects.append(obj) ok=1 except Pyro.errors.ConnectionDeniedError,x: # Server denied the new connection. # A possible solution is to delete another one we don't need # anymore and to try again. print 'Error creating proxy object:',x print ' -> removing old proxy and trying again...' if objects: del objects[0] for obj in objects: print 'object',id(obj),'is called remotely:',obj.getname(), 'and locally:',obj.__dict__['localname'] Pyro-3.14/examples/maxclients/Readme.txt0000644000076500000240000000044111517657603020564 0ustar irmenstaff00000000000000This test is for checking how Pyro handles the maximum number of client connections. It shows a possible way to deal with the event that the Pyro server is fully occupied with connections. Also it uses the Delegate Pattern on the server side instead of subclassing the Pyro.core.ObjBase. Pyro-3.14/examples/maxclients/server.py0000644000076500000240000000110511517657603020504 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os sys.path.insert(0,os.pardir) # to find testserver.py import testserver import Pyro.core ######## testclass object class testclass(object): def setname(self,name): self.name=name def getname(self): return self.name ######## main program Pyro.core.initServer() Pyro.config.PYRO_TRACELEVEL=3 Pyro.config.PYRO_LOGFILE='server_log' Pyro.config.PYRO_MAXCONNECTIONS = 10 print 'Reduced max number of simultaneous connections to 10' print 'Check the logfile for messages: server_log' testserver.start(testclass, 'maxclients', delegate=1) Pyro-3.14/examples/mobilehierarchy/0000755000076500000240000000000011567263340017622 5ustar irmenstaff00000000000000Pyro-3.14/examples/mobilehierarchy/client.py0000644000076500000240000000052211517657603021455 0ustar irmenstaff00000000000000import Pyro.core Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code import clientmodule3 mobileobject=clientmodule3.MyClass() print "sending object to server" server = Pyro.core.getProxyForURI("PYROLOC://localhost:12233/MobileHierarchy") result=server.process(mobileobject) print "done, got result, method call:",result.method() Pyro-3.14/examples/mobilehierarchy/clientmodule1.py0000644000076500000240000000012611517657603022744 0ustar irmenstaff00000000000000class BaseClass(object): def method(self): return "this is the method's result" Pyro-3.14/examples/mobilehierarchy/clientmodule2.py0000644000076500000240000000011211517657603022740 0ustar irmenstaff00000000000000import clientmodule1 class SomeClass(clientmodule1.BaseClass): pass Pyro-3.14/examples/mobilehierarchy/clientmodule3.py0000644000076500000240000000011011517657603022737 0ustar irmenstaff00000000000000import clientmodule2 class MyClass(clientmodule2.SomeClass): pass Pyro-3.14/examples/mobilehierarchy/readme.txt0000644000076500000240000000040411517657603021622 0ustar irmenstaff00000000000000Shows that the mobile code logic can deal with a hierarchy of depending modules. Client sends an object to the server that requires 3 modules to be sent as mobile code. Server sends an object in its response that also requires 3 modules to be downloaded. Pyro-3.14/examples/mobilehierarchy/server/0000755000076500000240000000000011567263340021130 5ustar irmenstaff00000000000000Pyro-3.14/examples/mobilehierarchy/server/server.py0000644000076500000240000000117511517657603023020 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core Pyro.config.PYRO_MOBILE_CODE=1 # Enable mobile code import servermodule3 class PyroServer(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def process(self, obj): print "Got call, invoking method, result=", obj.method() print "Returning response object." return servermodule3.ResponseClass() daemon = Pyro.core.Daemon(host='localhost', port=12233) print print 'The Pyro Deamon is running on ',daemon.hostname+':'+str(daemon.port) uri=daemon.connect(PyroServer(),'MobileHierarchy') # enter the service loop. print 'waiting for calls.' daemon.requestLoop() Pyro-3.14/examples/mobilehierarchy/server/servermodule1.py0000644000076500000240000000013411517657603024301 0ustar irmenstaff00000000000000class BaseClass: def method(self): return "this is the server object method's result" Pyro-3.14/examples/mobilehierarchy/server/servermodule2.py0000644000076500000240000000011211517657603024276 0ustar irmenstaff00000000000000import servermodule1 class SomeClass(servermodule1.BaseClass): pass Pyro-3.14/examples/mobilehierarchy/server/servermodule3.py0000644000076500000240000000011611517657603024303 0ustar irmenstaff00000000000000import servermodule2 class ResponseClass(servermodule2.SomeClass): pass Pyro-3.14/examples/multithread/0000755000076500000240000000000011567263340016776 5ustar irmenstaff00000000000000Pyro-3.14/examples/multithread/client.py0000644000076500000240000000325211517657601020632 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os, random import time import threading sys.path.insert(0,os.pardir) # to find testclient.py import testclient count = int(raw_input('Number of parallel clients: ')) test = testclient.getproxy('multithread') testObjects=[test] for i in range(count-1): testObjects.append(test.__copy__()) processtime = 1.0 def processing(index, proxy): thread=threading.currentThread() name=thread.getName() time.sleep(random.randint(1,5)) print 'Processing started',name while threading.currentThread().running: t1 = time.time() print name, "CALLING...." print name, proxy.process(name,processtime), span = time.time() - t1 print 'It took %.2f sec' % span print "exiting thread",name # start a set of threads which perform requests print print 'I will create a set of threads which run concurrently.' print 'Each thread invokes the remote object with a process time of',processtime,'seconds.' print 'The remote object will wait that long before completion.' print 'If the remote server is singlethreaded, each invocation is processed sequentially and has to wait for the previous one to complete. This will result in processing times longer than the specified amount!' print 'If the remote server is multithreaded, all remote invocations are processed in parallel and will complete exactly after the specified process time.' threads=[] for i in range(count): thread = threading.Thread(target=processing, args=(i, testObjects[i])) threads.append(thread) thread.running=True thread.start() void=raw_input('\nPress enter to stop...\n\n') for p in threads: p.running=False for p in threads: p.join() print 'stopped',p.getName() print 'Graceful exit.' Pyro-3.14/examples/multithread/Readme.txt0000644000076500000240000000232511517657601020740 0ustar irmenstaff00000000000000This example shows the need of multithreading in the Pyro server. The client spawns serveral concurrent loops which access the same remote object. If the Pyro server is single threading, each remote invocation has to wait for the previous one to complete. If the Pyro server is multithreading, each remote invocation is processed simultaneously. If your server is I/O bound, you may successfully run several requests at the same time without noticable delay (because the I/O gets executed in parallel). If your sever is CPU bound, you may notice that the multithreading has no effect, because the CPU is busy 100%. If you are lucky and have multiple CPUs, you may experience speedup after all. But no more than the number of cpus you have. Thread Local Storage (TLS): the server access Pyro's TLS feature. If you're running in single-threaded mode you'll see that the counter is shared among all invocations (because there is only one thread) and that it increases with each invocation. If running multithreaded, you'll see that each caller/thread has its own TLS and that the counters are increased independently. The server also takes care of correct initialization of the TLS by setting a custom initTLS function in the Daemon. Pyro-3.14/examples/multithread/server.py0000644000076500000240000000533311517657601020664 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os, time sys.path.insert(0,os.pardir) # to find testserver.py import testserver import time import Pyro.naming import Pyro.core from Pyro.errors import NamingError from threading import currentThread _timeval=0 def _cpu_delay(): # try to do a 100% cpu time delay loop without using python bytecode try: import cPickle x=cPickle.dumps(range(100)) except: import hmac x=hmac.new("somerandomthingie",str(range(100))).digest() def determinePeriod(): global _timeval print 'Determining how long to busy wait for 1 seconds' begin = time.time() i=0 while (time.time()-begin) < 1.0: _cpu_delay() i+=1 print 'That is',i _timeval=i return _timeval ######## testclass objects class IOtestclass_delegate(object): def process(self, name, period): print 'called by',name print 'thread=',currentThread().getName() try: currentThread().localStorage.counter+=1 print 'TLS.counter=',currentThread().localStorage.counter except AttributeError: print "No TLS available!" time.sleep(period) return 'READY!' class CPUtestclass_delegate(object): def process(self,name, period): global _timeval print 'called by',name try: currentThread().localStorage.counter+=1 print 'TLS.counter=',currentThread().localStorage.counter except AttributeError: print "No TLS available!" begin = time.time() print 'begin=',begin for i in range(period*_timeval): x=time.time() _cpu_delay() z=time.time()-begin print 'ready in ',z,'sec ',name ######## TLS INIT def initTLS(tls): print "INIT TLS! TLS=",tls print "Setting counter for this TLS to 0." tls.counter=0 ######## main program def main(): Pyro.config.PYRO_NS_DEFAULTGROUP=":test" if raw_input('multithreaded? y/n: ')=='y': Pyro.config.PYRO_MULTITHREADED = 1 print 'Using multithreaded server.' else: Pyro.config.PYRO_MULTITHREADED = 0 print 'Using singlethreaded server.' if raw_input('IO bound or CPU bound? io/cpu: ')=='io': testclass = IOtestclass_delegate print 'Using IO bound server.' else: testclass = CPUtestclass_delegate print 'Using CPU bound server.' determinePeriod() print "Class used for server object:", testclass Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) # set TLS init func daemon.setInitTLS(initTLS) # make sure our namespace group exists try: ns.createGroup(":test") except NamingError: pass try: ns.unregister(":test.multithread") except NamingError: pass object=Pyro.core.ObjBase() # delegate approach object.delegateTo(testclass()) daemon.connect(object,"multithread") # enter the service loop. print "Server started" daemon.requestLoop() if __name__=="__main__": main() Pyro-3.14/examples/naming/0000755000076500000240000000000011567263340015725 5ustar irmenstaff00000000000000Pyro-3.14/examples/naming/nametest.py0000644000076500000240000000741611517657601020131 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Naming example. Shows basic name server handling. # import Pyro.naming from Pyro.errors import PyroError,NamingError from Pyro.protocol import getHostname group = ':test' # the namespace group for all test servers #------------------------------------------------- subroutines -------------- # join: join 2 name components to form one name def join(part1,part2): if part2[0]==':': return part2 elif part1==':': return part1+part2 else: return part1+'.'+part2 # dumpNamespace: print a flat dump of all object names in the namespace def dumpNamespace(ns): print '--- namespace flat dump ---' flat = ns.flatlist() for (name, value) in flat: print ' ',name,'-->',value print '-------' # _listsub: recursive helper for listNamespace def _listsub(ns, parent,name,level): newgroup = join(parent,name) list = ns.list(newgroup) print ' '*level+'['+name+']' for (subname,type) in list: if type==0: # group _listsub(ns,newgroup,subname,level+1) elif type==1: # object name print ' '*(1+level)+subname # listNamespace: print the namespace in a recursive tree format def listNamespace(ns): print '--- namespace tree listing ---' _listsub(ns,'',':',0) print '-------' #------------------------------------------------- main program -------------- # initialize the server and set the default namespace group Pyro.core.initServer() Pyro.config.PYRO_NS_DEFAULTGROUP=group print 'Default group changed to',group # locate the NS locator = Pyro.naming.NameServerLocator() print 'searching for Naming Service...' ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port # make sure our namespace group exists try: ns.deleteGroup(group) # delete it and all stuff that's still in it except NamingError: pass ns.createGroup(group) # create it again print 'Created name group',group print 'Creating some subgroups...' ns.createGroup('group1') ns.createGroup('group2') ns.createGroup('group3') ns.createGroup(join('group3','group3a')) # subgroup ns.createGroup(join('group3','group3b')) # subgroup ns.createGroup(join('group3',join('group3b','deeplynested'))) # subgroup ns.createGroup('group3/notasubgroup') # / is not a name separator, so this is not a subgroup raw_input("Created some groups. Press enter to see them.") dumpNamespace(ns) listNamespace(ns) raw_input("That were the groups. Press enter to register some objects.") print 'Registering some objects...' URI='PYRO://localhost/12345678-12345678-12345678-12345678' ns.register('def_obj1',URI) ns.register('def_obj2',URI) ns.register('def_obj3',URI) ns.register(join('group1','def_obj1a'),URI) ns.register(join('group1','def_obj1b'),URI) ns.register(join('group2','def_obj2a'),URI) ns.register(join('group2','def_obj2b'),URI) ns.register(join('group3',join('group3a','someobject')),URI) ns.register(join('group3',join('group3b',join('deeplynested','deepobject'))),URI) raw_input("Registered some objects. Press enter to see them.") dumpNamespace(ns) listNamespace(ns) raw_input("That were the objects. Press enter to continue.") name = join('group3',join('group3b',join('deeplynested','deepobject'))) print 'Full name of',name,'is:',ns.fullName(name) grp = ns.fullName(join('group3',join('group3b','deeplynested'))) Pyro.config.PYRO_NS_DEFAULTGROUP = grp print 'Default group changed to',grp name = 'deepobject' print 'Full name of',name,'is:',ns.fullName(name) Pyro.config.PYRO_NS_DEFAULTGROUP = group print 'Default group changed back to',group raw_input("Press enter to clean up.") print 'Cleaning up...' ns.deleteGroup('group1') ns.deleteGroup('group2') ns.deleteGroup('group3') ns.deleteGroup('group3/notasubgroup') ns.unregister('def_obj1') ns.unregister('def_obj2') ns.unregister('def_obj3') ns.deleteGroup(group) print "done!" Pyro-3.14/examples/naming/Readme.txt0000644000076500000240000000012511517657601017663 0ustar irmenstaff00000000000000Use Pyro's object naming and show the use of the naming functions in the Nameserver. Pyro-3.14/examples/noNS/0000755000076500000240000000000011567263340015331 5ustar irmenstaff00000000000000Pyro-3.14/examples/noNS/client.py0000644000076500000240000000114611517657604017170 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Client that doesn't use the Name Server. Uses PYROLOC:// URI. # import sys import Pyro.core Pyro.core.initClient() objectName = 'QuoteGenerator' hostname = raw_input('Enter the hostname of the server: ') port = raw_input('Enter the port of the server, or just enter: ') print 'Creating proxy for object',objectName,' on ',hostname+':'+port if port: URI='PYROLOC://'+hostname+':'+port+'/'+objectName else: URI='PYROLOC://'+hostname+'/'+objectName print 'The URI is',URI proxy=Pyro.core.getProxyForURI(URI) print 'Getting some quotes...' print proxy.quote() print proxy.quote() Pyro-3.14/examples/noNS/client2.py0000644000076500000240000000051411517657604017250 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Client that doesn't use the Name Server. Uses URI directly. # import sys import Pyro.core Pyro.core.initClient() uri = raw_input('Enter the URI of the quote object: ') print 'Creating proxy' proxy=Pyro.core.getProxyForURI(uri) print 'Getting some quotes...' print proxy.quote() print proxy.quote() Pyro-3.14/examples/noNS/Readme.txt0000644000076500000240000000073211517657604017276 0ustar irmenstaff00000000000000This example shows the PYROLOC:// URI. You don't need a Name Server if you use this. But this is not recommended because you miss all the features of the Name Server. However, just take a look at how simple the client is now!! Also the client2.py uses the URI string directly if you could somehow obtain it (the server prints it out, so copy-paste it). It only contains two lines of Pyro code! Please also read the documentation on the PYROLOC:// and PYRONAME:// URIs. Pyro-3.14/examples/noNS/server.py0000644000076500000240000000166311517657604017224 0ustar irmenstaff00000000000000#!/usr/bin/env python # # The server that doesn't use the Name Server. # import sys, os import Pyro.core from Pyro.errors import PyroError class QuoteGen(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def quote(self): try: quote=os.popen('fortune').read() if len(quote)>0: return quote return "This system cannot provide you a good fortune, install it" except: return "I know only this quote... but it came from the server!" Pyro.core.initServer() daemon = Pyro.core.Daemon() print print 'The Pyro Deamon is running on ',daemon.hostname+':'+str(daemon.port) print '(you may need this info for the client to connect to)' print objectName='QuoteGenerator' uri=daemon.connect(QuoteGen(),objectName) # enter the service loop. print 'QuoteGen is ready for customers. I am not using the Name Server.' print 'Object name is:',objectName print 'The URI is: ',uri daemon.requestLoop() Pyro-3.14/examples/NS_sec_plugins/0000755000076500000240000000000011567263337017375 5ustar irmenstaff00000000000000Pyro-3.14/examples/NS_sec_plugins/NSSecEx.py0000644000076500000240000000321111517657601021210 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Example of Name Server security plugins. # # This shows possible implementations of a NS BC request validator # and a NS new connection validator. # # See the Readme.txt for more information. # ACCEPTED_ID = 'p4ssphr4se' #----- required global funcs that return validator objects ------ def BCGuard(): return BCReqValidator() def NSGuard(): v=NSnewConnValidator() v.setAllowedIdentifications([ACCEPTED_ID]) return v #----- validator object implementation -------- import Pyro.naming import Pyro.protocol # NS Broadcast Request Validator # Must inherit from the base class as shown, # because dispatcher code is in there. class BCReqValidator(Pyro.naming.BCReqValidator): # we have: # self.addr = address of client (ip, port) # self.sock = reply socket (used by self.reply method) def acceptLocationCmd(self): print self.addr[0],'WANTS TO KNOW OUR LOCATION. Ok...' return 1 def acceptShutdownCmd(self): print self.addr[0],'WANTS US TO SHUT DOWN, Pfff!' self.reply('denied!') # send this back to client return 0 # NS Pyro Daemon newConnValidator class NSnewConnValidator(Pyro.protocol.DefaultConnValidator): def acceptHost(self, tcpserver, conn): print conn.addr[0],'WANTS CONNECTION...' return Pyro.protocol.DefaultConnValidator.acceptHost(self, tcpserver, conn) def acceptIdentification(self, tcpserver, conn, token, challenge): print conn.addr[0],'SENDS IDENTIFICATION...' (ok,reason)=Pyro.protocol.DefaultConnValidator.acceptIdentification(self, tcpserver, conn, token, challenge) if not ok: print 'Connection denied! Make sure the identification is "'+ACCEPTED_ID+'"' return (ok,reason) Pyro-3.14/examples/NS_sec_plugins/Readme.txt0000644000076500000240000000144111517657601021327 0ustar irmenstaff00000000000000Example of Name Server security plugins. This shows possible implementations of a NS BC request validator (the one here denies all shutdown requests) and a NS new connection validator (the one here prints some info of the client wanting a Pyro connection, and requires a special identification passphrase to connect. Use the -i option with nsc). Make sure the current directory ('.') is part of your PYTHONPATH, because otherwise the security module cannot be loaded from this directory (this is correct -safe- behavior of Python). Start the nameserver with: "pyro-ns -v -s NSSecEx" or "python -m Pyro.naming -v -s NSSecEx" It will show you that it's using your security plugins. Then try to use the commands from the "nsc" or "xnsc" tool and see what happens and what is printed on your screen. Pyro-3.14/examples/oneway/0000755000076500000240000000000011567263340015756 5ustar irmenstaff00000000000000Pyro-3.14/examples/oneway/client.py0000644000076500000240000000112611517657603017612 0ustar irmenstaff00000000000000#!/usr/bin/env python import time import Pyro.core serv = Pyro.core.getProxyForURI("PYRONAME://oneway") print "starting server using a oneway call" serv._setOneway("start") serv.start() print "doing some stuff..." time.sleep(4) print "now contacting the server to see if it's done." print "we are faster, so you should see a few attempts," print "until the server is finished." while True: print "server done?" if serv.ready(): print "yes!" break else: print "no, trying again" time.sleep(1) print "getting the result from the server:",serv.result() Pyro-3.14/examples/oneway/Readme.txt0000644000076500000240000000225211517657603017721 0ustar irmenstaff00000000000000This shows how oneway method calls work. The client uses a oneway call to the server to start it. In the meantime, it continues by itself and does some things. After a short while, it uses regular Pyro calls again to check on the server until it is ready. Things to notice: Not only will the client continue immediately after the oneway method has been called, the server will also process the method invocation 'in the background'. Other methods may be called while the oneway call -which takes a while to complete- is still running. For more information see the Features chapter in the manual (callbacks & oneway calls). PYRO_ONEWAY_THREADED config item: This server sets PYRO_ONEWAY_THREADED to True, so Pyro will run incoming oneway method calls in their own thread. This allows for the server to continue to process other method calls on this object in the meantime! That is why the client can "poll" for server completion. (this is the default setting, by the way). Try setting PYRO_ONEWAY_THREADED to False in the server and see what happens. The client tries to poll the server for completion, but the method call blocks until the call to the 'oneway' method finished processing. Pyro-3.14/examples/oneway/server.py0000644000076500000240000000164411517657603017647 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import Pyro.naming import time class server(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.busy=False def start(self): print "start request received. Starting work..." self.busy=True for i in range(10): time.sleep(1) print 10-i print "work is done!" self.busy=False def ready(self): print "ready status requested (%r)" % (not self.busy) return not self.busy def result(self): return "The result :)" ######## main program Pyro.config.PYRO_ONEWAY_THREADED = True # try setting this to False and see what happens in the client Pyro.core.initServer() ns=Pyro.naming.NameServerLocator().getNS() daemon=Pyro.core.Daemon() daemon.useNameServer(ns) uri=daemon.connect(server(),"oneway") print "Server is ready." daemon.requestLoop() Pyro-3.14/examples/pickle/0000755000076500000240000000000011567263340015723 5ustar irmenstaff00000000000000Pyro-3.14/examples/pickle/pickletest.py0000644000076500000240000001700711517657601020453 0ustar irmenstaff00000000000000import Pyro.core import Pyro.protocol import Pyro.errors import Pyro.naming import Pyro.EventService.Clients import Pyro.EventService.Server import Pyro.configuration import cPickle as pickle import sys if sys.version_info < (2,5): raise SystemExit("this test only runs on Python 2.5 or newer") # a list of all classes to be tested for pickleability classes=[ Pyro.configuration.Config, Pyro.core.ObjBase, Pyro.core.CallbackObjBase, Pyro.core.PyroURI, Pyro.core.DynamicProxy, Pyro.core.DynamicProxyWithAttrs, Pyro.errors.PyroError, Pyro.errors.NamingError, Pyro.errors.PyroExceptionCapsule, Pyro.naming.NameServerLocator, Pyro.naming.NameServerProxy, Pyro.protocol.PYROAdapter, # Pyro.protocol.PYROSSLAdapter, Pyro.protocol.DefaultConnValidator, Pyro.protocol.BasicSSLValidator, Pyro.protocol.LocalStorage, Pyro.EventService.Server.Event, Pyro.EventService.Server.EventService, ] # various helper constructors follow def createConfig(clazz): c=clazz() c.setup(None) return c def createNameserverProxy(clazz): locator=Pyro.naming.NameServerLocator(identification="identify") ns=locator.getNS() if isinstance(ns,clazz): return ns raise TypeError("wrong class") def createPyroAdapter(clazz): a=clazz() a.setOneway(["method"]) a.setTimeout(42) a.setIdentification("identify") return a # A table of object constructors for various types. # If a type isn't present, its base type is tried. constructors={ object: lambda c: c(), str: lambda c: c("test"), int: lambda c: c(42), dict: lambda c: c({"test":42}), Exception: lambda c: c("errormessage"), Pyro.configuration.Config: createConfig, Pyro.core.PyroURI: lambda c: c("PYRO://127.0.0.1:9999/objectid"), Pyro.core.DynamicProxy: lambda c: c("PYRO://127.0.0.1:9999/objectid"), Pyro.errors.PyroExceptionCapsule: lambda c: c(Exception("errormessage"),"some error"), Pyro.naming.NameServerProxy: createNameserverProxy, Pyro.naming.NameServerLocator: lambda c: c(identification="identify"), Pyro.EventService.Server.Event: lambda c: c("subject","message"), Pyro.protocol.PYROAdapter: createPyroAdapter, } # check if objects are the same according to various attributes def compareObjects(obj1,obj2): if type(obj1) is type(obj2): t=type(obj1) if t in (str,int,dict,tuple): return obj1==obj2 if t in (Pyro.configuration.Config, Pyro.core.PyroURI): return obj1==obj2 if t in (Pyro.core.ObjBase, Pyro.core.CallbackObjBase): return obj1.objectGUID==obj2.objectGUID and \ obj1.delegate==obj2.delegate and \ obj1.lastUsed==obj2.lastUsed if isinstance(obj1, Exception): return obj1.args==obj2.args if t is Pyro.errors.PyroExceptionCapsule or obj1.__class__==Pyro.errors.PyroExceptionCapsule: return type(obj1.excObj)==type(obj2.excObj) and \ obj1.__class__==obj2.__class__ and\ obj1.excObj.args==obj2.excObj.args and \ obj1.args==obj2.args if t is Pyro.naming.NameServerLocator: return obj1.identification==obj2.identification if t in (Pyro.naming.NameServerProxy,Pyro.core.DynamicProxy,Pyro.core.DynamicProxyWithAttrs): return obj1.URI==obj2.URI and \ obj1.objectID==obj2.objectID and \ compareObjects(obj1.adapter,obj2.adapter) if t is Pyro.protocol.PYROAdapter: return obj1.ident==obj2.ident and \ obj1.timeout==obj2.timeout and \ obj1.onewayMethods==obj2.onewayMethods if t in (Pyro.protocol.LocalStorage, Pyro.protocol.DefaultConnValidator, Pyro.protocol.BasicSSLValidator, Pyro.EventService.Server.Event, Pyro.EventService.Server.EventService, Pyro.EventService.Clients.Publisher): return obj1.__dict__==obj2.__dict__ print "NO COMPARE FOR TYPE",t return False # Create a list of objects to be tested for pickleability. # An object is created for every class in the class list. def createTestObjects(classes, verbose=False): def createObject(clazz): def findConstructor(clazz): if not clazz: return None,None if clazz in constructors: return constructors[clazz],clazz return findConstructor(clazz.__base__) constr,clazz2=findConstructor(clazz) return constr(clazz),clazz in constructors,clazz2 objects=[] for clazz in classes: obj,specified,clazz2=createObject(clazz) objects.append(obj) if not specified and verbose: print "Warning, no specific constructor for",clazz.__name__,", used:",clazz2.__name__ return objects # Do the pickle test! def pickletest(objects, protocol): print "----------------------------" print "Pickle test with protocol",protocol goodcount=0 for obj in objects: print obj.__class__.__name__,":", try: p=pickle.dumps(obj,protocol=protocol) try: obj2=pickle.loads(p) try: if compareObjects(obj,obj2): print "OK" goodcount+=1 else: print "COMPARE FAIL, DICT=",obj.__dict__ except Exception,x: print "COMPARE FAIL:",x except Exception,x: print "LOADS FAIL:",x except Exception,x: print "DUMPS FAIL:",x print errorcount=len(objects)-goodcount print "FAILURES:",errorcount, " OK:",goodcount print return errorcount # We need to check some special cases more thorougly. def specialcases(protocol): print "SPECIAL CASES" errorcount=0 locator=Pyro.naming.NameServerLocator() ns=locator.getNS() print "PyroAdapter bound to URI:", a=createPyroAdapter(Pyro.protocol.PYROAdapter) assert not hasattr(a,"URI") assert not hasattr(a,"conn") a.bindToURI(ns.URI) assert isinstance(a.conn, Pyro.protocol.TCPConnection) assert isinstance(a.URI, Pyro.core.PyroURI) try: s=pickle.dumps(a,protocol=protocol) a2=pickle.loads(s) assert not hasattr(a,"conn"), "original cannot have conn because of release" assert not hasattr(a2,"conn"), "pickled obj cannot have conn" if compareObjects(a,a2): print "OK" else: errorcount+=1 print "COMPARE FAIL, DICT=",a.__dict__ except Exception,x: errorcount+=1 print "ERROR",x print "DynamicProxy bound to URI:", p=Pyro.core.getProxyForURI(ns.URI) assert not hasattr(p.adapter,"conn") p.ping() assert isinstance(p.adapter.conn, Pyro.protocol.TCPConnection) try: s=pickle.dumps(p,protocol=protocol) p2=pickle.loads(s) assert not hasattr(p2.adapter,"conn"), "pickled obj cannot have conn" assert p.adapter is not None, "original must still have conn" if compareObjects(p,p2): print "OK" else: errorcount+=1 print "COMPARE FAIL, DICT=",p.__dict__ except Exception,x: errorcount+=1 print "ERROR",x print "DynamicAttrProxy bound to URI:", p=Pyro.core.getAttrProxyForURI(ns.URI) assert not hasattr(p.adapter,"conn") p.ping() assert isinstance(p.adapter.conn, Pyro.protocol.TCPConnection) try: s=pickle.dumps(p,protocol=protocol) p2=pickle.loads(s) assert not hasattr(p2.adapter,"conn"), "pickled obj cannot have conn" assert p.adapter is not None, "original must still have conn" if compareObjects(p,p2): print "OK" else: errorcount+=1 print "COMPARE FAIL, DICT=",p.__dict__ except Exception,x: errorcount+=1 print "ERROR",x return errorcount # let's go def main(): #initNameServer() errorcount=0 objects=createTestObjects(classes, verbose=True) for protocol in range(pickle.HIGHEST_PROTOCOL+1): errorcount+=pickletest(objects,protocol) errorcount+=specialcases(protocol) objects=createTestObjects(classes, verbose=False) # new set of objects for next cycle print print "TOTAL ERRORS:",errorcount # must be zero! if __name__=="__main__": main() Pyro-3.14/examples/pickle/Readme.txt0000644000076500000240000000050411517657601017662 0ustar irmenstaff00000000000000This test contains a quite detailed test of the ability of various key Pyro objects to be pickled. Especially the proxy objects need to work flawlessly. The program repeats the test for every available pickle protocol level. There should be zero errors at the end. The test requires a name server to be up and running. Pyro-3.14/examples/proxysharing/0000755000076500000240000000000011567263340017211 5ustar irmenstaff00000000000000Pyro-3.14/examples/proxysharing/client.py0000644000076500000240000000203011517657602021037 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core, Pyro.naming import threading import time stop=False def myThread(nsproxy, proxy): global stop name=threading.currentThread().getName() try: while not stop: result=nsproxy.list(":test") result=proxy.method("the quick brown fox jumps over the lazy dog") except Exception,x: print "**** Exception in thread %s: {%s} %s" % (name, type(x), x) print "done in thread %s." % name nsproxy = Pyro.naming.NameServerLocator().getNS() proxy = Pyro.core.getAttrProxyForURI("PYRONAME://:test.proxysharing") # now create a handful of threads and give each of them the same two proxy objects threads = [] for i in range(20): thread=threading.Thread(target=myThread, args=(nsproxy, proxy) ) # thread.setDaemon(True) threads.append(thread) print "Starting all threads and running them for 5 seconds." print "They're hammering the name server and the test server using the same proxy objects." print "You should not see any exceptions." for t in threads: t.start() time.sleep(5) print "END!" stop=True Pyro-3.14/examples/proxysharing/Readme.txt0000644000076500000240000000020411517657602021146 0ustar irmenstaff00000000000000This example shows how Pyro deals with sharing proxies in different threads. Due to internal locking you can freely share proxies. Pyro-3.14/examples/proxysharing/server.py0000644000076500000240000000100711517657602021072 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import Pyro.naming from Pyro.errors import NamingError class RemoteObject(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self, arg): return " ~~this is the remote result~~ " ns=Pyro.naming.NameServerLocator().getNS() daemon=Pyro.core.Daemon() daemon.useNameServer(ns) try: ns.createGroup(":test") except NamingError: pass uri=daemon.connect(RemoteObject(),":test.proxysharing") print "Server is ready." daemon.requestLoop() Pyro-3.14/examples/quickstart/0000755000076500000240000000000011567263340016646 5ustar irmenstaff00000000000000Pyro-3.14/examples/quickstart/client.py0000644000076500000240000000030611517657602020500 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.ext import remote print 'getting remote object "quickstart"...' test = remote.get_remote_object('quickstart') print test.method1("Johnny") print test.method2(42) Pyro-3.14/examples/quickstart/object.py0000644000076500000240000000037311517657602020474 0ustar irmenstaff00000000000000 class myObject(object): def method1(s, string): return "Your string length is: "+str(len(string)) def method2(s, number): return "The square of your number is: "+str(number*number) def remote_objects(): return { 'quickstart': myObject() } Pyro-3.14/examples/quickstart/pyrorun0000644000076500000240000001101711517657602020312 0ustar irmenstaff00000000000000#!/usr/bin/env python # by John Wiegley # # This script can be used to quickly and easily export objects, # without tying them to Pyro at all. It only requires a special # global function, named `remote_objects', which is used both to # construct the objects, and name them for the nameserver. # # NOTE: If your module must wait on a socket for input, you should # start that code in a thread, so that `pyrorun' occupies the main # thread. Be sure also to make your thread as a daemon! Otherwise, # the module won't shut down. Here's an example: # # from threading import Thread # # def remote_objects(): # t = Thread(target = main) # t.setDaemon(1) # t.start() # return { 'my_object': my_object() } import os import os.path import string import sys import time import types import Pyro.ext.remote as remote import Pyro.util Pyro.config.PYRO_MULTITHREADED = 0 # Default to simple, single-thraeded from getopt import getopt (opts, args) = getopt(sys.argv[1:], [], longopts = [ "this-host=", "this-port=", "nameserver=", "ns-port=", "verbose", "help", "multithreaded", "compress" ]) true, false = 1, 0 this_host = '' this_port = None nameserver = None ns_port = None verbose = false def usage(): print """usage: pyrorun [options] options are: --help Show this usage screen --verbose Be verbose about object remoting --this-host HOST Externally visible name for this host --this-port PORT The port that the objects are available on --nameserver HOST Use HOST as the Pyro nameserver --ns-port PORT Use PORT for the nameserver's port --multithreaded Enable Pyro's multi-threaded server code --compressed Enable compressed message transmission (Note: This is required on both sides) Every one of MODULES will be loaded, and a function named `remote_objects' within that module called. This function should return a dictionary identifying the objects to be published. For example: class Foo: pass class Bar: pass class Baz: pass def remote_objects(): return { 'foo': Foo(), 'bar': Bar(), 'baz': Baz() }""" sys.exit(0) for opt in opts: if opt[0] == "--this-host": this_host = opt[1] elif opt[0] == "--this-port": this_port = int(opt[1]) elif opt[0] == "--nameserver": nameserver = opt[1] elif opt[0] == "--ns-port": ns_port = int(opt[1]) elif opt[0] == "--verbose": verbose = remote.verbose = true elif opt[0] == "--multithreaded": Pyro.config.PYRO_MULTITHREADED = true elif opt[0] == "--compress": Pyro.config.PYRO_COMPRESSED = true elif opt[0] == "--help": usage() if len(args) == 0: usage() class AttrToDict: def __init__(self, dict): self.dict = dict def __hasattr__(self, item): return self.dict.has_key(item) def __getattr__(self, item): return self.dict[item] remote_ready = false remote.daemon_host = this_host remote.daemon_port = this_port for module in args: try: if os.path.isfile(module): globs = {} execfile(module, globs) mod = AttrToDict(globs) else: mod = __import__(module) components = module.split('.') for comp in components[1:]: mod = getattr(mod, comp) except: sys.stderr.write("Failed to import %s: %s\n" % (module, sys.exc_value)) else: if hasattr(mod, 'remote_objects'): dict = mod.remote_objects() if type(dict) is not types.DictType: sys.stderr.write("`remote_objects' in %s does not " "return a dictionary\n" % module) else: for name, obj in dict.items(): if verbose: print "Remoting", name remote.provide_local_object(obj, name, nameserver, ns_port) remote_ready = true else: sys.stderr.write("%s has no global function " "named `remote_objects'\n" % module) if remote_ready: if verbose: print "Waiting for requests..." sys.exit(remote.handle_requests()) else: sys.stderr.write("There were no objects to remote!\n") sys.exit(1) Pyro-3.14/examples/quickstart/Readme.txt0000644000076500000240000000314611517657602020613 0ustar irmenstaff00000000000000This is a Pyro Quickstart example. Using the supplied "remote.py" module, it becomes extremely easy to set up a distributed object system using Pyro. (The module is in the extensions package: Pyro.ext.remote) But, using the "pyrorun" script, it is even easier to provide Python objects as remote Pyro objects; you don't have to write a single line of server code. Great for testing. Type $ pyrorun to see a short help text, and Type $ pyrorun --verbose object to load the object.py module and to start a Pyro server for the "quickstart" object (myObject). This is exactly the same object as server.py uses. In both cases, client.py is the client that accesses this remote object. The remote.py module was kindly provided by John Wiegley. It requires the "signal" module, which is not available on certain systems. To create a server, and remote an object from it, use this snippet: import sys from Pyro.ext import remote remote.daemon_port = port remote.verbose = verbose remote.provide_local_object(MyObject(), 'my_object') sys.exit(remote.handle_requests()) This will look for a nameserver by broadcast. Optionally, the keyword argument 'nameserver' can be passed to 'provide_local_object'. To get a proxy for 'my_object' from a client package: from Pyro.ext import remote my_object = remote.get_remote_object('my_object') [note: remote.py will first unregister any previous occurence of a name in the NameServer, and then register the new name. Existing bindings will be silently overwritten. ] See also the quickstart-noNS example, which doesn't use the Name Server at all. Pyro-3.14/examples/quickstart/server.py0000644000076500000240000000076511517657602020541 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys from Pyro.ext import remote import object # if you don't like the objects to be registered in the Default namespace # uncomment the following two lines, and use the 'nsc' tool to create # the appropriate name group: # import Pyro # Pyro.config.PYRO_NS_DEFAULTGROUP=":mygroup" print 'Providing local object as "quickstart"...' remote.provide_local_object(object.myObject(), 'quickstart') print 'Waiting for requests.' sys.exit(remote.handle_requests(wait_time=2)) Pyro-3.14/examples/quickstart-noNS/0000755000076500000240000000000011567263340017521 5ustar irmenstaff00000000000000Pyro-3.14/examples/quickstart-noNS/client.py0000644000076500000240000000035411517657603021357 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.ext import remote_nons print 'getting remote object "quickstart"...' test = remote_nons.get_server_object('quickstart','localhost', 9123) print test.method1("Johnny") print test.method2(42) Pyro-3.14/examples/quickstart-noNS/object.py0000644000076500000240000000042111517657603021342 0ustar irmenstaff00000000000000PYRO_PORT=9123 class myObject(object): def method1(s, string): return "Your string length is: "+str(len(string)) def method2(s, number): return "The square of your number is: "+str(number*number) def remote_objects(): return { 'quickstart': myObject() } Pyro-3.14/examples/quickstart-noNS/Readme.txt0000644000076500000240000000046411517657603021467 0ustar irmenstaff00000000000000This is a Pyro Quickstart example that works without the Name Server. It is adapted from the "quickstart" example (that uses the NS). It used the "Pyro.ext.remote_nons" module. For more information, see the source code and the "quickstart" example. (but don't pay attention to all Name Server references). Pyro-3.14/examples/quickstart-noNS/server.py0000644000076500000240000000113311517657603021403 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys from Pyro.ext import remote_nons import object # if you don't like the objects to be registered in the Default namespace # uncomment the following two lines, and use the 'nsc' tool to create # the appropriate name group: # import Pyro # Pyro.config.PYRO_NS_DEFAULTGROUP=":mygroup" print 'Providing local object as "quickstart"...' remote_nons.provide_server_object(object.myObject(), 'quickstart', 'localhost', object.PYRO_PORT) print 'Waiting for requests.' sys.exit(remote_nons.handle_requests(wait_time=2)) Pyro-3.14/examples/Readme.txt0000644000076500000240000000751311517657604016425 0ustar irmenstaff00000000000000This directory contains some assorted unstructured test programs. Just examine the source to get enlightened. Read on for usage tips. AllInOne - shows a single application with NS, ES, servers, clients agent2 - a true mobile agent application (code downloading) agent3 - another mobile agent example where the agent travels to other servers attributes - uses the attribute access feature authenticate - shows how connection authentication works autoreconnect - shows the auto reconnect/rebind feature BankExample - a simple electronic banking example Bank2 - a more advanced bank example benchmark - Pyro benchmark callback - shows callbacks from server to client, and oneway call chatbox-ES - a chat server and client using the Event Server chatbox-non-ES - a chat server and client not using the Event Server circle - shows circular and conversation communication. countingcars - a more advanced Event Service example denyhosts - shows how to use a custom newConnectionValidator, to block or grant connection access to certain hosts. disconnect - shows the different ways in which you can disconnect objects from the daemon distributed-computing - a computational task (md5 cracking or merge sorting) is distributed among concurrently operating processors (push-style). Uses ES for progress monitoring distributed-computing2 - a computational task (prime factorization) is distributed among concurrently operating processors (pull-style). exceptions - remote exceptions test factory - uses the object creation feature filetransfer - example of a simple ftp-like file transfer mechanism using Pyro. hugetransfer - test transfer of huge data structures inheritance - inheritance of remote objects logging - test the logging facility maxclients - example to test limit on simultaneous connections multithread - shows need of multithreading server mobilehierarchy - shows that mobile code can process a hierarchy of needed modules naming - shows naming functions NS_sec_plugins - shows the connection validator plugin feature of the NS noNS - how to use Pyro without a Name Server (not recommended) oneway - shows how oneway calls work in the background pickle - check pickleability of various Pyro objects. proxysharing - shows the sharing of proxies over different threads quickstart - shows the use of the Pyro.ext.remote module which makes using Pyro extremely easy, and a 'pyrorun' script which makes it even easier (no more server code needed!) quickstart-noNS - like "quickstart" but doesn't use the Name Server sessions - show use of TLS and user-session resource objects. simple - a simple generic test ssl - shows how to use the Secure Socket Layer (SSL) features stockquotes - shows the use of the Event Service for stock quotes stresstest - stress testing for Pyro, the NS, and the ES. testmobile - various tests of the mobile code features timeout - shows socket timeout handling tlstest - test program to check if TLS is being initialized properly (including COM) threadmobile - technical multithreading test of Pyro's mobile code import logic user_passwd_auth - shows how to do username + password type connection validation. testserver.py is the base implementation for most of the test servers. testclient.py is the base implementation for most of the test clients. Examples that use very specific features have their own server or client code, they don't use the testserver/testclient. If your network doesn't support broadcast lookup of the Pyro NameServer, please set the environment variable PYRO_NS_HOSTNAME to the name of the host that the NS is running on. Pyro-3.14/examples/sessions/0000755000076500000240000000000011567263340016322 5ustar irmenstaff00000000000000Pyro-3.14/examples/sessions/auth_client.py0000644000076500000240000000302311517657601021173 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core, Pyro.protocol import threading, time, sys, copy # worker thread code def processing(username, proxy): print "Processing started for user ",username time.sleep(0.5) proxy.init() for i in range(30): sys.stdout.write(username+" ") sys.stdout.flush() proxy.addline("little text line") time.sleep(0.1) print "Stop processing for user "+username proxy.close() # the Connection Validator, client side # This is only an example, don't use it like this in your own code! class SimpleClientsideConnValidator(Pyro.protocol.DefaultConnValidator): def createAuthToken(self, authid, challenge, peeraddr, URI, daemon): # make a single string out of the ident tuple return "%s:%s" % (authid[0], authid[1]) def mungeIdent(self, ident): # for simplicity's sake we just return the ident verbatim (username, password) return (ident[0], ident[1]) # start a set of threads, 1 per user, which perform requests users=["peter","nancy","wendy","vince","steve"] password = "secretpassw0rd" storageProxy = Pyro.core.getProxyForURI("PYRONAME://:test.datastorage_auth") storageProxy._setNewConnectionValidator( SimpleClientsideConnValidator() ) for username in users: # every thread needs its own user identification! # so make a copy of the proxy and set the identification token. proxy = copy.copy(storageProxy) proxy._setIdentification( (username, password) ) thread = threading.Thread(target=processing, args=(username, proxy)) thread.daemon=False thread.start() print "Wait for threads to finish." Pyro-3.14/examples/sessions/auth_server.py0000644000076500000240000000430011517657601021222 0ustar irmenstaff00000000000000import Pyro.core import Pyro.naming import Pyro.protocol import sys # Server based on using TLS to store session data. # It uses client authentication to get the user name for the session. print """ This is the storage server that depends on TLS and multithreading. It uses client connection authentication to identify the client that belongs to the connection, instead of simply storing a client identifier that is passed in via a remote method call.""" # The datastore. # It will store lines of text in a file named after the 'user'. # The resource that is owned by this user session (the file handle) is stored on the TLS. class DataStoreAuth(Pyro.core.ObjBase): def init(self): # use the username set on the connection object (by the ConnValidator) tls=self.getLocalStorage() tls.username=tls.caller.username tls.datastore=open("datastorage_%s.txt" % tls.username,"w") def addline(self, textline): tls=self.getLocalStorage() sys.stdout.write("adding line to "+tls.datastore.name+"\n") sys.stdout.flush() tls.datastore.write(textline+" | user="+tls.username+" | came from "+str(tls.caller)+"\n") def close(self): tls=self.getLocalStorage() tls.datastore.close() # The Connection Validator, server side # This is only an example, don't use it like this in your own code! class SimpleServersideConnValidator(Pyro.protocol.DefaultConnValidator): def acceptIdentification(self, daemon, connection, token, challenge): # The token will be the username:password string, received from the client. login, password = token.split(':', 1) if password!="secretpassw0rd": return (0,Pyro.constants.DENIED_SECURITY) # We store the login name on the connection object to refer to it later. connection.username=login return (1,0) daemon=Pyro.core.Daemon() ns=Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) daemon.setNewConnectionValidator( SimpleServersideConnValidator() ) try: ns.createGroup(":test") except Exception: pass try: ns.unregister(":test.datastorage_auth") except Exception: pass daemon.connect(DataStoreAuth(), ":test.datastorage_auth") print "Server (auth) is running." daemon.requestLoop() Pyro-3.14/examples/sessions/basic_client.py0000644000076500000240000000167211517657601021323 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import threading, time NUMTHREADS=5 test = Pyro.core.getProxyForURI("PYRONAME://:test.threadstorage") print "Will reuse the proxy in all threads." print "Observe in the server console that the TLS counter is the same counter for all calls. " def processing(index, proxy): print 'Processing started',index while threading.currentThread().running: t1 = time.time() print index, "CALLING...." proxy.process("thread_"+str(index)) time.sleep(NUMTHREADS+1) print "exiting thread",index # start a set of threads which perform requests threads=[] for i in range(NUMTHREADS): thread = threading.Thread(target=processing, args=(i, test)) threads.append(thread) thread.running=True time.sleep(0.5) thread.start() void=raw_input('\nPress enter to stop...\n\n') print "Stopping threads." for p in threads: p.running=False for p in threads: p.join() print 'stopped',p.getName() print 'Graceful exit.' Pyro-3.14/examples/sessions/basic_client2.py0000644000076500000240000000201111517657601021371 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import threading, time, copy NUMTHREADS=5 testProxy = Pyro.core.getProxyForURI("PYRONAME://:test.threadstorage") print "Will make copy of the proxy for every thread." print "Observe in the server console that the TLS counter is now unique per proxy." def processing(index, proxy): print 'Processing started',index while threading.currentThread().running: t1 = time.time() print index, "CALLING...." proxy.process("thread_"+str(index)) time.sleep(NUMTHREADS+1) print "exiting thread",index # start a set of threads which perform requests threads=[] for i in range(NUMTHREADS): proxy=copy.copy(testProxy) thread = threading.Thread(target=processing, args=(i, proxy)) threads.append(thread) thread.running=True time.sleep(0.5) thread.start() void=raw_input('\nPress enter to stop...\n\n') print "Stopping threads." for p in threads: p.running=False for p in threads: p.join() print 'stopped',p.getName() print 'Graceful exit.' Pyro-3.14/examples/sessions/basic_server.py0000644000076500000240000000156411517657601021353 0ustar irmenstaff00000000000000import Pyro.core import Pyro.naming def initTLSfunc(tls): print "initializig TLS",repr(tls) # we create a counter attribute and set it to 0 initially, # so that all worker invocations can just increase the counter. tls.counter=0 class Worker(Pyro.core.ObjBase): def process(self, clientThreadName): print "got a call from client thread",clientThreadName tls=self.getLocalStorage() tls.counter+=1 print " TLS.counter=%d (tls=%r)" % (tls.counter,tls) print " caller=",tls.caller daemon=Pyro.core.Daemon() ns=Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) daemon.setInitTLS(initTLSfunc) try: ns.createGroup(":test") except Exception: pass try: ns.unregister(":test.threadstorage") except Exception: pass daemon.connect(Worker(), ":test.threadstorage") print "Server running." daemon.requestLoop() Pyro-3.14/examples/sessions/Readme.txt0000644000076500000240000000111511517657601020260 0ustar irmenstaff00000000000000Some examples to show the use of the Thread Local Storage, its caller attribute, and the use of user-session specific resource objects. There is a basic example to show TLS usage: basic_server, basic_client/basic_client2. There is an example to show use of user-session resource objects (in this example only simple data files where a user can append lines to): storage_server_caller/storage_server_tls, storage_client. There is also an example to show how you might make use of the connection authentication mechanism to obtain session user data (username): auth_server, auth_client. Pyro-3.14/examples/sessions/storage_client.py0000644000076500000240000000131311517657601021676 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import threading, time, sys def processing(username): print "Processing started for user ",username time.sleep(0.5) data = Pyro.core.getProxyForURI("PYRONAME://:test.datastorage") data.init(username) for i in range(30): sys.stdout.write(username+" ") sys.stdout.flush() data.addline("line from user "+username) time.sleep(0.1) print "Stop processing for user "+username data.close() # start a set of threads, 1 per user, which perform requests users=["peter","nancy","wendy","vince","steve"] for username in users: thread = threading.Thread(target=processing, args=(username, )) thread.daemon=False thread.start() print "Wait for threads to finish." Pyro-3.14/examples/sessions/storage_server_caller.py0000644000076500000240000000315711517657601023260 0ustar irmenstaff00000000000000import Pyro.core import Pyro.naming import sys # server based on using caller object on the TLS to store session data print """ This is the storage server that depends on the caller object in the TLS to keep track of what the resource is for that given session. Because that object is always equal to the current active client connection, it will work with or without multithreading enabled. You can check this by looking at the output on the screen and the contents of the datafiles.""" print Pyro.config.PYRO_MULTITHREADED=raw_input("Enable multithreading y/n? ") in ('y','Y') # The datastore. # It will store lines of text in a file named after the 'user'. # The resource that is owned by this user session (the file handle) is stored # on the caller object on the TLS. class DataStore(Pyro.core.ObjBase): def init(self, username): caller=self.getLocalStorage().caller caller.datastore=open("datastorage_%s.txt"%username,"w") def addline(self, textline): caller=self.getLocalStorage().caller sys.stdout.write("adding line to "+caller.datastore.name+"\n") sys.stdout.flush() caller.datastore.write(textline+" | came from "+str(caller)+"\n") def close(self): caller=self.getLocalStorage().caller caller.datastore.close() daemon=Pyro.core.Daemon() ns=Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) try: ns.createGroup(":test") except Exception: pass try: ns.unregister(":test.datastorage") except Exception: pass daemon.connect(DataStore(), ":test.datastorage") print "Server (caller version) is running." daemon.requestLoop() Pyro-3.14/examples/sessions/storage_server_tls.py0000644000076500000240000000366611517657601022625 0ustar irmenstaff00000000000000import Pyro.core import Pyro.naming import sys # server based on using TLS to store session data print """ This is the storage server that depends on Thread Local Storage to keep track of what the resource is for that given session. TLS only works for this if the server runs with real threads (so every connection/session has its own distinct TLS). If you disable multithreading, TLS will overlap for all sessions and resources get mixed up. You can check this by looking at the output on the screen and the contents of the datafiles.""" print """ If running with multithreading you will, after running the storage_client, end up with a few datafiles: one for every user, and only that user's lines in it. If not using multithreading things will break: almost all lines (from all users) end up in a single datafile and errors might occur because wrong stuff is closed.""" print Pyro.config.PYRO_MULTITHREADED=raw_input("Enable multithreading y/n? ") in ('y','Y') # The datastore. # It will store lines of text in a file named after the 'user'. # The resource that is owned by this user session (the file handle) is stored on the TLS. class DataStore(Pyro.core.ObjBase): def init(self, username): tls=self.getLocalStorage() tls.datastore=open("datastorage_%s.txt"%username,"w") def addline(self, textline): tls=self.getLocalStorage() sys.stdout.write("adding line to "+tls.datastore.name+"\n") sys.stdout.flush() tls.datastore.write(textline+" | came from "+str(tls.caller)+"\n") def close(self): tls=self.getLocalStorage() tls.datastore.close() daemon=Pyro.core.Daemon() ns=Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) try: ns.createGroup(":test") except Exception: pass try: ns.unregister(":test.datastorage") except Exception: pass daemon.connect(DataStore(), ":test.datastorage") print "Server (TLS version) is running." daemon.requestLoop() Pyro-3.14/examples/simple/0000755000076500000240000000000011567263340015745 5ustar irmenstaff00000000000000Pyro-3.14/examples/simple/client.py0000644000076500000240000000067011517657600017601 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.util import Pyro.core Pyro.core.initClient() test = Pyro.core.getProxyForURI("PYRONAME://:test.simple") 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 '*** invoking server method that crashes ***' try: print test.error() except Exception,x: print "".join(Pyro.util.getPyroTraceback(x)) Pyro-3.14/examples/simple/Readme.txt0000644000076500000240000000104511517657600017704 0ustar irmenstaff00000000000000This is some kind of hello-world test. There is a simple object, a server and a client which calls the methods of the object. Nothing fancy, just to check if PYRO works. Also this is a nice starting point for your own Pyro projects. HOW TO START THIS: 1) start a Pyro Name Server using the 'ns' command. 2) in a different console window, start the server using 'python server.py'. 3) in a third console window, run the client using 'python client.py'. Have a look at the "example" chapter in the manual to see other very simple Pyro examples. Pyro-3.14/examples/simple/server.py0000644000076500000240000000117411517657600017631 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import Pyro.naming from Pyro.errors import NamingError import tst # The testclass object. # make a Pyro object from our regular class. # (note that this can also be done using delegation) class testclass(Pyro.core.ObjBase, tst.testclass): def __init__(self): Pyro.core.ObjBase.__init__(self) ######## main program Pyro.core.initServer() ns=Pyro.naming.NameServerLocator().getNS() daemon=Pyro.core.Daemon() daemon.useNameServer(ns) try: ns.createGroup(":test") except NamingError: pass uri=daemon.connect(testclass(),":test.simple") print "Server is ready." daemon.requestLoop() Pyro-3.14/examples/simple/tst.py0000644000076500000240000000063411517657600017135 0ustar irmenstaff00000000000000 # These classes will be remotely accessed. class testclass(object): def mul(s, arg1, arg2): return arg1*arg2 def add(s, arg1, arg2): return arg1+arg2 def sub(s, arg1, arg2): return arg1-arg2 def div(s, arg1, arg2): return arg1/arg2 def error(s): x=foo() x.crash() class foo(object): def crash(s): s.crash2('going down...') def crash2(s, arg): # this statement will crash on purpose: x=arg/2 Pyro-3.14/examples/ssl/0000755000076500000240000000000011567263340015255 5ustar irmenstaff00000000000000Pyro-3.14/examples/ssl/certs/0000755000076500000240000000000011567263340016375 5ustar irmenstaff00000000000000Pyro-3.14/examples/ssl/certs/ca.pem0000644000076500000240000000305211517657602017466 0ustar irmenstaff00000000000000-----BEGIN CERTIFICATE----- MIIEYTCCA0mgAwIBAgIJAI0ayJqYcQ+xMA0GCSqGSIb3DQEBBQUAMH0xEDAOBgNV BAMTB25lcHR1bmUxCzAJBgNVBAYTAk5MMRAwDgYDVQQIEwdVdHJlY2h0MRAwDgYD VQQHEwdXb2VyZGVuMREwDwYDVQQKEwhQeXJvIENvLjElMCMGCSqGSIb3DQEJARYW cHlyby1jYUBkb21haW4uaW52YWxpZDAeFw0wOTA3MTExOTA0MThaFw0xNDA3MTAx OTA0MThaMH0xEDAOBgNVBAMTB25lcHR1bmUxCzAJBgNVBAYTAk5MMRAwDgYDVQQI EwdVdHJlY2h0MRAwDgYDVQQHEwdXb2VyZGVuMREwDwYDVQQKEwhQeXJvIENvLjEl MCMGCSqGSIb3DQEJARYWcHlyby1jYUBkb21haW4uaW52YWxpZDCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAPCv5RPpK2AC1MnEoREcEd8z0FqS1wO/dXy2 CxhJsTssOFwcrO6byuf2X2c9Cql/YAYzxcg2x+w5ORTWQFxnL9Alw2U+k9a7JoEt saWsDEuEJOod35C4RMrCKmJjyrXoU8yinncnyeaxh8PdR1y0HKDYWV9gRlv4o3yl ULCSBB7v0kHa6pJrfIUua06Lw6wNVXJ3OBoxz2WacjxIhylQI2G1ULQsSoyfTd31 2Be/VoskVWHuBlYfGXtbJye8QEIHtDw4F6+7Nuf0ARPwPiWwa1B+lJ2oVidrRN5I OhCj0KY9cWjOoiZnferGgmMWJ9prc+ju7lxG23wwGE9e5dvIpvUCAwEAAaOB4zCB 4DAdBgNVHQ4EFgQUNa+zSggyCWUGyu8eb1vMUSDtig8wgbAGA1UdIwSBqDCBpYAU Na+zSggyCWUGyu8eb1vMUSDtig+hgYGkfzB9MRAwDgYDVQQDEwduZXB0dW5lMQsw CQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHV29lcmRlbjER MA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG9w0BCQEWFnB5cm8tY2FAZG9tYWlu LmludmFsaWSCCQCNGsiamHEPsTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA A4IBAQDTRpEB+NupID/uMMstjDxnHyUO4ZEnCgFgPLWO2AUNF6Gn4u1rHhMMt3l4 YfRWpQXy6TJ1/c5F5IAuq0y12YcfMAWRHW2c4w5u0YrFO3iG+d6Gc1x1tGBT41w2 16AGqu2j/QA0pNSwhcMzLCVOsBSwXU7MyBEvtVH9UK8v+uCc9cwR+JpEPC8O72Ob 11csIyw0ygJ1Ha4p90KnEmp2N3rDOKocrsXCeKRwWy9W0DM6Gn0nJct/VB7/u6SW HcaeL6szCRs7s2N5WV2r35XDGsLy26k+EbhBmop8o8XdfArrzEsQp8vQYsKQmx8f 1YmqsKutK59VyZgxDGft8IpGfIdo -----END CERTIFICATE----- Pyro-3.14/examples/ssl/certs/client.key0000644000076500000240000000156711517657602020401 0ustar irmenstaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC9KTPPVslJcJqIKW/KG3PYkqEPQcPjO8lPUKfyyMZtav4JCaB2 Q5RStt33FrYC478PLe77OrXkIAhZOpfV5O+ePi0DAsR3bXGhWsIKnS4VPyIuhe4+ IPsoLA9+F71Pc+1JhCKuGOEHg9TvfHnZK1iiccCVbCoQoD8ZQqH7gV0OSQIDAQAB AoGBAKgzUfmE/zlJYm+/AMNjQ9CJGaP6Y5fUGr5nRt/wvdifscIRiFiA4QvnxVmo HTJWlxaFTqKgYwq1nRbSrh6pEpNGhCvi5hjuCGJNPIHhsu2Iv9rHk35XgDlEn1P5 AoK8G1+gnzsQX8feUnCbl2FYGuXDg/3pjtSSVIe7rN07PFNxAkEA9hEV/sBitGZc C7/xpGhqgvJjO57t9TZiqzQFF5vau8N11ZBGz/dgtZN7E2tsRLGoa8mEWRIGpWa2 zqWST7A5xQJBAMTMCbtwmeG9M6+LX237TdNIf3CARMsQVxz8K9hrqbsgzy3c8LUP M5qosbANFNile86mZHWclTtTIeyFm6WUvrUCQEwNu96hV+CqQojfKrDGFSWkBUfI N4Qy6ww/Z9OEOHcQUrtPfJOKwjlc/A1khnI09vlZr65YA/XbaWU1h6NZtIECQFP2 XZycEPtP1tJpm0B4PJ8NSln8T913TkNuj2JZpOPSvrHplsUZBEJTAOSE4EUCbY0g TrIjKURTVqDr7Bk8GhUCQFmEr+p5twVeyDecv1Oj8VU0NZkniCNl5KVTwORicMO0 PnI8aPdPWA4j0CHbov5MgWB2JQ9MeR8eIkeS12jKgOk= -----END RSA PRIVATE KEY----- Pyro-3.14/examples/ssl/certs/client.pem0000644000076500000240000001177011517657603020370 0ustar irmenstaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC9KTPPVslJcJqIKW/KG3PYkqEPQcPjO8lPUKfyyMZtav4JCaB2 Q5RStt33FrYC478PLe77OrXkIAhZOpfV5O+ePi0DAsR3bXGhWsIKnS4VPyIuhe4+ IPsoLA9+F71Pc+1JhCKuGOEHg9TvfHnZK1iiccCVbCoQoD8ZQqH7gV0OSQIDAQAB AoGBAKgzUfmE/zlJYm+/AMNjQ9CJGaP6Y5fUGr5nRt/wvdifscIRiFiA4QvnxVmo HTJWlxaFTqKgYwq1nRbSrh6pEpNGhCvi5hjuCGJNPIHhsu2Iv9rHk35XgDlEn1P5 AoK8G1+gnzsQX8feUnCbl2FYGuXDg/3pjtSSVIe7rN07PFNxAkEA9hEV/sBitGZc C7/xpGhqgvJjO57t9TZiqzQFF5vau8N11ZBGz/dgtZN7E2tsRLGoa8mEWRIGpWa2 zqWST7A5xQJBAMTMCbtwmeG9M6+LX237TdNIf3CARMsQVxz8K9hrqbsgzy3c8LUP M5qosbANFNile86mZHWclTtTIeyFm6WUvrUCQEwNu96hV+CqQojfKrDGFSWkBUfI N4Qy6ww/Z9OEOHcQUrtPfJOKwjlc/A1khnI09vlZr65YA/XbaWU1h6NZtIECQFP2 XZycEPtP1tJpm0B4PJ8NSln8T913TkNuj2JZpOPSvrHplsUZBEJTAOSE4EUCbY0g TrIjKURTVqDr7Bk8GhUCQFmEr+p5twVeyDecv1Oj8VU0NZkniCNl5KVTwORicMO0 PnI8aPdPWA4j0CHbov5MgWB2JQ9MeR8eIkeS12jKgOk= -----END RSA PRIVATE KEY----- Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=neptune, C=NL, ST=Utrecht, L=Woerden, O=Pyro Co./emailAddress=pyro-ca@domain.invalid Validity Not Before: Jul 11 19:05:53 2009 GMT Not After : Apr 6 19:05:53 2012 GMT Subject: C=NL, ST=Utrecht, O=Pyro Co., OU=Client, CN=neptune/emailAddress=pyro-client@domain.invalid Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:bd:29:33:cf:56:c9:49:70:9a:88:29:6f:ca:1b: 73:d8:92:a1:0f:41:c3:e3:3b:c9:4f:50:a7:f2:c8: c6:6d:6a:fe:09:09:a0:76:43:94:52:b6:dd:f7:16: b6:02:e3:bf:0f:2d:ee:fb:3a:b5:e4:20:08:59:3a: 97:d5:e4:ef:9e:3e:2d:03:02:c4:77:6d:71:a1:5a: c2:0a:9d:2e:15:3f:22:2e:85:ee:3e:20:fb:28:2c: 0f:7e:17:bd:4f:73:ed:49:84:22:ae:18:e1:07:83: d4:ef:7c:79:d9:2b:58:a2:71:c0:95:6c:2a:10:a0: 3f:19:42:a1:fb:81:5d:0e:49 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: EA:C2:5B:77:F0:F3:91:06:4A:97:16:0C:17:5B:DA:A8:FC:E3:C7:2E X509v3 Authority Key Identifier: keyid:35:AF:B3:4A:08:32:09:65:06:CA:EF:1E:6F:5B:CC:51:20:ED:8A:0F DirName:/CN=neptune/C=NL/ST=Utrecht/L=Woerden/O=Pyro Co./emailAddress=pyro-ca@domain.invalid serial:8D:1A:C8:9A:98:71:0F:B1 Netscape CA Revocation Url: https://www.sial.org/ca-crl.pem Signature Algorithm: sha1WithRSAEncryption 25:e0:13:6e:c7:cf:ac:2b:a8:ee:7e:de:bc:e8:1b:92:8d:32: 26:38:c6:f0:e2:3d:33:91:c8:6e:56:98:bc:7f:33:06:ef:54: 3e:67:46:ce:79:76:d5:dc:e7:20:12:a6:7a:74:6f:d5:e1:7d: 8f:4a:72:cc:fa:02:3d:fc:d5:7d:5d:2d:0b:4c:8e:b1:8e:41: 51:ec:db:48:bc:a1:36:9c:85:2f:2b:8f:2b:59:d3:5d:fb:ee: 89:5d:6c:4b:35:4c:00:81:2d:32:88:19:f1:02:4b:cc:f9:5a: 2e:ad:f3:07:73:81:c8:e7:14:65:3c:70:0d:32:00:b4:21:ef: f4:fa:22:ca:3d:0a:ea:c0:c1:6c:c8:f5:49:a5:bb:21:2d:68: 53:8d:3b:3b:f8:a9:b1:f7:21:94:da:a0:f5:7a:7f:10:d1:74: 36:00:50:5e:04:06:97:3b:7c:98:f5:ad:e2:0d:f9:9f:fd:36: 9b:81:09:6c:c1:9b:b8:0d:27:f0:74:f0:e8:07:12:d5:58:08: 78:05:09:b5:15:64:bb:6b:5d:be:c6:5a:35:da:c4:2a:8a:8b: 62:fc:72:36:c9:ee:d2:b5:45:b9:a0:a6:72:b2:71:80:6d:d3: fc:49:81:00:09:a6:3f:c6:25:4a:32:7b:69:7f:59:48:e8:ab: ef:59:f3:36 -----BEGIN CERTIFICATE----- MIIECDCCAvCgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MRAwDgYDVQQDEwduZXB0 dW5lMQswCQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHV29l cmRlbjERMA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG9w0BCQEWFnB5cm8tY2FA ZG9tYWluLmludmFsaWQwHhcNMDkwNzExMTkwNTUzWhcNMTIwNDA2MTkwNTUzWjCB gDELMAkGA1UEBhMCTkwxEDAOBgNVBAgTB1V0cmVjaHQxETAPBgNVBAoTCFB5cm8g Q28uMQ8wDQYDVQQLEwZDbGllbnQxEDAOBgNVBAMTB25lcHR1bmUxKTAnBgkqhkiG 9w0BCQEWGnB5cm8tY2xpZW50QGRvbWFpbi5pbnZhbGlkMIGfMA0GCSqGSIb3DQEB AQUAA4GNADCBiQKBgQC9KTPPVslJcJqIKW/KG3PYkqEPQcPjO8lPUKfyyMZtav4J CaB2Q5RStt33FrYC478PLe77OrXkIAhZOpfV5O+ePi0DAsR3bXGhWsIKnS4VPyIu he4+IPsoLA9+F71Pc+1JhCKuGOEHg9TvfHnZK1iiccCVbCoQoD8ZQqH7gV0OSQID AQABo4IBETCCAQ0wCQYDVR0TBAIwADAdBgNVHQ4EFgQU6sJbd/DzkQZKlxYMF1va qPzjxy4wgbAGA1UdIwSBqDCBpYAUNa+zSggyCWUGyu8eb1vMUSDtig+hgYGkfzB9 MRAwDgYDVQQDEwduZXB0dW5lMQswCQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNo dDEQMA4GA1UEBxMHV29lcmRlbjERMA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG 9w0BCQEWFnB5cm8tY2FAZG9tYWluLmludmFsaWSCCQCNGsiamHEPsTAuBglghkgB hvhCAQQEIRYfaHR0cHM6Ly93d3cuc2lhbC5vcmcvY2EtY3JsLnBlbTANBgkqhkiG 9w0BAQUFAAOCAQEAJeATbsfPrCuo7n7evOgbko0yJjjG8OI9M5HIblaYvH8zBu9U PmdGznl21dznIBKmenRv1eF9j0pyzPoCPfzVfV0tC0yOsY5BUezbSLyhNpyFLyuP K1nTXfvuiV1sSzVMAIEtMogZ8QJLzPlaLq3zB3OByOcUZTxwDTIAtCHv9Poiyj0K 6sDBbMj1SaW7IS1oU407O/ipsfchlNqg9Xp/ENF0NgBQXgQGlzt8mPWt4g35n/02 m4EJbMGbuA0n8HTw6AcS1VgIeAUJtRVku2tdvsZaNdrEKoqLYvxyNsnu0rVFuaCm crJxgG3T/EmBAAmmP8YlSjJ7aX9ZSOir71nzNg== -----END CERTIFICATE----- Pyro-3.14/examples/ssl/certs/client_nokey.pem0000644000076500000240000001020111517657602021560 0ustar irmenstaff00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=neptune, C=NL, ST=Utrecht, L=Woerden, O=Pyro Co./emailAddress=pyro-ca@domain.invalid Validity Not Before: Jul 11 19:05:53 2009 GMT Not After : Apr 6 19:05:53 2012 GMT Subject: C=NL, ST=Utrecht, O=Pyro Co., OU=Client, CN=neptune/emailAddress=pyro-client@domain.invalid Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:bd:29:33:cf:56:c9:49:70:9a:88:29:6f:ca:1b: 73:d8:92:a1:0f:41:c3:e3:3b:c9:4f:50:a7:f2:c8: c6:6d:6a:fe:09:09:a0:76:43:94:52:b6:dd:f7:16: b6:02:e3:bf:0f:2d:ee:fb:3a:b5:e4:20:08:59:3a: 97:d5:e4:ef:9e:3e:2d:03:02:c4:77:6d:71:a1:5a: c2:0a:9d:2e:15:3f:22:2e:85:ee:3e:20:fb:28:2c: 0f:7e:17:bd:4f:73:ed:49:84:22:ae:18:e1:07:83: d4:ef:7c:79:d9:2b:58:a2:71:c0:95:6c:2a:10:a0: 3f:19:42:a1:fb:81:5d:0e:49 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: EA:C2:5B:77:F0:F3:91:06:4A:97:16:0C:17:5B:DA:A8:FC:E3:C7:2E X509v3 Authority Key Identifier: keyid:35:AF:B3:4A:08:32:09:65:06:CA:EF:1E:6F:5B:CC:51:20:ED:8A:0F DirName:/CN=neptune/C=NL/ST=Utrecht/L=Woerden/O=Pyro Co./emailAddress=pyro-ca@domain.invalid serial:8D:1A:C8:9A:98:71:0F:B1 Netscape CA Revocation Url: https://www.sial.org/ca-crl.pem Signature Algorithm: sha1WithRSAEncryption 25:e0:13:6e:c7:cf:ac:2b:a8:ee:7e:de:bc:e8:1b:92:8d:32: 26:38:c6:f0:e2:3d:33:91:c8:6e:56:98:bc:7f:33:06:ef:54: 3e:67:46:ce:79:76:d5:dc:e7:20:12:a6:7a:74:6f:d5:e1:7d: 8f:4a:72:cc:fa:02:3d:fc:d5:7d:5d:2d:0b:4c:8e:b1:8e:41: 51:ec:db:48:bc:a1:36:9c:85:2f:2b:8f:2b:59:d3:5d:fb:ee: 89:5d:6c:4b:35:4c:00:81:2d:32:88:19:f1:02:4b:cc:f9:5a: 2e:ad:f3:07:73:81:c8:e7:14:65:3c:70:0d:32:00:b4:21:ef: f4:fa:22:ca:3d:0a:ea:c0:c1:6c:c8:f5:49:a5:bb:21:2d:68: 53:8d:3b:3b:f8:a9:b1:f7:21:94:da:a0:f5:7a:7f:10:d1:74: 36:00:50:5e:04:06:97:3b:7c:98:f5:ad:e2:0d:f9:9f:fd:36: 9b:81:09:6c:c1:9b:b8:0d:27:f0:74:f0:e8:07:12:d5:58:08: 78:05:09:b5:15:64:bb:6b:5d:be:c6:5a:35:da:c4:2a:8a:8b: 62:fc:72:36:c9:ee:d2:b5:45:b9:a0:a6:72:b2:71:80:6d:d3: fc:49:81:00:09:a6:3f:c6:25:4a:32:7b:69:7f:59:48:e8:ab: ef:59:f3:36 -----BEGIN CERTIFICATE----- MIIECDCCAvCgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MRAwDgYDVQQDEwduZXB0 dW5lMQswCQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHV29l cmRlbjERMA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG9w0BCQEWFnB5cm8tY2FA ZG9tYWluLmludmFsaWQwHhcNMDkwNzExMTkwNTUzWhcNMTIwNDA2MTkwNTUzWjCB gDELMAkGA1UEBhMCTkwxEDAOBgNVBAgTB1V0cmVjaHQxETAPBgNVBAoTCFB5cm8g Q28uMQ8wDQYDVQQLEwZDbGllbnQxEDAOBgNVBAMTB25lcHR1bmUxKTAnBgkqhkiG 9w0BCQEWGnB5cm8tY2xpZW50QGRvbWFpbi5pbnZhbGlkMIGfMA0GCSqGSIb3DQEB AQUAA4GNADCBiQKBgQC9KTPPVslJcJqIKW/KG3PYkqEPQcPjO8lPUKfyyMZtav4J CaB2Q5RStt33FrYC478PLe77OrXkIAhZOpfV5O+ePi0DAsR3bXGhWsIKnS4VPyIu he4+IPsoLA9+F71Pc+1JhCKuGOEHg9TvfHnZK1iiccCVbCoQoD8ZQqH7gV0OSQID AQABo4IBETCCAQ0wCQYDVR0TBAIwADAdBgNVHQ4EFgQU6sJbd/DzkQZKlxYMF1va qPzjxy4wgbAGA1UdIwSBqDCBpYAUNa+zSggyCWUGyu8eb1vMUSDtig+hgYGkfzB9 MRAwDgYDVQQDEwduZXB0dW5lMQswCQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNo dDEQMA4GA1UEBxMHV29lcmRlbjERMA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG 9w0BCQEWFnB5cm8tY2FAZG9tYWluLmludmFsaWSCCQCNGsiamHEPsTAuBglghkgB hvhCAQQEIRYfaHR0cHM6Ly93d3cuc2lhbC5vcmcvY2EtY3JsLnBlbTANBgkqhkiG 9w0BAQUFAAOCAQEAJeATbsfPrCuo7n7evOgbko0yJjjG8OI9M5HIblaYvH8zBu9U PmdGznl21dznIBKmenRv1eF9j0pyzPoCPfzVfV0tC0yOsY5BUezbSLyhNpyFLyuP K1nTXfvuiV1sSzVMAIEtMogZ8QJLzPlaLq3zB3OByOcUZTxwDTIAtCHv9Poiyj0K 6sDBbMj1SaW7IS1oU407O/ipsfchlNqg9Xp/ENF0NgBQXgQGlzt8mPWt4g35n/02 m4EJbMGbuA0n8HTw6AcS1VgIeAUJtRVku2tdvsZaNdrEKoqLYvxyNsnu0rVFuaCm crJxgG3T/EmBAAmmP8YlSjJ7aX9ZSOir71nzNg== -----END CERTIFICATE----- Pyro-3.14/examples/ssl/certs/server.key0000644000076500000240000000156711517657602020431 0ustar irmenstaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQCyDBc0Y5WFsm2pIWev+AkeTIQqs7m7WyKrU30EpfYWmt/fUdpM EDX1cy0vhKoKWCGMr1wySNqXA81p4xDOawcgo3iK+pTESK/nfUdv0JVZ5RIB3Z/1 zcy8BIq8DkpuMrGb9Q9G3GVO4RsPBGAxqkEij/3fx7XGINcie3rStR0NCQIDAQAB AoGAK2I40UImr+IvSFPkcQNOLXzVg5Yxfo6RXw/D2bsawJ1SSZcnSvt0Om+rJfX8 fPLGkgI0w+fuo3eAuPlkIfgATzKSmf4FMyUEi7spVMpUdUVJ1SpwEUbNMZzbvC2f JCfTIjwN6kJ/O4CfOenyuY8h/7SvTVQ2SnHdZKm+55Eml4UCQQDa2YmYbGWsMZ7V uJYBpCJL2HOUwb5INOYZm/GlGdq41rpwNw0jbOQYBaMMRyTvZGuLuD7DfA1S7xGv tFqocV8DAkEA0EVqLz1uhtMDEYGcJG6SCKeyq7pT8m+8PDBLAZ+9MHnoeTAJ4RaW bKMMLkXIxsvebRU7IYEZARSLGEAXcghQAwJAB3vC0bx7KY000jgSoRG8vn0zTpXl bNCqQZvsgmnE6eclzuqC/4AcuJmQvr28DJeReeCd/M9EIV7davWu1+2wgwJAPZgp 3Sq341vIjobcXNHukmy2JJ+IcCynaM2HgUVyuEF1hVG1ukHp3tshoRW2WUS11WDw CeqaMH2Y3WUnCex3bQJBAKlgz2FVmIpZu3MXFVGHL6++dktlN5XUUwctfGGL3MWV SSix9dt0zgiUsi3Q1uSh6wrUG/NZ9ww16a7xFHeYXmo= -----END RSA PRIVATE KEY----- Pyro-3.14/examples/ssl/certs/server.pem0000644000076500000240000001177011517657602020417 0ustar irmenstaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQCyDBc0Y5WFsm2pIWev+AkeTIQqs7m7WyKrU30EpfYWmt/fUdpM EDX1cy0vhKoKWCGMr1wySNqXA81p4xDOawcgo3iK+pTESK/nfUdv0JVZ5RIB3Z/1 zcy8BIq8DkpuMrGb9Q9G3GVO4RsPBGAxqkEij/3fx7XGINcie3rStR0NCQIDAQAB AoGAK2I40UImr+IvSFPkcQNOLXzVg5Yxfo6RXw/D2bsawJ1SSZcnSvt0Om+rJfX8 fPLGkgI0w+fuo3eAuPlkIfgATzKSmf4FMyUEi7spVMpUdUVJ1SpwEUbNMZzbvC2f JCfTIjwN6kJ/O4CfOenyuY8h/7SvTVQ2SnHdZKm+55Eml4UCQQDa2YmYbGWsMZ7V uJYBpCJL2HOUwb5INOYZm/GlGdq41rpwNw0jbOQYBaMMRyTvZGuLuD7DfA1S7xGv tFqocV8DAkEA0EVqLz1uhtMDEYGcJG6SCKeyq7pT8m+8PDBLAZ+9MHnoeTAJ4RaW bKMMLkXIxsvebRU7IYEZARSLGEAXcghQAwJAB3vC0bx7KY000jgSoRG8vn0zTpXl bNCqQZvsgmnE6eclzuqC/4AcuJmQvr28DJeReeCd/M9EIV7davWu1+2wgwJAPZgp 3Sq341vIjobcXNHukmy2JJ+IcCynaM2HgUVyuEF1hVG1ukHp3tshoRW2WUS11WDw CeqaMH2Y3WUnCex3bQJBAKlgz2FVmIpZu3MXFVGHL6++dktlN5XUUwctfGGL3MWV SSix9dt0zgiUsi3Q1uSh6wrUG/NZ9ww16a7xFHeYXmo= -----END RSA PRIVATE KEY----- Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=neptune, C=NL, ST=Utrecht, L=Woerden, O=Pyro Co./emailAddress=pyro-ca@domain.invalid Validity Not Before: Jul 11 19:05:53 2009 GMT Not After : Apr 6 19:05:53 2012 GMT Subject: C=NL, ST=Utrecht, O=Pyro Co., OU=Server, CN=neptune/emailAddress=pyro-server@domain.invalid Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:b2:0c:17:34:63:95:85:b2:6d:a9:21:67:af:f8: 09:1e:4c:84:2a:b3:b9:bb:5b:22:ab:53:7d:04:a5: f6:16:9a:df:df:51:da:4c:10:35:f5:73:2d:2f:84: aa:0a:58:21:8c:af:5c:32:48:da:97:03:cd:69:e3: 10:ce:6b:07:20:a3:78:8a:fa:94:c4:48:af:e7:7d: 47:6f:d0:95:59:e5:12:01:dd:9f:f5:cd:cc:bc:04: 8a:bc:0e:4a:6e:32:b1:9b:f5:0f:46:dc:65:4e:e1: 1b:0f:04:60:31:aa:41:22:8f:fd:df:c7:b5:c6:20: d7:22:7b:7a:d2:b5:1d:0d:09 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: 26:90:05:76:8A:09:92:22:86:28:FC:A0:5A:12:67:D1:E4:3D:1C:54 X509v3 Authority Key Identifier: keyid:35:AF:B3:4A:08:32:09:65:06:CA:EF:1E:6F:5B:CC:51:20:ED:8A:0F DirName:/CN=neptune/C=NL/ST=Utrecht/L=Woerden/O=Pyro Co./emailAddress=pyro-ca@domain.invalid serial:8D:1A:C8:9A:98:71:0F:B1 Netscape CA Revocation Url: https://www.sial.org/ca-crl.pem Signature Algorithm: sha1WithRSAEncryption ed:3b:1a:0b:2b:ed:88:37:66:3d:b2:5c:1e:5b:29:8b:57:27: ff:15:44:6b:7b:1a:10:23:dc:27:9a:92:ff:c4:e7:5a:6d:2a: e6:91:f0:32:92:6c:fa:a2:2a:36:77:4c:67:61:34:08:df:0f: f6:d6:ae:f7:c4:40:95:6d:a6:bf:6d:45:7b:d7:aa:55:6b:af: 53:96:39:1b:2e:3a:45:84:bf:a1:9d:58:37:1c:de:0b:5f:8c: 37:f0:04:a8:13:2f:6f:91:16:43:f7:fc:6d:c5:01:3d:9b:6c: 6e:0a:d8:95:20:16:43:a5:22:16:d4:2f:ac:7f:fd:d5:29:87: 41:c2:f2:d3:d0:45:d9:a0:d3:e9:f1:3d:0c:28:38:5c:55:de: a1:36:cc:01:22:4e:34:f0:af:9c:7f:e3:95:65:5f:1f:d5:64: 24:81:7b:52:28:ad:cd:75:07:65:14:6f:29:a2:de:fb:1d:3d: b8:07:6d:cd:be:0f:a3:55:8a:df:d8:f6:02:68:79:d7:22:7d: b3:ea:64:7b:33:68:58:94:f9:b3:cf:1c:e8:9f:36:06:b3:99: da:89:04:44:22:e8:c8:0c:a3:9c:e4:71:0b:6e:7c:cd:71:61: ff:ae:1d:82:7b:44:8c:c9:ac:62:d4:b8:aa:9e:65:1b:84:21: 86:10:3c:36 -----BEGIN CERTIFICATE----- MIIECDCCAvCgAwIBAgIBAjANBgkqhkiG9w0BAQUFADB9MRAwDgYDVQQDEwduZXB0 dW5lMQswCQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHV29l cmRlbjERMA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG9w0BCQEWFnB5cm8tY2FA ZG9tYWluLmludmFsaWQwHhcNMDkwNzExMTkwNTUzWhcNMTIwNDA2MTkwNTUzWjCB gDELMAkGA1UEBhMCTkwxEDAOBgNVBAgTB1V0cmVjaHQxETAPBgNVBAoTCFB5cm8g Q28uMQ8wDQYDVQQLEwZTZXJ2ZXIxEDAOBgNVBAMTB25lcHR1bmUxKTAnBgkqhkiG 9w0BCQEWGnB5cm8tc2VydmVyQGRvbWFpbi5pbnZhbGlkMIGfMA0GCSqGSIb3DQEB AQUAA4GNADCBiQKBgQCyDBc0Y5WFsm2pIWev+AkeTIQqs7m7WyKrU30EpfYWmt/f UdpMEDX1cy0vhKoKWCGMr1wySNqXA81p4xDOawcgo3iK+pTESK/nfUdv0JVZ5RIB 3Z/1zcy8BIq8DkpuMrGb9Q9G3GVO4RsPBGAxqkEij/3fx7XGINcie3rStR0NCQID AQABo4IBETCCAQ0wCQYDVR0TBAIwADAdBgNVHQ4EFgQUJpAFdooJkiKGKPygWhJn 0eQ9HFQwgbAGA1UdIwSBqDCBpYAUNa+zSggyCWUGyu8eb1vMUSDtig+hgYGkfzB9 MRAwDgYDVQQDEwduZXB0dW5lMQswCQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNo dDEQMA4GA1UEBxMHV29lcmRlbjERMA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG 9w0BCQEWFnB5cm8tY2FAZG9tYWluLmludmFsaWSCCQCNGsiamHEPsTAuBglghkgB hvhCAQQEIRYfaHR0cHM6Ly93d3cuc2lhbC5vcmcvY2EtY3JsLnBlbTANBgkqhkiG 9w0BAQUFAAOCAQEA7TsaCyvtiDdmPbJcHlspi1cn/xVEa3saECPcJ5qS/8TnWm0q 5pHwMpJs+qIqNndMZ2E0CN8P9tau98RAlW2mv21Fe9eqVWuvU5Y5Gy46RYS/oZ1Y NxzeC1+MN/AEqBMvb5EWQ/f8bcUBPZtsbgrYlSAWQ6UiFtQvrH/91SmHQcLy09BF 2aDT6fE9DCg4XFXeoTbMASJONPCvnH/jlWVfH9VkJIF7UiitzXUHZRRvKaLe+x09 uAdtzb4Po1WK39j2Amh51yJ9s+pkezNoWJT5s88c6J82BrOZ2okERCLoyAyjnORx C258zXFh/64dgntEjMmsYtS4qp5lG4QhhhA8Ng== -----END CERTIFICATE----- Pyro-3.14/examples/ssl/certs/server_nokey.pem0000644000076500000240000001020111517657602021610 0ustar irmenstaff00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=neptune, C=NL, ST=Utrecht, L=Woerden, O=Pyro Co./emailAddress=pyro-ca@domain.invalid Validity Not Before: Jul 11 19:05:53 2009 GMT Not After : Apr 6 19:05:53 2012 GMT Subject: C=NL, ST=Utrecht, O=Pyro Co., OU=Server, CN=neptune/emailAddress=pyro-server@domain.invalid Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:b2:0c:17:34:63:95:85:b2:6d:a9:21:67:af:f8: 09:1e:4c:84:2a:b3:b9:bb:5b:22:ab:53:7d:04:a5: f6:16:9a:df:df:51:da:4c:10:35:f5:73:2d:2f:84: aa:0a:58:21:8c:af:5c:32:48:da:97:03:cd:69:e3: 10:ce:6b:07:20:a3:78:8a:fa:94:c4:48:af:e7:7d: 47:6f:d0:95:59:e5:12:01:dd:9f:f5:cd:cc:bc:04: 8a:bc:0e:4a:6e:32:b1:9b:f5:0f:46:dc:65:4e:e1: 1b:0f:04:60:31:aa:41:22:8f:fd:df:c7:b5:c6:20: d7:22:7b:7a:d2:b5:1d:0d:09 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: 26:90:05:76:8A:09:92:22:86:28:FC:A0:5A:12:67:D1:E4:3D:1C:54 X509v3 Authority Key Identifier: keyid:35:AF:B3:4A:08:32:09:65:06:CA:EF:1E:6F:5B:CC:51:20:ED:8A:0F DirName:/CN=neptune/C=NL/ST=Utrecht/L=Woerden/O=Pyro Co./emailAddress=pyro-ca@domain.invalid serial:8D:1A:C8:9A:98:71:0F:B1 Netscape CA Revocation Url: https://www.sial.org/ca-crl.pem Signature Algorithm: sha1WithRSAEncryption ed:3b:1a:0b:2b:ed:88:37:66:3d:b2:5c:1e:5b:29:8b:57:27: ff:15:44:6b:7b:1a:10:23:dc:27:9a:92:ff:c4:e7:5a:6d:2a: e6:91:f0:32:92:6c:fa:a2:2a:36:77:4c:67:61:34:08:df:0f: f6:d6:ae:f7:c4:40:95:6d:a6:bf:6d:45:7b:d7:aa:55:6b:af: 53:96:39:1b:2e:3a:45:84:bf:a1:9d:58:37:1c:de:0b:5f:8c: 37:f0:04:a8:13:2f:6f:91:16:43:f7:fc:6d:c5:01:3d:9b:6c: 6e:0a:d8:95:20:16:43:a5:22:16:d4:2f:ac:7f:fd:d5:29:87: 41:c2:f2:d3:d0:45:d9:a0:d3:e9:f1:3d:0c:28:38:5c:55:de: a1:36:cc:01:22:4e:34:f0:af:9c:7f:e3:95:65:5f:1f:d5:64: 24:81:7b:52:28:ad:cd:75:07:65:14:6f:29:a2:de:fb:1d:3d: b8:07:6d:cd:be:0f:a3:55:8a:df:d8:f6:02:68:79:d7:22:7d: b3:ea:64:7b:33:68:58:94:f9:b3:cf:1c:e8:9f:36:06:b3:99: da:89:04:44:22:e8:c8:0c:a3:9c:e4:71:0b:6e:7c:cd:71:61: ff:ae:1d:82:7b:44:8c:c9:ac:62:d4:b8:aa:9e:65:1b:84:21: 86:10:3c:36 -----BEGIN CERTIFICATE----- MIIECDCCAvCgAwIBAgIBAjANBgkqhkiG9w0BAQUFADB9MRAwDgYDVQQDEwduZXB0 dW5lMQswCQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHV29l cmRlbjERMA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG9w0BCQEWFnB5cm8tY2FA ZG9tYWluLmludmFsaWQwHhcNMDkwNzExMTkwNTUzWhcNMTIwNDA2MTkwNTUzWjCB gDELMAkGA1UEBhMCTkwxEDAOBgNVBAgTB1V0cmVjaHQxETAPBgNVBAoTCFB5cm8g Q28uMQ8wDQYDVQQLEwZTZXJ2ZXIxEDAOBgNVBAMTB25lcHR1bmUxKTAnBgkqhkiG 9w0BCQEWGnB5cm8tc2VydmVyQGRvbWFpbi5pbnZhbGlkMIGfMA0GCSqGSIb3DQEB AQUAA4GNADCBiQKBgQCyDBc0Y5WFsm2pIWev+AkeTIQqs7m7WyKrU30EpfYWmt/f UdpMEDX1cy0vhKoKWCGMr1wySNqXA81p4xDOawcgo3iK+pTESK/nfUdv0JVZ5RIB 3Z/1zcy8BIq8DkpuMrGb9Q9G3GVO4RsPBGAxqkEij/3fx7XGINcie3rStR0NCQID AQABo4IBETCCAQ0wCQYDVR0TBAIwADAdBgNVHQ4EFgQUJpAFdooJkiKGKPygWhJn 0eQ9HFQwgbAGA1UdIwSBqDCBpYAUNa+zSggyCWUGyu8eb1vMUSDtig+hgYGkfzB9 MRAwDgYDVQQDEwduZXB0dW5lMQswCQYDVQQGEwJOTDEQMA4GA1UECBMHVXRyZWNo dDEQMA4GA1UEBxMHV29lcmRlbjERMA8GA1UEChMIUHlybyBDby4xJTAjBgkqhkiG 9w0BCQEWFnB5cm8tY2FAZG9tYWluLmludmFsaWSCCQCNGsiamHEPsTAuBglghkgB hvhCAQQEIRYfaHR0cHM6Ly93d3cuc2lhbC5vcmcvY2EtY3JsLnBlbTANBgkqhkiG 9w0BAQUFAAOCAQEA7TsaCyvtiDdmPbJcHlspi1cn/xVEa3saECPcJ5qS/8TnWm0q 5pHwMpJs+qIqNndMZ2E0CN8P9tau98RAlW2mv21Fe9eqVWuvU5Y5Gy46RYS/oZ1Y NxzeC1+MN/AEqBMvb5EWQ/f8bcUBPZtsbgrYlSAWQ6UiFtQvrH/91SmHQcLy09BF 2aDT6fE9DCg4XFXeoTbMASJONPCvnH/jlWVfH9VkJIF7UiitzXUHZRRvKaLe+x09 uAdtzb4Po1WK39j2Amh51yJ9s+pkezNoWJT5s88c6J82BrOZ2okERCLoyAyjnORx C258zXFh/64dgntEjMmsYtS4qp5lG4QhhhA8Ng== -----END CERTIFICATE----- Pyro-3.14/examples/ssl/client.py0000644000076500000240000000126511517657603017115 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core Pyro.config.PYRO_DNS_URI=True Pyro.config.PYROSSL_CERT="client.pem" # Pyro.config.PYROSSL_KEY="client.key" def sendMsg(obj): message="Irmen de Jong is a space alien" print print 'Sending secret message ('+message+')...' reply=obj.passSecretMessage(message) print 'I got a secret reply: ',reply print print "First, connect using the Name Server (PYRONAME://)" test = Pyro.core.getProxyForURI("PYRONAME://:test.ssl") sendMsg(test) print "Next, connect without using the Name Server (PYROLOCSSL://)" host=raw_input("Enter the hostname where the SSL server is running: ") test = Pyro.core.getProxyForURI("PYROLOCSSL://"+host+"/ssl") sendMsg(test) Pyro-3.14/examples/ssl/Readme.txt0000644000076500000240000000345511517657603017226 0ustar irmenstaff00000000000000This example shows the SSL features of Pyro. The server is created using the PYROSSL protocol, and will only accept SSL connections. It installs a connection validator that prints some info about the client's SSL certificate. The client code is no different than regular Pyro clients, because the Proxy (actually the protocol adapter) knows how to deal with the PYROSSL: protocol. Take a peek in the nameserver, you'll see that the server is registered with a PYROSSL: uri. The "certs" directory contains a bunch of example certificates. Make sure that this directory can be found by Pyro. (the location is specified in the PYROSSL_CERTDIR config item, which defaults to "certs" in the PYRO_STORAGE directory, which is by default the current directory). NOTE: the supplied example certificates are only there to let you initialise the SSL layer. M2Crypto/SSL will check the hostname of the certificate (if it does its job), and will probably revoke it (because I put in the hostname of my own machine) See m2crypto homepage ( http://chandlerproject.org/Projects/MeTooCrypto ) or openssl documentation ( http://www.openssl.org ) for instructions on how to create your own ca and server/client certificates. Here is a good guide: (Yes, use the Local CA and create two certificates-- one for the server and one for the client). Important: the creation of the CA .csr (req) files must be done in a different directory, each with its own host.key. The final creation of the server.pem and client.pem file is done by concatenating the requester host.key and the .cert file. You can omit this but then you need to specify the PYROSSL_KEY location separately. NOTE: the demo certificates provided are valid until April 2012. At that moment I'll have to create new demo certificates. Pyro-3.14/examples/ssl/server.py0000644000076500000240000000354511517657603017150 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys import Pyro.naming, Pyro.core, Pyro.util, Pyro.protocol from Pyro.errors import PyroError,NamingError from Pyro.protocol import getHostname ######## Custom connections validator. class printCertValidator(Pyro.protocol.BasicSSLValidator): def checkCertificate(self,cert): if cert is None: return (0,3) print "Cert issuer: %s" % cert.get_issuer() print "Cert subject: %s" % cert.get_subject() print "Cert valid not before: %s" % cert.get_not_before() print "Cert valid not after: %s" % cert.get_not_after() return (1,0) ##### test object class testclass(Pyro.core.ObjBase): def passSecretMessage(self,arg): print 'I got a secret message: ',arg return "Elvis Presley isn't dead, he just went home" ##### main program. Pyro.config.PYROSSL_CERT="server.pem" # Pyro.config.PYROSSL_KEY="server.key" Pyro.config.PYRO_DNS_URI=True Pyro.config.PYRO_TRACELEVEL=3 Pyro.config.PYRO_NS_DEFAULTGROUP=':test' Pyro.config.PYRO_LOGFILE='server_log' print 'Check the logfile for messages: server_log' # Construct the Pyro Daemon with our own connection validator, using SSL daemon = Pyro.core.Daemon(prtcol='PYROSSL') daemon.setNewConnectionValidator(printCertValidator()) ### <<--- !!! # locate the NS locator = Pyro.naming.NameServerLocator() print 'searching for Naming Service...' ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port # make sure our namespace group exists try: ns.createGroup(Pyro.config.PYRO_NS_DEFAULTGROUP) except NamingError: pass daemon.useNameServer(ns) # connect a new object implementation (first unregister previous one) try: ns.unregister('ssl') except NamingError: pass uri=daemon.connect(testclass(),'ssl') print "server uri=",uri # enter the service loop. print 'Server object "ssl" ready.' daemon.requestLoop() Pyro-3.14/examples/stockquotes/0000755000076500000240000000000011567263340017040 5ustar irmenstaff00000000000000Pyro-3.14/examples/stockquotes/Client.py0000644000076500000240000000126211517657600020632 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.EventService.Clients import Subscriber from Server import symbols from Pyro.errors import NamingError class StockSubscriber(Subscriber): def __init__(self, symbols): Subscriber.__init__(self) symbols=map(lambda s: 'STOCKQUOTE.'+s, symbols) self.subscribe(symbols) def event(self, event): print event.subject,'=',event.msg print "Available stock quote symbols:" for s in symbols: print ' ',s, print symbols=raw_input("Enter comma separated stock symbols to listen to: ").split(',') try: listener=StockSubscriber(symbols) print 'Listening!' listener.listen() except NamingError: print 'Cannot find service. Is the Event Service running?' Pyro-3.14/examples/stockquotes/Client_noNS.py0000644000076500000240000000137411517657600021573 0ustar irmenstaff00000000000000#!/usr/bin/env python # This client connects to the Event server directly, # it doesn't use the name server but requires a direct URI. from Pyro.EventService.Clients import Subscriber from Server import symbols class StockSubscriber(Subscriber): def __init__(self, symbols, es_URI): Subscriber.__init__(self, esURI=es_URI) symbols=map(lambda s: 'STOCKQUOTE.'+s, symbols) self.subscribe(symbols) def event(self, event): print event.subject,'=',event.msg print "Available stock quote symbols:" for s in symbols: print ' ',s, print symbols=raw_input("Enter comma separated stock symbols to listen to: ").split(',') es_URI = raw_input("enter URI of the Event Server: ") listener=StockSubscriber(symbols, es_URI) print 'Listening!' listener.listen() Pyro-3.14/examples/stockquotes/MClient.py0000644000076500000240000000103611517657600020746 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.EventService.Clients import Subscriber from Server import symbols from Pyro.errors import NamingError class MatchStockSubscriber(Subscriber): def __init__(self, pattern): Subscriber.__init__(self) self.subscribeMatch(pattern) def event(self, event): print event.subject,'=',event.msg pattern = '^STOCKQUOTE\\.S.*$' try: listener=MatchStockSubscriber(pattern) print 'Listening on pattern',pattern listener.listen() except NamingError: print 'Cannot find service. Is the Event Service running?' Pyro-3.14/examples/stockquotes/Readme.txt0000644000076500000240000000165111517657600021002 0ustar irmenstaff00000000000000This simple stock quote server shows the use of the Event Service. Clients subscribe to certain stock quotes. Stock markets publish certain stock quotes at random intervals. The interested clients receive the quotes. MAKE SURE THAT THE EVENT SERVICE (es) HAS BEEN STARTED IN ADVANCE. You may want to start multiple clients in different windows that listen to different (or the same!) stock symbols. If you start a second or third server, stock quotes will be generated faster because there are multiple publishers! Publishers don't know the interested parties and the listeners don't know where the quotes come from. The MClient program uses a pattern match for subscribing. See also the countingcars example. Note: the Server_noNS and Client_noNS show the use of the event server without using the name server. You can start the Event server itself with the -N parameter. This way you don't need to have a name server running. Pyro-3.14/examples/stockquotes/Server.py0000644000076500000240000000147311517657600020666 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.EventService.Clients import Publisher from Pyro.errors import NamingError import random, time symbols = ('SUN','MICROSOFT','IBM','ORACLE','SAP','NOVELL') class StockMarket(Publisher): def __init__(self, symbols): Publisher.__init__(self) self.symbols=symbols def publishQuote(self): symbol=random.choice(self.symbols) quote =round(random.random()*100+50,2) print symbol,'=',quote self.publish('STOCKQUOTE.'+symbol, quote) def main(): try: market1 = StockMarket(symbols[:3]) market2 = StockMarket(symbols[3:]) print 'Publishing quotes.' while 1: time.sleep(random.random()) market = random.choice( (market1, market2) ) market.publishQuote() except NamingError: print 'Cannot find service. Is the Event Service running?' if __name__=='__main__': main() Pyro-3.14/examples/stockquotes/Server_noNS.py0000644000076500000240000000160711517657600021622 0ustar irmenstaff00000000000000#!/usr/bin/env python # This server connects to the Event server directly, # it doesn't use the name server but requires a direct URI. from Pyro.EventService.Clients import Publisher import random, time symbols = ('SUN','MICROSOFT','IBM','ORACLE','SAP','NOVELL') class StockMarket(Publisher): def __init__(self, symbols, es_URI): Publisher.__init__(self, esURI=es_URI) self.symbols=symbols def publishQuote(self): symbol=random.choice(self.symbols) quote =round(random.random()*100+50,2) print symbol,'=',quote self.publish('STOCKQUOTE.'+symbol, quote) def main(): es_URI = raw_input("enter URI of the Event Server: ") market1 = StockMarket(symbols[:3], es_URI) market2 = StockMarket(symbols[3:], es_URI) print 'Publishing quotes.' while 1: time.sleep(random.random()) market = random.choice( (market1, market2) ) market.publishQuote() if __name__=='__main__': main() Pyro-3.14/examples/stresstest/0000755000076500000240000000000011567263340016677 5ustar irmenstaff00000000000000Pyro-3.14/examples/stresstest/consumer.py0000644000076500000240000000414311527042455021104 0ustar irmenstaff00000000000000#!/usr/bin/env python from __future__ import with_statement from Pyro.EventService.Clients import Subscriber from Pyro.errors import NamingError from threading import Thread import Pyro.util import time class TrafficCounter(Subscriber,Thread): def __init__(self,id): self.subjPrefix="STRESSTEST.CARS.HEADING." Subscriber.__init__(self) Thread.__init__(self) self.patterns=['north','east','south','west'] self.currentPattern=None self.counter=0 self.id=id self.lock=Pyro.util.getLockObject() self.subscribeNextPattern() def subscribeNextPattern(self): # because this is called from an event, # and that may occur in multiple threads concurrently, # we need a lock on this. # If we don't, it's possible that multiple threads # access the same ES proxy concurrently --> HAVOC. with self.lock: if self.currentPattern: self.unsubscribe(self.subjPrefix+self.currentPattern) try: self.currentPattern=self.patterns.pop() print self.id,'I am now watching for cars heading',self.currentPattern self.subscribe(self.subjPrefix+self.currentPattern) except IndexError: print self.id,'I watched all directions. Start over.' self.patterns=['north','south','east','west'] self.currentPattern=self.patterns.pop() self.subscribe(self.subjPrefix+self.currentPattern) def event(self, event): (color,car)=event.msg print self.id,'A',color,car,'went',event.subject[len(self.subjPrefix):] self.counter+=1 if self.counter>=4: self.counter=0 print self.id,"There were enough cars in that direction. Let's look somewhere else." self.subscribeNextPattern() def run(self): try: print self.id,'Going to count cars.' self.listen() print self.id,'Stopped counting cars.' except NamingError: print 'Cannot find service. Is the Event Service running?' def main(): threads=[] for i in range(20): tc=TrafficCounter(i) tc.start() threads.append(tc) try: while 1: time.sleep(10) except KeyboardInterrupt: print 'Break-- weating for threads to stop.' for tc in threads: tc.abort() for tc in threads: tc.join() if __name__=='__main__': main() Pyro-3.14/examples/stresstest/naming.py0000644000076500000240000000355511517657577020550 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.naming import random, time from threading import Thread import random from Pyro.errors import NamingError import binascii mustStop=0 def name(): return str(random.random())[-3:] def nameG(): if random.random()>0.3: return 'trasher.G'+name()+'.'+name() else: return 'trasher.'+name() class NamingTrasher(Thread): def __init__(self,number): Thread.__init__(self) self.number=number def flatlist(self): if random.random()>0.8: try: a=len(self.ns.flatlist()) except NamingError,x: pass def register(self): for i in range(4): try: self.ns.register(nameG(),'PYRO://localhost/111111111') except NamingError,x: pass def remove(self): try: self.ns.unregister(nameG()) except NamingError,x: pass def resolve(self): try: uri=self.ns.resolve(nameG()) except NamingError,x: pass def creategrp(self): for i in range(3): try: self.ns.createGroup('trasher.G'+name()) except NamingError,x: pass def delgrp(self): if random.random()>0.8: try: self.ns.deleteGroup('trasher.G'+name()) except NamingError,x: pass def run(self): self.ns = Pyro.naming.NameServerLocator().getNS() print 'Name Server trasher running.' while not mustStop: random.choice((self.flatlist, self.register, self.remove, self.resolve, self.creategrp, self.delgrp)) () print self.number,'called' time.sleep(random.random()/10) print 'Trasher exiting.' def main(): Pyro.core.initClient() threads=[] ns = Pyro.naming.NameServerLocator().getNS() ns.createGroup('trasher') for i in range(10): nt=NamingTrasher(i) nt.start() threads.append(nt) try: while 1: time.sleep(10) except KeyboardInterrupt: global mustStop mustStop=1 print 'Break-- waiting for threads to stop.' for nt in threads: nt.join() ns.deleteGroup('trasher') if __name__=='__main__': main() Pyro-3.14/examples/stresstest/producer.py0000644000076500000240000000237211517657577021116 0ustar irmenstaff00000000000000#!/usr/bin/env python from Pyro.EventService.Clients import Publisher from Pyro.errors import NamingError import random, time from threading import Thread mustStop=0 class VehicleProducer(Publisher, Thread): directions = ('north','east','south','west') cars = ('Ford','Toyota','Chrysler','Vauxhall','Honda','BMW') colors = ('red','green','white','black','blue','yellow') def __init__(self,id): Publisher.__init__(self) Thread.__init__(self) self.id=id def nextVehicle(self): direction=random.choice(self.directions) car=random.choice(self.cars) color=random.choice(self.colors) self.publish('STRESSTEST.CARS.HEADING.'+direction, (color,car)) print self.id,'published' def run(self): print 'Producer running.' try: global mustStop while not mustStop: time.sleep(random.random()/10) self.nextVehicle() print 'Producer stopped.' except NamingError: print 'Cannot find service. Is the Event Service running?' def main(): threads=[] for i in range(10): vp=VehicleProducer(i) vp.start() threads.append(vp) try: while 1: time.sleep(10) except KeyboardInterrupt: global mustStop mustStop=1 print 'Break-- waiting for threads to stop.' for vp in threads: vp.join() if __name__=='__main__': main() Pyro-3.14/examples/stresstest/Readme.txt0000644000076500000240000000116211517657577020653 0ustar irmenstaff00000000000000This example contains: - a stress test for the Naming Server (naming.py) This test creates a bunch of threads that connect to the NS and create/delete groups and object registrations randomly, very fast. - a stress test for the Event Server (producer.py and consumer.py) The producer starts a bunch of threads that all publish events very fast on the same channel. The consumer creates a bunch of consumers that all listen on that channel. No other stress tests seem necessary because the two above exercise most of Pyro's features. You should keep an eye on the CPU load and the memory usage. Both should be stable. Pyro-3.14/examples/testclient.py0000644000076500000240000000224511517657604017214 0ustar irmenstaff00000000000000# # Test client utility startup code # Kind of verbose; all this could be done much simpler # by using PYRONAME:// or something, but it shows what is happening. # import sys import Pyro.naming, Pyro.core, Pyro.protocol group = ':test' # the default namespace group for the tests # objname = the name of the object which is used in the NS # withAttrs = use a DynamicProxyWithAttrs or a regular DynamicProxy? def getproxy(objName, withAttrs=0): # initialize the client and set the default namespace group Pyro.core.initClient() Pyro.config.PYRO_NS_DEFAULTGROUP=group # locate the NS locator = Pyro.naming.NameServerLocator() print 'Searching Naming Service...', ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port # resolve the Pyro object print 'asking for object' try: URI=ns.resolve(objName) print 'URI:',URI except Pyro.core.PyroError,x: print 'Couldn\'t locate object, nameserver says:',x raise SystemExit # create a proxy for the Pyro object, and return that if withAttrs: return Pyro.core.getAttrProxyForURI(URI) else: return Pyro.core.getProxyForURI(URI) Pyro-3.14/examples/testmobile/0000755000076500000240000000000011567263340016623 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/bothways/0000755000076500000240000000000011567263340020463 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/bothways/client.py0000644000076500000240000000162111517657603022317 0ustar irmenstaff00000000000000import Pyro.core import Pyro.util import clientparams.parameters if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" HOST="localhost" URI = "PYROLOC://%s/testmobile.bothways" HOST=raw_input("server host (empty for %s): " % HOST) or HOST obj=Pyro.core.getProxyForURI(URI % HOST) # create a parameter of a class that the serve doesn't know about. # Pyro's mobile code feature will supply the code to the server. param = clientparams.parameters.ClientParameter("some name") result=obj.method(param) # the restut will be an 'unknown' object type ! # Pyro's mobile code feature will make the server supply the code to us as well. print "result of calling remote object: ",result print "name was: ",result.name Pyro-3.14/examples/testmobile/bothways/clientparams/0000755000076500000240000000000011567263340023145 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/bothways/clientparams/__init__.py0000644000076500000240000000000511517657603025255 0ustar irmenstaff00000000000000#bla Pyro-3.14/examples/testmobile/bothways/clientparams/parameters.py0000644000076500000240000000022111517657603025661 0ustar irmenstaff00000000000000 class ClientParameter(object): def __init__(self, name): self.name=name def method(self): print "I am client parameter object ",self.name Pyro-3.14/examples/testmobile/bothways/Readme.txt0000644000076500000240000000016011517657603022422 0ustar irmenstaff00000000000000Testing the passing of code from client to server, as well as server back to client. No nameserver is required. Pyro-3.14/examples/testmobile/bothways/server/0000755000076500000240000000000011567263340021771 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/bothways/server/server.py0000644000076500000240000000225111517657603023655 0ustar irmenstaff00000000000000#!/usr/bin/env python # # The server that doesn't use the Name Server. # import sys, os import Pyro.core from Pyro.errors import PyroError import serverparams.parameters if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" class Test(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self, argument): print "some method called on test class, arg=",argument print "calling method on the passed object..." argument.method() # create object of a type that that client does not know about, # so Pyro's mobile code featuer will also pass the (module)code # back to the client. return serverparams.parameters.ServerResult("server arg") Pyro.core.initServer() daemon = Pyro.core.Daemon() objectName='testmobile.bothways' uri=daemon.connect(Test(),objectName) # enter the service loop. print 'Server is ready for customers. I am not using the Name Server.' print 'Object name is:',objectName print 'The URI is: ',uri daemon.requestLoop() Pyro-3.14/examples/testmobile/bothways/server/serverparams/0000755000076500000240000000000011567263340024503 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/bothways/server/serverparams/__init__.py0000644000076500000240000000000511517657603026613 0ustar irmenstaff00000000000000#bla Pyro-3.14/examples/testmobile/bothways/server/serverparams/parameters.py0000644000076500000240000000020711517657603027223 0ustar irmenstaff00000000000000 class ServerResult(object): def __init__(self, name): self.name=name def method(self): print "I am parameter object ",self.name Pyro-3.14/examples/testmobile/client2server/0000755000076500000240000000000011567263340021412 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/client2server/client.py0000644000076500000240000000047711517657604023257 0ustar irmenstaff00000000000000import Pyro.core import Pyro.util import params.parameters HOST="localhost" URI = "PYROLOC://%s/testmobile.client2server" HOST=raw_input("server host (empty for %s): " % HOST) or HOST obj=Pyro.core.getProxyForURI(URI % HOST) p = params.parameters.Parameter("Some name") print "calling remote object: ",obj.method(p) Pyro-3.14/examples/testmobile/client2server/params/0000755000076500000240000000000011567263340022675 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/client2server/params/__init__.py0000644000076500000240000000000511517657604025006 0ustar irmenstaff00000000000000#bla Pyro-3.14/examples/testmobile/client2server/params/parameters.py0000644000076500000240000000020411517657604025413 0ustar irmenstaff00000000000000 class Parameter(object): def __init__(self, name): self.name=name def method(self): print "I am parameter object ",self.name Pyro-3.14/examples/testmobile/client2server/Readme.txt0000644000076500000240000000011611517657604023353 0ustar irmenstaff00000000000000Testing the passing of code from client to server. No nameserver is required. Pyro-3.14/examples/testmobile/client2server/server/0000755000076500000240000000000011567263340022720 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/client2server/server/server.py0000644000076500000240000000162011517657604024604 0ustar irmenstaff00000000000000#!/usr/bin/env python # # The server that doesn't use the Name Server. # import sys, os import Pyro.core from Pyro.errors import PyroError if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" class Test(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self, argument): print "some method called on test class, arg=",argument return "this is the answer" Pyro.core.initServer() daemon = Pyro.core.Daemon() objectName='testmobile.client2server' uri=daemon.connect(Test(),objectName) # enter the service loop. print 'Server is ready for customers. I am not using the Name Server.' print 'Object name is:',objectName print 'The URI is: ',uri daemon.requestLoop() Pyro-3.14/examples/testmobile/imports/0000755000076500000240000000000011567263340020320 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/imports/client.py0000644000076500000240000000130611517657604022155 0ustar irmenstaff00000000000000import Pyro.core import Pyro.util import params.parameters if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" HOST="localhost" URI = "PYROLOC://%s/testmobile.imports" HOST=raw_input("server host (empty for %s): " % HOST) or HOST obj=Pyro.core.getProxyForURI(URI % HOST) p = params.parameters.Parameter("Some name") try: result=obj.method(p) print "calling remote object: ",result print "calling result method:", result.getAnswer() except Exception,x: print "".join(Pyro.util.getPyroTraceback(x)) Pyro-3.14/examples/testmobile/imports/params/0000755000076500000240000000000011567263340021603 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/imports/params/__init__.py0000644000076500000240000000000511517657604023714 0ustar irmenstaff00000000000000#bla Pyro-3.14/examples/testmobile/imports/params/base.py0000644000076500000240000000024011517657604023070 0ustar irmenstaff00000000000000 class ParameterBase(object): def __init__(self, basename): self.basename=basename def basemethod(self): print "I am parameterbase object ",self.basename Pyro-3.14/examples/testmobile/imports/params/parameters.py0000644000076500000240000000034511517657604024327 0ustar irmenstaff00000000000000import params.base class Parameter(params.base.ParameterBase): def __init__(self, name): params.base.ParameterBase.__init__(self, "name of base") self.name=name def method(self): print "I am parameter object ",self.name Pyro-3.14/examples/testmobile/imports/Readme.txt0000644000076500000240000000027111517657604022263 0ustar irmenstaff00000000000000Testing the passing of code from client to server, where the mobile code also includes other modules. No nameserver is required. Requires PYRO_MOBILE_CODE=1 for both server and client. Pyro-3.14/examples/testmobile/imports/server/0000755000076500000240000000000011567263340021626 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/imports/server/answers/0000755000076500000240000000000011567263340023310 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/imports/server/answers/__init__.py0000644000076500000240000000000511517657604025421 0ustar irmenstaff00000000000000#bla Pyro-3.14/examples/testmobile/imports/server/answers/answer.py0000644000076500000240000000034411517657604025167 0ustar irmenstaff00000000000000import answers.base class Answer(answers.base.AnswerBase): def __init__(self, answer): answers.base.AnswerBase.__init__(self) self.answer=answer def getAnswer(self): return "answer="+self.answer+self.answerBaseSuffix() Pyro-3.14/examples/testmobile/imports/server/answers/base.py0000644000076500000240000000020011517657604024571 0ustar irmenstaff00000000000000 class AnswerBase(object): def __init__(self): pass def answerBaseSuffix(self): return "(this is suffix from base class)" Pyro-3.14/examples/testmobile/imports/server/server.py0000644000076500000240000000177311517657604023523 0ustar irmenstaff00000000000000#!/usr/bin/env python # # The server that doesn't use the Name Server. # import sys, os import Pyro.core from Pyro.errors import PyroError import answers.answer if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" class Test(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self, argument): print "some method called on test class, arg=",argument # returning an answer object result=answers.answer.Answer("some answer") print "returning",result return result Pyro.core.initServer() daemon = Pyro.core.Daemon() objectName='testmobile.imports' uri=daemon.connect(Test(),objectName) # enter the service loop. print 'Server is ready for customers. I am not using the Name Server.' print 'Object name is:',objectName print 'The URI is: ',uri daemon.requestLoop() Pyro-3.14/examples/testmobile/passon/0000755000076500000240000000000011567263340020126 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/passon/client.py0000644000076500000240000000033611517657604021765 0ustar irmenstaff00000000000000import Pyro.core import Pyro.util import params.parameters obj=Pyro.core.getProxyForURI("PYRONAME://:testmobile_passon_server1") p = params.parameters.Parameter("Some name") print "calling remote object: ",obj.method1(p) Pyro-3.14/examples/testmobile/passon/params/0000755000076500000240000000000011567263340021411 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/passon/params/__init__.py0000644000076500000240000000000511517657604023522 0ustar irmenstaff00000000000000#bla Pyro-3.14/examples/testmobile/passon/params/parameters.py0000644000076500000240000000020411517657604024127 0ustar irmenstaff00000000000000 class Parameter(object): def __init__(self, name): self.name=name def method(self): print "I am parameter object ",self.name Pyro-3.14/examples/testmobile/passon/Readme.txt0000644000076500000240000000020011517657604022061 0ustar irmenstaff00000000000000Testing the passing of code from client to server, to another server, and finally back to the client. A nameserver is required. Pyro-3.14/examples/testmobile/passon/server/0000755000076500000240000000000011567263340021434 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/passon/server/server1.py0000644000076500000240000000230711517657604023404 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os import Pyro.core from Pyro.errors import PyroError import Pyro.naming if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" class Test(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method1(self, argument): print "method1 called on test class, arg=",argument try: obj=Pyro.core.getProxyForURI("PYRONAME://:testmobile_passon_server2") print "calling method on server2, passing on argument" result="Server1 result;" result+=obj.method2(argument) except PyroError,x: print "Some error occured!",x result="there was an error in server1, see the console over there" print "returning result:",result return result Pyro.core.initServer() Pyro.core.initClient() ns=Pyro.naming.NameServerLocator().getNS() daemon = Pyro.core.Daemon() daemon.useNameServer(ns) objectName=':testmobile_passon_server1' uri=daemon.connect(Test(),objectName) # enter the service loop. print 'Server1 is ready for customers.' daemon.requestLoop() Pyro-3.14/examples/testmobile/passon/server/server2.py0000644000076500000240000000152211517657604023403 0ustar irmenstaff00000000000000#!/usr/bin/env python import sys, os import Pyro.core from Pyro.errors import PyroError import Pyro.naming if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" class Test(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method2(self, argument): print "method2 called on test class, arg=",argument return "Server2 answer" Pyro.core.initServer() daemon = Pyro.core.Daemon() ns=Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) objectName=':testmobile_passon_server2' uri=daemon.connect(Test(),objectName) # enter the service loop. print 'Server2 is ready for customers.' daemon.requestLoop() Pyro-3.14/examples/testmobile/Readme.txt0000644000076500000240000000010511517657604020562 0ustar irmenstaff00000000000000Various examples to test the different types of mobile code support. Pyro-3.14/examples/testmobile/server2client/0000755000076500000240000000000011567263340021412 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/server2client/client.py0000644000076500000240000000115111517657604023245 0ustar irmenstaff00000000000000import Pyro.core import Pyro.util if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" HOST="localhost" URI = "PYROLOC://%s/testmobile.server2client" HOST=raw_input("server host (empty for %s): " % HOST) or HOST obj=Pyro.core.getProxyForURI(URI % HOST) result=obj.method("some name") # this will be an 'unknown' object type ! print "result of calling remote object: ",result print "name was: ",result.name Pyro-3.14/examples/testmobile/server2client/Readme.txt0000644000076500000240000000012311517657604023351 0ustar irmenstaff00000000000000Testing the passing of code from server back to client. No nameserver is required. Pyro-3.14/examples/testmobile/server2client/server/0000755000076500000240000000000011567263340022720 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/server2client/server/params/0000755000076500000240000000000011567263340024203 5ustar irmenstaff00000000000000Pyro-3.14/examples/testmobile/server2client/server/params/__init__.py0000644000076500000240000000000511517657604026314 0ustar irmenstaff00000000000000#bla Pyro-3.14/examples/testmobile/server2client/server/params/parameters.py0000644000076500000240000000020411517657604026721 0ustar irmenstaff00000000000000 class Parameter(object): def __init__(self, name): self.name=name def method(self): print "I am parameter object ",self.name Pyro-3.14/examples/testmobile/server2client/server/server.py0000644000076500000240000000212611517657604024606 0ustar irmenstaff00000000000000#!/usr/bin/env python # # The server that doesn't use the Name Server. # import sys, os import Pyro.core from Pyro.errors import PyroError import params.parameters if not Pyro.config.PYRO_MOBILE_CODE: print "\nWARNING: PYRO_MOBILE_CODE not enabled\n" if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: print "\nWARNING: Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1\n" class Test(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self, argument): print "some method called on test class, arg=",argument # create object of a type that that client does not know about, # so Pyro's mobile code featuer will also pass the (module)code # back to the client. return params.parameters.Parameter(argument) Pyro.core.initServer() daemon = Pyro.core.Daemon() objectName='testmobile.server2client' uri=daemon.connect(Test(),objectName) # enter the service loop. print 'Server is ready for customers. I am not using the Name Server.' print 'Object name is:',objectName print 'The URI is: ',uri daemon.requestLoop() Pyro-3.14/examples/testserver.py0000644000076500000240000000337611517657604017252 0ustar irmenstaff00000000000000# # Test server base implementation, used by a lot of the tests, # to avoid having to repeat this boilerplate code time after time. # import sys import Pyro.naming import Pyro.core from Pyro.protocol import getHostname from Pyro.errors import PyroError,NamingError group = ':test' # the namespace group for all test servers ######## main program # objClass = the class of the Pyro implementation object # objName = the name for the object to register in the Name Server # delegate = whether to use the delegate approach or the regular # objBase subclassing approach def start(objClass, objName, delegate=0): # initialize the server and set the default namespace group Pyro.core.initServer() Pyro.config.PYRO_NS_DEFAULTGROUP=group # locate the NS daemon = Pyro.core.Daemon() locator = Pyro.naming.NameServerLocator() print 'searching for Naming Service...' ns = locator.getNS() print 'Naming Service found at',ns.URI.address,'('+(Pyro.protocol.getHostname(ns.URI.address) or '??')+') port',ns.URI.port # make sure our namespace group exists try: ns.createGroup(group) except NamingError: pass daemon.useNameServer(ns) # connect a new object implementation (first unregister previous one) try: ns.unregister(objName) except NamingError: pass if delegate: print 'Delegation...' # use Deletation approach obj=Pyro.core.ObjBase() obj.delegateTo(objClass()) daemon.connect(obj,objName) else: # use regular ObjBase subclassing approach obj=objClass() daemon.connect(obj,objName) # enter the service loop. print 'Server object "'+objName+'" ready.' try: # daemon.setTimeout(5) daemon.requestLoop() except KeyboardInterrupt: print 'shutting down gracefully.' daemon.disconnect(obj) daemon.shutdown() print 'Exiting.' Pyro-3.14/examples/threadmobile/0000755000076500000240000000000011567263340017113 5ustar irmenstaff00000000000000Pyro-3.14/examples/threadmobile/client.py0000644000076500000240000000214411517657602020747 0ustar irmenstaff00000000000000import threading import time import Pyro.core import Pyro.util import params.parameters HOST="localhost" URI = "PYROLOC://%s/test.threadmobile" NUM_THREADS=10 if not Pyro.config.PYRO_MOBILE_CODE: raise SystemExit("PYRO_MOBILE_CODE not enabled") if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: raise SystemExit("Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1") startEvent = threading.Event() def worker(): obj=Pyro.core.getProxyForURI(URI % HOST) startEvent.wait() name = threading.currentThread().getName() print "worker", name p = params.parameters.Parameter(name) try: result=obj.method(p) print "result=",result,"calling method on it..." result.method() except Exception,x: print "".join(Pyro.util.getPyroTraceback(x)) time.sleep(1) print "exit" HOST=raw_input("server host (empty for %s): " % HOST) or HOST workers=[] for i in range(NUM_THREADS): w=threading.Thread(target=worker) workers.append(w) w.start() time.sleep(0.1) time.sleep(1) print "starting!" startEvent.set() print "running" for w in workers: w.join() print "done!" Pyro-3.14/examples/threadmobile/params/0000755000076500000240000000000011567263340020376 5ustar irmenstaff00000000000000Pyro-3.14/examples/threadmobile/params/__init__.py0000644000076500000240000000000511517657602022505 0ustar irmenstaff00000000000000#bla Pyro-3.14/examples/threadmobile/params/parameters.py0000644000076500000240000000020411517657602023112 0ustar irmenstaff00000000000000 class Parameter(object): def __init__(self, name): self.name=name def method(self): print "I am parameter object ",self.name Pyro-3.14/examples/threadmobile/Readme.txt0000644000076500000240000000044311517657602021055 0ustar irmenstaff00000000000000This is a fairly technical test of using multiple threads with the mobile code feature. It sort-of tests the thread-safety of Pyro's mobile code import logic. Make sure you activate PYRO_MOBILE_CODE for both the server (in the server/ directory) and the client. No nameserver is required. Pyro-3.14/examples/threadmobile/server/0000755000076500000240000000000011567263340020421 5ustar irmenstaff00000000000000Pyro-3.14/examples/threadmobile/server/outparams.py0000644000076500000240000000022011517657602023003 0ustar irmenstaff00000000000000 class ResultParameter(object): def __init__(self, name): self.name=name def method(self): print "I am resultparameter object ",self.name Pyro-3.14/examples/threadmobile/server/server.py0000644000076500000240000000240211517657602022302 0ustar irmenstaff00000000000000#!/usr/bin/env python # # The server that doesn't use the Name Server. # import threading, time import sys, os import Pyro.core from Pyro.errors import PyroError import outparams if not Pyro.config.PYRO_MOBILE_CODE: raise SystemExit("PYRO_MOBILE_CODE not enabled") if Pyro.config.PYRO_XML_PICKLE=='gnosis' and Pyro.config.PYRO_GNOSIS_PARANOIA>=0: raise SystemExit("Using gnosis xml pickle but PYRO_GNOSIS_PARANOIA needs to be -1") class Test(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self, argument): print threading.currentThread().getName(),"got invocation" print "some method called on test class, arg=",argument time.sleep(0.2) print "invoking method on argument object" argument.method() return outparams.ResultParameter(threading.currentThread().getName()) Pyro.core.initServer() daemon = Pyro.core.Daemon() print print 'The Pyro Deamon is running on ',daemon.hostname+':'+str(daemon.port) print '(you may need this info for the client to connect to)' print objectName='test.threadmobile' uri=daemon.connect(Test(),objectName) # enter the service loop. print 'Server is ready for customers. I am not using the Name Server.' print 'Object name is:',objectName print 'The URI is: ',uri daemon.requestLoop() Pyro-3.14/examples/timeout/0000755000076500000240000000000011567263340016142 5ustar irmenstaff00000000000000Pyro-3.14/examples/timeout/Readme.txt0000644000076500000240000000023411517657602020102 0ustar irmenstaff00000000000000This is an example that shows the socket timeout handling. timeout.py -- example with _setTimeout() timeout2.py -- example without any timeout settings Pyro-3.14/examples/timeout/timeout.py0000644000076500000240000000324111517657602020205 0ustar irmenstaff00000000000000import time import Pyro.core import Pyro.naming from Pyro.errors import NamingError from threading import Thread class MySimpleServer(Thread): def __init__(self, daemon): super(MySimpleServer, self).__init__() self.__oDaemon = daemon self.__tIsRuning = False self.__tIsStopped = False self.setDaemon(True) def run(self): self.__tIsRunning = True while self.__tIsRunning: time.sleep(.1) self.__oDaemon.handleRequests(3) self.__oDaemon.shutdown() self.__tIsStopped = True print "server stopped." def stop(self): self.__tIsRunning = False def isStopped(self): return self.__tIsStopped class MyObject(Pyro.core.ObjBase): def test(self): print 'test called' Pyro.core.initServer() oNs = Pyro.naming.NameServerLocator().getNS() oDaemon = Pyro.core.Daemon() oDaemon.useNameServer(oNs) strTag = 'timeout_example' try: oNs.unregister(strTag) except NamingError: pass oObj = MyObject() strUri = oDaemon.connect(oObj, strTag) oServer = MySimpleServer(oDaemon) oServer.start() oProxy = Pyro.core.getProxyForURI(strUri) oProxy._setTimeout(4) print 'calling remote method on live proxy.' oProxy.test() print 'success.' print "stopping server..." oServer.stop() while not oServer.isStopped(): time.sleep(.1) print print 'calling remote method on stale proxy.' print 'because we specified a timeout, Pyro should take notice of this and throw a TimeoutException after a short while.' oProxy = Pyro.core.getProxyForURI(strUri) oProxy._setTimeout(4) oProxy.test() print 'This should not be shown!!! You should have seen a TimeoutException' Pyro-3.14/examples/timeout/timeout2.py0000644000076500000240000000337111517657602020273 0ustar irmenstaff00000000000000import time import Pyro.core import Pyro.naming from Pyro.errors import NamingError from threading import Thread class MySimpleServer(Thread): def __init__(self, daemon): super(MySimpleServer, self).__init__() self.__oDaemon = daemon self.__tIsRuning = False self.__tIsStopped = False self.setDaemon(True) def run(self): self.__tIsRunning = True while self.__tIsRunning: time.sleep(.1) self.__oDaemon.handleRequests(3) self.__oDaemon.shutdown() self.__tIsStopped = True print "server stopped." def stop(self): self.__tIsRunning = False def isStopped(self): return self.__tIsStopped class MyObject(Pyro.core.ObjBase): def test(self): print 'test called' Pyro.core.initServer() oNs = Pyro.naming.NameServerLocator().getNS() oDaemon = Pyro.core.Daemon() oDaemon.useNameServer(oNs) strTag = 'timeout_example' try: oNs.unregister(strTag) except NamingError: pass oObj = MyObject() strUri = oDaemon.connect(oObj, strTag) oServer = MySimpleServer(oDaemon) oServer.start() oProxy = Pyro.core.getProxyForURI(strUri) # not setting timeout this time: oProxy._setTimeout(4) print 'calling remote method on live proxy.' oProxy.test() print 'success.' print "stopping server..." oServer.stop() while not oServer.isStopped(): time.sleep(.1) print print 'calling remote method on stale proxy.' print 'as we did not specify a timeout, this should block indefinitely.' print '(press ctrl-c or ctrl-break to abort the program)' oProxy = Pyro.core.getProxyForURI(strUri) # not setting timeout this time: oProxy._setTimeout(4) oProxy.test() print 'This should not be shown!!! The method call should have blocked indefinitely!' Pyro-3.14/examples/tlstest/0000755000076500000240000000000011567263340016156 5ustar irmenstaff00000000000000Pyro-3.14/examples/tlstest/client.py0000644000076500000240000000106111517657603020010 0ustar irmenstaff00000000000000import Pyro.core uri=raw_input("paste uri:") obj1=Pyro.core.getProxyForURI(uri) obj1._setOneway("oneway") obj2=Pyro.core.getProxyForURI(uri) obj2._setOneway("oneway") print "obj1.ping" obj1.ping() print "obj1.ping" obj1.ping() print "obj2.ping" obj2.ping() print "obj2.ping" obj2.ping() print "obj1.oneway" obj1.oneway() print "obj1.oneway" obj1.oneway() print "obj2.oneway" obj1.oneway() print "obj2.oneway" obj1.oneway() print "obj1.comcall" print "usernames=",obj1.comcall() print "obj2.comcall" print "usernames=",obj2.comcall() Pyro-3.14/examples/tlstest/Readme.txt0000644000076500000240000000063211517657603020121 0ustar irmenstaff00000000000000This example shows the behavior of initTLS. It should be called in the context of the new thread. (so the thread id is checked to be the same). It also checks if it is called for Oneway method calls. If you run it on Windows, it will also use a tiny bit of COM code to get usernames. This should work fine. If you fail to initialize COM properly, you get errors such as "CoInitialize has not been called"... Pyro-3.14/examples/tlstest/server.py0000644000076500000240000000364711517657603020054 0ustar irmenstaff00000000000000import threading import sys, time import Pyro.core try: import win32com.client, pythoncom has_com=True except ImportError: has_com=False class TestServer(Pyro.core.ObjBase): def ping(self): threadid=threading.currentThread().ident tls=self.getLocalStorage() output="server: ping, tls=%s, threadid=%s, TLS threadid=%s\n" % (id(tls), threadid, tls.threadid) sys.stdout.write(output) sys.stdout.flush() if tls.threadid!=threadid: sys.stdout.write("!!!!! ERROR: threadids aren't identical !!!!!\n") sys.stdout.flush() def oneway(self): threadid=threading.currentThread().ident tls=self.getLocalStorage() output="server: oneway, tls=%s, threadid=%s, TLS threadid=%s\n" % (id(tls), threadid, tls.threadid) sys.stdout.write(output) sys.stdout.flush() if tls.threadid!=threadid: sys.stdout.write("!!!!! ERROR: threadids aren't identical !!!!!\n") sys.stdout.flush() def comcall(self): if not has_com: print "server: no com, doing nothing" return else: locator=win32com.client.Dispatch("WbemScripting.SwbemLocator") top = locator.ConnectServer(".", r"\\.\root\cimv2") users = top.InstancesOf("Win32_UserAccount") names=[] for u in users: names.append(u.name) return names def initTLS(tls): threadid=threading.currentThread().ident if has_com: pythoncom.CoInitialize() # initialize COM for this thread sys.stdout.write("server: initTLS, tls=%s, threadid=%s\n" % (id(tls),threadid)) sys.stdout.flush() tls.threadid=threadid daemon=Pyro.core.Daemon() obj=TestServer() uri=daemon.connect(obj) daemon.setInitTLS(initTLS) print "URI=",uri print "server running" daemon.requestLoop() Pyro-3.14/examples/user_passwd_auth/0000755000076500000240000000000011567263340020034 5ustar irmenstaff00000000000000Pyro-3.14/examples/user_passwd_auth/client.py0000644000076500000240000000105611517657577021704 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.core import connvalidator import getpass login = raw_input('Enter user name: ') password = getpass.getpass('Enter password: ') ident = "%s:%s" % (login,password) Pyro.core.initClient() obj = Pyro.core.getProxyForURI("PYRONAME://authentication") obj._setNewConnectionValidator( connvalidator.UserLoginConnValidator() ) obj._setIdentification( (login,password) ) # Try to call the method. # If you supplied correct username/password, it will succeed. result=obj.method('foo bar') print "Result from method call: ",result Pyro-3.14/examples/user_passwd_auth/connvalidator.py0000644000076500000240000000405611517657577023274 0ustar irmenstaff00000000000000from Pyro.protocol import DefaultConnValidator import Pyro.constants import hmac try: import hashlib md5=hashlib.md5 except ImportError: import md5 md5=md5.md5 # Example username/password database: (passwords stored in ascii md5 hash) EXAMPLE_ALLOWED_USERS = { "irmen": "5ebe2294ecd0e0f08eab7690d2a6ee69", # 'secret' "guest": "084e0343a0486ff05530df6c705c8bb4", # 'guest' "root": "bbbb2edef660739a6071ab5a4f8a869f", # 'change_me' } # # Example login/password validator. # Passwords are protected using md5 so they are not stored in plaintext. # The actual identification check is done using a hmac-md5 secure hash. # class UserLoginConnValidator(DefaultConnValidator): def acceptIdentification(self, daemon, connection, token, challenge): # extract tuple (login, processed password) from token as returned by createAuthToken # processed password is a hmac hash from the server's challenge string and the password itself. login, processedpassword = token.split(':', 1) knownpasswdhash = EXAMPLE_ALLOWED_USERS.get(login) # Check if the username/password is valid. if knownpasswdhash: # Known passwords are stored as ascii hash, but the auth token contains a binary hash. # So we need to convert our ascii hash to binary to be able to validate. knownpasswdhash=knownpasswdhash.decode("hex") if hmac.new(challenge,knownpasswdhash).digest() == processedpassword: print "ALLOWED", login connection.authenticated=login # store for later reference by Pyro object return(1,0) print "DENIED",login return (0,Pyro.constants.DENIED_SECURITY) def createAuthToken(self, authid, challenge, peeraddr, URI, daemon): # authid is what mungeIdent returned, a tuple (login, hash-of-password) # we return a secure auth token based on the server challenge string. return "%s:%s" % (authid[0], hmac.new(challenge,authid[1]).digest() ) def mungeIdent(self, ident): # ident is tuple (login, password), the client sets this. # we don't like to store plaintext passwords so store the md5 hash instead. return (ident[0], md5(ident[1]).digest()) Pyro-3.14/examples/user_passwd_auth/Readme.txt0000644000076500000240000000072011517657577022007 0ustar irmenstaff00000000000000Example of username + password connection Authentication. This example uses a custom connection validator that checks username + password before a client can connect. Notice that you don't have to restrict yourself to a single string that is passed as identification, it can be any python object (in this case, a login/password tuple, where the password is munged to avoid storing it in plaintext). The connection validator is used both by client and server. Pyro-3.14/examples/user_passwd_auth/server.py0000644000076500000240000000203011517657577021725 0ustar irmenstaff00000000000000#!/usr/bin/env python import Pyro.naming import Pyro.core import connvalidator class testobject(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) def method(self,arg): caller = self.getLocalStorage().caller # TCPConnection of the caller login = caller.authenticated # set by the conn.validator return "You are '%s' and you were allowed to connect." % login Pyro.core.initServer() ns = Pyro.naming.NameServerLocator().getNS() daemon = Pyro.core.Daemon() daemon.useNameServer(ns) daemon.setNewConnectionValidator( connvalidator.UserLoginConnValidator() ) daemon.connect(testobject(),'authentication') print "---\nfor reference: the following users and passwords are recognised:" print "user: root password: change_me" print "user: irmen password: secret" print "user: guest password: guest" print "(this table is printed for sake of example; the passwords" print " are not stored in plain text but as md5 hashes)" print # enter the service loop. print 'Server started.' daemon.requestLoop() Pyro-3.14/LICENSE0000644000076500000240000000247511517657604013660 0ustar irmenstaff00000000000000 PYRO - Python Remote Objects Software License, copyright, and disclaimer PYRO is Copyright (c) by Irmen de Jong (irmen@razorvine.net) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This is the "MIT Software License" which is OSI-certified, and GPL-compatible. See http://www.opensource.org/licenses/mit-license.php Pyro-3.14/MANIFEST.in0000644000076500000240000000023311517657604014377 0ustar irmenstaff00000000000000include LICENSE include Pyro.conf include MANIFEST.in recursive-include bin * recursive-include examples * recursive-include docs * global-exclude */CVS/* Pyro-3.14/PKG-INFO0000644000076500000240000000234011567263340013732 0ustar irmenstaff00000000000000Metadata-Version: 1.0 Name: Pyro Version: 3.14 Summary: distributed object middleware for Python (IPC/RPC), version 3.x Home-page: http://www.xs4all.nl/~irmen/pyro3/ Author: Irmen de Jong Author-email: irmen@razorvine.net License: MIT Description: Pyro stands for PYthon Remote Objects. It is an advanced and powerful Distributed Object Technology system written entirely in Python, that is designed to be very easy to use. This is version 3.x of Pyro, the stable version. For a more modern version with new features, look at Pyro4 instead. Keywords: distributed objects,middleware,network communication,DOT,RMI,IPC Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Development Status :: 6 - Mature Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Topic :: Software Development :: Object Brokering Classifier: Topic :: System :: Distributed Computing Classifier: Topic :: System :: Networking Pyro-3.14/Pyro/0000755000076500000240000000000011567263336013574 5ustar irmenstaff00000000000000Pyro-3.14/Pyro/__init__.py0000644000076500000240000000223011537206303015666 0ustar irmenstaff00000000000000############################################################################# # # Pyro file to make Pyro a package, and to set up configuration. # # This is part of "Pyro" - Python Remote Objects # Which is (c) Irmen de Jong - irmen@razorvine.net # # Note: to see what Pyro version this is, print Pyro.constants.VERSION # ############################################################################# # Initialize Pyro Configuration. # # This is put here because it could actually initialize config stuff needed # even before the code calls core.initClient or core.initServer. # # Pyro.config is a class, which has a __getattr__ member, so all # pyro code can use Pyro.config. to look up a value. # This allows for tweaking the configuration lookups by writing # a custom __getattr__ and/or __init__ for the class. # However, currently the class initializer adds configuration items # as regular class data members. import os import Pyro.configuration config = Pyro.configuration.Config() try: confFile = os.environ['PYRO_CONFIG_FILE'] except KeyError: confFile = '' if not confFile and os.path.isfile('Pyro.conf'): confFile='Pyro.conf' config.setup(confFile) Pyro-3.14/Pyro/configuration.py0000644000076500000240000001620211537206303017002 0ustar irmenstaff00000000000000############################################################################# # # Sets up Pyro's configuration (Pyro.config). # # This is part of "Pyro" - Python Remote Objects # Which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# # Initialize Pyro Configuration. import re, os, random, tempfile import Pyro.errors from Pyro.errors import PyroError import Pyro.constants import Pyro.util2 # not util because of cyclic dependency try: from pickle import HIGHEST_PROTOCOL as PICKLE_HIGHEST_PROTOCOL except ImportError: PICKLE_HIGHEST_PROTOCOL = 1 # ---------------------- DEFAULT CONFIGURATION VARIABLES ----------- # Special characters are '$CURDIR' (current directory, absolute) and # $STORAGE which is replaced by the PYRO_STORAGE path. _defaults= { 'PYRO_STORAGE': '$CURDIR', # current dir (abs) 'PYRO_HOST': '', 'PYRO_PUBLISHHOST': None, 'PYRO_PORT': 7766, 'PYRO_PORT_RANGE': 100, 'PYRO_NS_HOSTNAME': None, 'PYRO_NS_PORT': 9090, # tcp 'PYRO_NS_BC_ADDR': None, 'PYRO_NS_BC_PORT': 9090, # udp 'PYRO_NS2_HOSTNAME': None, 'PYRO_NS2_PORT': 9091, # tcp 'PYRO_NS2_BC_ADDR': None, 'PYRO_NS2_BC_PORT': 9091, # udp 'PYRO_NS_URIFILE': '$STORAGE/Pyro_NS_URI', # (abs) 'PYRO_NS_DEFAULTGROUP': ':Default', 'PYRO_BC_RETRIES': 1, 'PYRO_BC_TIMEOUT': 0.75, 'PYRO_PICKLE_FORMAT': PICKLE_HIGHEST_PROTOCOL, 'PYRO_XML_PICKLE': None, 'PYRO_GNOSIS_PARANOIA': 0, 'PYRO_TRACELEVEL': 0, 'PYRO_USER_TRACELEVEL': 0, 'PYRO_LOGFILE': '$STORAGE/Pyro_log', # (abs) 'PYRO_USER_LOGFILE': '$STORAGE/Pyro_userlog', # (abs) 'PYRO_STDLOGGING': 0, 'PYRO_STDLOGGING_CFGFILE': 'logging.cfg', 'PYRO_MAXCONNECTIONS': 200, 'PYRO_TCP_LISTEN_BACKLOG': 200, 'PYRO_BROKEN_MSGWAITALL': 0, 'PYRO_MULTITHREADED': 1, # assume 1 'PYRO_COMPRESSION': 0, 'PYRO_MOBILE_CODE': 0, 'PYRO_DNS_URI': 0, 'PYRO_CHECKSUM': 0, 'PYRO_SOCK_KEEPALIVE': 1, 'PYRO_ES_QUEUESIZE': 1000, 'PYRO_ES_BLOCKQUEUE': 1, 'PYRO_DETAILED_TRACEBACK': 0, 'PYRO_ONEWAY_THREADED': 1, 'PYROSSL_CERTDIR': '$STORAGE/certs', # (abs) 'PYROSSL_CA_CERT': 'ca.pem', 'PYROSSL_CERT': 'host.pem', 'PYROSSL_KEY': None, 'PYROSSL_POSTCONNCHECK': 1 } # ---------------------- END OF DEFAULT CONFIGURATION VARIABLES ----- class Config(object): def __init__(self): _defaults['PYRO_MULTITHREADED']=Pyro.util2.supports_multithreading() self.__dict__[Pyro.constants.CFGITEM_PYRO_INITIALIZED] = 0 def __eq__(self, other): return self.__dict__==other.__dict__ def setup(self, configFile): reader = ConfigReader(_defaults) try: reader.parse(configFile) except EnvironmentError,x: raise PyroError("Error reading config file: "+configFile+"; "+str(x)); self.__dict__.update(reader.items) if configFile: self.__dict__['PYRO_CONFIG_FILE'] = os.path.abspath(configFile) else: self.__dict__['PYRO_CONFIG_FILE'] = '' def finalizeConfig_Client(self): # For the client, we're done for now! # It's nice if the storage directory exists and is # writable, but if it isn't, we can continue happily. # If Pyro needs to write something (log?), it will # fail at that point if it can't access the storage dir. # This behavior is good enough for clients. pass def finalizeConfig_Server(self, storageCheck): if storageCheck: # The server needs a storage dir. Because it's a server, # this usually is no problem. So create & test it here. # Create the storage directory if it doesn't exist yet if not os.path.exists(self.PYRO_STORAGE): os.mkdir(self.PYRO_STORAGE) # see if we have permission there, in a thread-safe fashion. if not os.path.isdir(self.PYRO_STORAGE): raise IOError('PYRO_STORAGE is not a directory ['+self.PYRO_STORAGE+']') try: if os.name=='java': # jython doesn't have suitable TemporaryFile implementation (lacks dir param) javatestfile=os.path.join(self.PYRO_STORAGE,'_pyro_'+str(random.random())+".tmp") f=open(javatestfile,"w") else: # use tempfile to safely create a unique temporary file even on multi-cpu nodes f=tempfile.TemporaryFile(dir=self.PYRO_STORAGE, suffix='.tmp', prefix='_pyro_') except Exception,x: print x raise IOError('no write access to PYRO_STORAGE ['+self.PYRO_STORAGE+']') else: f.close() if os.name=='java': os.remove(javatestfile) # def __getattr__(self,name): # # add smart code here to deal with other requested config items! class ConfigReader(object): def __init__(self, defaults): self.matcher=re.compile(r'^(\w+)\s*=\s*(\S*)') self.items=defaults.copy() def _check(self, filename): print "ConfigReader: checking file", filename items=[] for l in open(filename).readlines(): l=l.rstrip() if not l or l.startswith('#'): continue # skip empty line or comment match=self.matcher.match(l) if match: items.append(match.group(1)) allitems=self.items.keys() allitems.sort() for item in allitems: if item not in items: print "MISSING item: ",item try: items.remove(item) except ValueError: pass if items: print "items NOT in DEFAULTS:", items else: print "ok!" def parse(self, filename): linenum=0 if filename: for l in open(filename).readlines(): l=l.rstrip() linenum=linenum+1 if not l or l.startswith('#'): continue # skip empty line or comment match=self.matcher.match(l) if match: if match.group(1) in _defaults.keys(): if match.group(2): self.items[match.group(1)] = match.group(2) else: raise KeyError('Unknown config item in configfile (line %d): %s' % (linenum, match.group(1))) else: raise ValueError('Syntax error in config file, line '+str(linenum)) # Parse the environment variables (they override the config file) self.items.update(self.processEnv(_defaults.keys())) # First, fix up PYRO_STORAGE because others depend on it. self.items['PYRO_STORAGE'] = self.treatSpecial(self.items['PYRO_STORAGE']) # Now fix up all other items: for i in self.items.keys(): newVal = self.treatSpecial(self.items[i]) if i in ('PYRO_STORAGE', 'PYRO_LOGFILE', 'PYRO_USER_LOGFILE', 'PYRO_NS_URIFILE'): newVal=os.path.abspath(newVal) # fix the variable type if it's an integer or float if type(_defaults[i]) == type(42): newVal = int(newVal) if type(_defaults[i]) == type(0.1): newVal = float(newVal) self.items[i]= newVal def processEnv(self, keys): env={} for key in keys: try: env[key] = os.environ[key] except KeyError: pass return env def treatSpecial(self, value): # treat special escape strings if type(value)==type(""): if value=='$CURDIR': return os.curdir elif value.startswith('$STORAGE/'): return os.path.join(self.items['PYRO_STORAGE'], value[9:]) return value # easy config diagnostic with python -m if __name__=="__main__": print "Pyro version:",Pyro.constants.VERSION r=ConfigReader(_defaults) if os.path.exists("Pyro.conf"): r._check("Pyro.conf") x=Config() if os.path.exists("Pyro.conf"): x.setup("Pyro.conf") else: x.setup(None) x.finalizeConfig_Server(1) items=vars(x).items() items.sort() print "Active configuration settings:" for item,value in items: print item+"="+str(value) Pyro-3.14/Pyro/constants.py0000644000076500000240000000252711567263106016163 0ustar irmenstaff00000000000000############################################################################# # # Pyro global constants # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# # General Pyro Version String #### VERSION = '3.14' # Fixed (internal) GUIDs INTERNAL_DAEMON_GUID='c0000000'+'01100000'+'10000000'+'10000001' # Pyro names for the standard Services NAMESERVER_NAME = ":Pyro.NameServer" EVENTSERVER_NAME = ":Pyro.EventService" # Pyro traceback attribute for remote exceptions TRACEBACK_ATTRIBUTE = "remote_stacktrace" #### Remote Invocation Flags (bit flags) #### RIF_Varargs = (1<<0) # for '*args' syntax RIF_Keywords = (1<<1) # for '**keywords' syntax RIF_Oneway = (1<<2) # for oneway (no result) messages - currently internal use only RIF_VarargsAndKeywords = RIF_Varargs | RIF_Keywords #### Reasons why a connection may be denied #### DENIED_UNSPECIFIED=0 DENIED_SERVERTOOBUSY=1 DENIED_HOSTBLOCKED=2 DENIED_SECURITY=3 deniedReasons={ DENIED_UNSPECIFIED:'unspecified reason', DENIED_SERVERTOOBUSY:'server too busy', DENIED_HOSTBLOCKED:'host blocked', DENIED_SECURITY:'security reasons' } # special config items CFGITEM_PYRO_INITIALIZED = "_PYRO_INITIALIZED" # NS roles NSROLE_SINGLE=0 NSROLE_PRIMARY=1 NSROLE_SECONDARY=2 Pyro-3.14/Pyro/core.py0000644000076500000240000007646311537206303015102 0ustar irmenstaff00000000000000############################################################################# # # Pyro Core Library # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# from __future__ import with_statement import sys, time, re, os, weakref import imp, marshal, new, socket from pickle import PicklingError import Pyro.constants, Pyro.util, Pyro.protocol, Pyro.errors from Pyro.errors import * from types import UnboundMethodType, MethodType, BuiltinMethodType, TupleType, StringType, UnicodeType if Pyro.util.supports_multithreading(): import threading Log=Pyro.util.Log def _checkInit(pyrotype="client"): if not getattr(Pyro.config, Pyro.constants.CFGITEM_PYRO_INITIALIZED): # If Pyro has not been initialized explicitly, do it automatically. if pyrotype=="server": initServer() else: initClient() ############################################################################# # # ObjBase - Server-side object implementation base class # or master class with the actual object as delegate # # SynchronizedObjBase - Just the same, but with synchronized method # calls (thread-safe). # ############################################################################# class ObjBase(object): def __init__(self): self.objectGUID=Pyro.util.getGUID() self.delegate=None self.lastUsed=time.time() # for later reaping unused objects if Pyro.config.PYRO_MOBILE_CODE: self.codeValidator=lambda n,m,a: 1 # always accept def GUID(self): return self.objectGUID def setGUID(self, guid): # used with persistent name server self.objectGUID = guid def delegateTo(self,delegate): self.delegate=delegate def setPyroDaemon(self, daemon): # This will usually introduce a cyclic reference between the # object and the daemon. Use a weak ref if available. # NOTE: if you correctly clean up the object (that is, disconnect it from the daemon) # the cyclic reference is cleared correctly, and no problem occurs. # NOTE: you have to make sure your original daemon object doesn't get garbage collected # if you still want to use the objects! You have to keep a ref. to the daemon somewhere. if daemon: self.daemon=weakref.proxy(daemon) else: self.daemon=None def setCodeValidator(self, v): if not callable(v): raise TypeError("codevalidator must be a callable object") self.codeValidator=v def getDaemon(self): return self.daemon def getLocalStorage(self): return self.daemon.getLocalStorage() def _gotReaped(self): # Called when daemon reaps this object due to unaccessed time # Override this method if needed; to act on this event pass def getProxy(self): return self.daemon.getProxyForObj(self) def getAttrProxy(self): return self.daemon.getAttrProxyForObj(self) def Pyro_dyncall(self, method, flags, args): # update the timestamp self.lastUsed=time.time() # find the method in this object, and call it with the supplied args. keywords={} if flags & Pyro.constants.RIF_Keywords: # reconstruct the varargs from a tuple like # (a,b,(va1,va2,va3...),{kw1:?,...}) keywords=args[-1] args=args[:-1] if flags & Pyro.constants.RIF_Varargs: # reconstruct the varargs from a tuple like (a,b,(va1,va2,va3...)) args=args[:-1]+args[-1] if keywords and type(keywords.iterkeys().next()) is unicode and sys.platform!="cli": # IronPython sends all strings as unicode, but apply() doesn't grok unicode keywords. # So we need to rebuild the keywords dict with str keys... keywords = dict([(str(k),v) for k,v in keywords.iteritems()]) # If the method is part of ObjBase, never call the delegate object because # that object doesn't implement that method. If you don't check this, # remote attributes won't work with delegates for instance, because the # delegate object doesn't implement _r_xa. (remote_xxxattr) if method in dir(ObjBase): return getattr(self,method) (*args,**keywords) else: # try..except to deal with obsoleted string exceptions (raise "blahblah") try : return getattr(self.delegate or self,method) (*args,**keywords) except : exc_info = sys.exc_info() try: if type(exc_info[0]) == StringType : if exc_info[1] == None : raise Exception, exc_info[0], exc_info[2] else : raise Exception, "%s: %s" % (exc_info[0], exc_info[1]), exc_info[2] else : raise finally: del exc_info # delete frame to allow proper GC # remote getattr/setattr support: def _r_ha(self, attr): try: attr = getattr(self.delegate or self,attr) if type(attr) in (UnboundMethodType, MethodType, BuiltinMethodType): return 1 # method except: pass return 2 # attribute def _r_ga(self, attr): return getattr(self.delegate or self, attr) def _r_sa(self, attr, value): setattr(self.delegate or self, attr, value) # remote code downloading support (server downloads from client): def remote_supply_code(self, name, module, sourceaddr): # XXX this is nasty code, and also duplicated in protocol.py _retrieveCode() if Pyro.config.PYRO_MOBILE_CODE and self.codeValidator(name,module,sourceaddr): try: imp.acquire_lock() # threadsafe imports if name in sys.modules and getattr(sys.modules[name],'_PYRO_bytecode',None): # already have this module, don't import again # we checked for the _PYRO_bytecode attribute because that is only # present when all loading code below completed successfully return Log.msg('ObjBase','loading supplied code: ',name,'from',str(sourceaddr)) if module[0:4]!=imp.get_magic(): # compile source code code=compile(module,'','exec') else: # read bytecode from the client code=marshal.loads(module[8:]) # make the module hierarchy and add all names to sys.modules name=name.split('.') path='' mod=new.module("pyro-agent-context") for m in name: path+='.'+m # use already loaded modules instead of overwriting them real_path = path[1:] if sys.modules.has_key(real_path): mod = sys.modules[real_path] else: setattr(mod,m,new.module(path[1:])) mod=getattr(mod,m) sys.modules[path[1:]]=mod # execute the module code in the right module. exec code in mod.__dict__ # store the bytecode for possible later reference if we need to pass it on mod.__dict__['_PYRO_bytecode'] = module finally: imp.release_lock() else: Log.warn('ObjBase','attempt to supply code denied: ',name,'from',str(sourceaddr)) raise PyroError('attempt to supply code denied') # remote code retrieve support (client retrieves from server): def remote_retrieve_code(self, name): # XXX codeValidator: can we somehow get the client's address it is sent to? # XXX this code is ugly. And duplicated in protocol.py remoteInvocation. if Pyro.config.PYRO_MOBILE_CODE and self.codeValidator(name,None,None): Log.msg("ObjBase","supplying code: ",name) try: importmodule=new.module("pyro-server-import") try: exec "import " + name in importmodule.__dict__ except ImportError: Log.error("ObjBase","Client wanted a non-existing module:", name) raise PyroError("Client wanted a non-existing module", name) m=eval("importmodule."+name) # try to load the module's compiled source, or the real .py source if that fails. # note that the source code (.py) is opened with universal newline mode (filebase,ext)=os.path.splitext(m.__file__) if ext.startswith(".PY"): exts = ( (".PYO","rb"), (".PYC","rb"), (".PY","rU") ) # uppercase else: exts = ( (".pyo","rb"), (".pyc","rb"), (".py","rU") ) # lowercase for ext,mode in exts: try: m=open(filebase+ext, mode).read() return m # supply the module to the client! except: pass Log.error("ObjBase","cannot read module source code for module:", name) raise PyroError("cannot read module source code") finally: del importmodule else: Log.error("ObjBase","attempt to retrieve code denied:", name) raise PyroError("attempt to retrieve code denied") class SynchronizedObjBase(ObjBase): def __init__(self): ObjBase.__init__(self) self.synlock=Pyro.util.getLockObject() def Pyro_dyncall(self, method, flags, args): with self.synlock: return ObjBase.Pyro_dyncall(self, method,flags,args) # Use this class instead if you're using callback objects and you # want to see local exceptions. (otherwise they go back to the calling server...) class CallbackObjBase(ObjBase): def __init__(self): ObjBase.__init__(self) def Pyro_dyncall(self, method, flags, args): try: return ObjBase.Pyro_dyncall(self,method,flags,args) except Exception,x: # catch all errors Log.warn('CallbackObjBase','Exception in callback object: ',x) raise PyroExceptionCapsule(x,str(x)) ############################################################################# # # PyroURI - Pyro Universal Resource Identifier # # This class represents a Pyro URI (which consists of four parts, # a protocol identifier, an IP address, a portnumber, and an object ID. # # The URI can be converted to a string representation (str converter). # The URI can also be read back from such a string (reinitFromString). # The URI can be initialised from its parts (init). # The URI can be initialised from a string directly, if the init # code detects a ':' and '/' in the host argument (which is then # assumed to be a string URI, not a host name/ IP address). # ############################################################################# class PyroURI(object): def __init__(self,host,objectID=0,port=0,prtcol='PYRO'): # if the 'host' arg is a PyroURI, copy contents if isinstance(host, PyroURI): self.init(host.address, host.objectID, host.port, host.protocol) else: # If the 'host' arg contains '://', assume it's an URI string. if host.find('://')>0: self.reinitFromString(host) else: if not objectID: raise URIError('invalid URI format') self.init(host, objectID, port, prtcol) def __str__(self): return self.protocol+'://'+self.address+':'+str(self.port)+'/'+self.objectID def __repr__(self): return '' def __hash__(self): # XXX this is handy but not safe. If the URI changes, the object will be in the wrong hash bucket. return hash(str(self)) def __cmp__(self, o): return cmp(str(self), str(o)) def clone(self): return PyroURI(self) def init(self,host,objectID,port=0,prtcol='PYRO'): if '/' in host: raise URIError('malformed hostname') if Pyro.config.PYRO_DNS_URI: self.address = host else: self.address=Pyro.protocol.getIPAddress(host) if not self.address: raise URIError('unknown host') if port: if type(port)==type(1): self.port=port else: raise TypeError("port must be integer") else: self.port=Pyro.config.PYRO_PORT self.protocol=prtcol self.objectID=objectID def reinitFromString(self,arg): if arg.startswith('PYROLOC') or arg.startswith('PYRONAME'): uri=processStringURI(arg) self.init(uri.address,uri.objectID,uri.port,uri.protocol) return x=re.match(r'(?P[^\s:/]+)://(?P[^\s:]+):?(?P\d+)?/(?P\S*)',arg) if x: port=None if x.group('port'): port=int(x.group('port')) self.init(x.group('hostname'), x.group('id'), port, x.group('protocol')) return Log.error('PyroURI','invalid URI format passed: '+arg) raise URIError('invalid URI format') def getProxy(self): return DynamicProxy(self) def getAttrProxy(self): return DynamicProxyWithAttrs(self) # # This method takes a string representation of a Pyro URI # and parses it. If it's a meta-protocol URI such as # PYRONAME://.... it will do what is needed to make # a regular PYRO:// URI out of it (resolve names etc). # def processStringURI(URI): # PYRONAME(SSL)://[hostname[:port]/]objectname x=re.match(r'(?PPYRONAME|PYRONAMESSL)://(((?P[^\s:]+):(?P\d+)/)|((?P[^\s:]+)/))?(?P\S*)',URI) if x: protocol=x.group('protocol') if protocol=="PYRONAMESSL": raise ProtocolError("NOT SUPPORTED YET: "+protocol) # XXX obviously, this should be implemented hostname=x.group('hostname') or x.group('onlyhostname') port=x.group('port') name=x.group('name') import Pyro.naming loc=Pyro.naming.NameServerLocator() if port: port=int(port) NS=loc.getNS(host=hostname,port=port) return NS.resolve(name) # PYROLOC(SSL)://hostname[:port]/objectname x=re.match(r'(?PPYROLOC|PYROLOCSSL)://(?P[^\s:]+):?(?P\d+)?/(?P\S*)',URI) if x: protocol=x.group('protocol') hostname=x.group('hostname') port=x.group('port') if port: port=int(port) else: port=0 name=x.group('name') return PyroURI(hostname,name,port,protocol) if URI.startswith('PYROLOC') or URI.startswith('PYRONAME'): # hmm should have matched above. Likely invalid. raise URIError('invalid URI format') # It's not a meta-protocol such as PYROLOC or PYRONAME, # let the normal Pyro URI deal with it. # (it can deal with regular PYRO: and PYROSSL: protocols) return PyroURI(URI) ############################################################################# # # DynamicProxy - dynamic Pyro proxy # # Can be used by clients to invoke objects for which they have no # precompiled proxy. # ############################################################################# def getProxyForURI(URI): return DynamicProxy(URI) def getAttrProxyForURI(URI): return DynamicProxyWithAttrs(URI) class _RemoteMethod(object): # method call abstraction, adapted from Python's xmlrpclib # it would be rather easy to add nested method calls, but # that is not compatible with the way that Pyro's method # calls are defined to work ( no nested calls ) def __init__(self, send, name): self.__send = send self.__name = name def __call__(self, *args, **kwargs): return self.__send(self.__name, args, kwargs) class DynamicProxy(object): def __init__(self, URI): _checkInit() # init required if type(URI) in (StringType,UnicodeType): URI=processStringURI(URI) self.URI = URI self.objectID = URI.objectID # Delay adapter binding to enable transporting of proxies. # We just create an adapter, and don't connect it... self.adapter = Pyro.protocol.getProtocolAdapter(self.URI.protocol) # ---- don't forget to register local vars with DynamicProxyWithAttrs, see below def __del__(self): try: self.adapter.release(nolog=1) except (AttributeError, RuntimeError): pass def _setIdentification(self, ident): self.adapter.setIdentification(ident) def _setNewConnectionValidator(self, validator): self.adapter.setNewConnectionValidator(validator) def _setOneway(self, methods): if type(methods) not in (type([]), type((0,))): methods=(methods,) self.adapter.setOneway(methods) def _setTimeout(self,timeout): self.adapter.setTimeout(timeout) def _transferThread(self, newOwnerThread=None): pass # dummy function to retain API compatibility with Pyro 3.7 def _release(self): if self.adapter: self.adapter.release() def _local(self): return self.URI._local() def _islocal(self): return self.URI._islocal() def __copy__(self): # create copy of current proxy object proxyCopy = DynamicProxy(self.URI) proxyCopy.adapter.setIdentification(self.adapter.getIdentification(), munge=False) # copy identification info proxyCopy._setTimeout(self.adapter.timeout) proxyCopy._setOneway(self.adapter.onewayMethods) proxyCopy._setNewConnectionValidator(self.adapter.getNewConnectionValidator()) return proxyCopy def __deepcopy__(self, arg): raise PyroError("cannot deepcopy a proxy") def __getattr__(self, name): if name in ("__getnewargs__","__getinitargs__"): # allows it to be safely pickled raise AttributeError() return _RemoteMethod(self._invokePYRO, name) def __repr__(self): return "<"+self.__class__.__name__+" for "+str(self.URI)+">" def __str__(self): return repr(self) def __hash__(self): # makes it possible to use this class as a key in a dict return hash(self.objectID) def __eq__(self,other): # makes it possible to compare two proxies using objectID return hasattr(other,"objectID") and self.objectID==other.objectID def __ne__(self,other): # makes it possible to compare two proxies using objectID return not hasattr(other,"objectID") or self.objectID!=other.objectID def __nonzero__(self): return 1 def __coerce__(self,other): # makes it possible to compare two proxies using objectID (cmp) if hasattr(other,"objectID"): return (self.objectID, other.objectID) return None def _invokePYRO(self, name, vargs, kargs): if not self.adapter.connected(): # rebind here, don't do it from inside the remoteInvocation because deadlock will occur self.adapter.bindToURI(self.URI) return self.adapter.remoteInvocation(name, Pyro.constants.RIF_VarargsAndKeywords, vargs, kargs) # Pickling support, otherwise pickle uses __getattr__: def __getstate__(self): # for pickling, return a non-connected copy of ourselves: cpy = self.__copy__() cpy._release() return cpy.__dict__ def __setstate__(self, args): # for pickling, to restore the pickled state self.__dict__.update(args) class DynamicProxyWithAttrs(DynamicProxy): _local_attrs = ("_local_attrs","URI", "objectID", "adapter", "_attr_cache") def __init__(self, URI): self._attr_cache = {} DynamicProxy.__init__(self, URI) def _r_ga(self, attr, value=0): if value: return _RemoteMethod(self._invokePYRO, "_r_ga") (attr) # getattr else: return _RemoteMethod(self._invokePYRO, "_r_ha") (attr) # hasattr def findattr(self, attr): if attr in self._attr_cache.keys(): return self._attr_cache[attr] # look it up and cache the value self._attr_cache[attr] = self._r_ga(attr) return self._attr_cache[attr] def __copy__(self): # create copy of current proxy object return DynamicProxyWithAttrs(self.URI) def __setattr__(self, attr, value): if attr in self._local_attrs: self.__dict__[attr]=value else: result = self.findattr(attr) if result==2: # attribute return _RemoteMethod(self._invokePYRO, "_r_sa") (attr,value) else: raise AttributeError('not an attribute') def __getattr__(self, attr): # allows it to be safely pickled if attr not in ("__getnewargs__","__getinitargs__", "__hash__","__eq__","__ne__") and attr not in self._local_attrs: result=self.findattr(attr) if result==1: # method return _RemoteMethod(self._invokePYRO, attr) elif result: return self._r_ga(attr, 1) raise AttributeError ############################################################################# # # Daemon - server-side Pyro daemon # # Accepts and dispatches incoming Pyro method calls. # ############################################################################# # The pyro object that represents the daemon. # The daemon is not directly remotely accessible, for security reasons. class DaemonServant(ObjBase): def __init__(self, daemon): ObjBase.__init__(self) self.daemon=weakref.proxy(daemon) def getRegistered(self): return self.daemon.getRegistered() def ResolvePYROLOC(self, name): return self.daemon.ResolvePYROLOC(name) # The daemon itself: class Daemon(Pyro.protocol.TCPServer, ObjBase): def __init__(self,prtcol='PYRO',host=None,port=0,norange=0,publishhost=None): ObjBase.__init__(self) self.NameServer = None self.connections=[] _checkInit("server") # init required self.setGUID(Pyro.constants.INTERNAL_DAEMON_GUID) self.implementations={Pyro.constants.INTERNAL_DAEMON_GUID:(DaemonServant(self),'__PYRO_Internal_Daemon')} self.persistentConnectedObjs=[] # guids self.transientsCleanupAge=0 self.transientsMutex=Pyro.util.getLockObject() self.nscallMutex=Pyro.util.getLockObject() if host is None: host=Pyro.config.PYRO_HOST if publishhost is None: publishhost=Pyro.config.PYRO_PUBLISHHOST # Determine range scanning or random port allocation if norange: # Fixed or random port allocation # If port is zero, OS will randomly assign, otherwise, # attempt to use the provided port value self.port = port portrange = 1 else: # Scanning port allocation if port: self.port = port else: self.port = Pyro.config.PYRO_PORT portrange=Pyro.config.PYRO_PORT_RANGE if not publishhost: publishhost=host errormsg='' for i in range(portrange): try: Pyro.protocol.TCPServer.__init__(self, self.port, host, Pyro.config.PYRO_MULTITHREADED,prtcol) if not self.port: # If we bound to an OS provided port, report it self.port = self.sock.getsockname()[1] self.hostname = publishhost or Pyro.protocol.getHostname() self.protocol = prtcol self.adapter = Pyro.protocol.getProtocolAdapter(prtcol) self.validateHostnameAndIP() # ignore any result message... it's in the log already. return except ProtocolError,msg: errormsg=msg self.port+=1 Log.error('Daemon','Couldn\'t start Pyro daemon: ' +str(errormsg)) raise DaemonError('Couldn\'t start Pyro daemon: ' +str(errormsg)) # to be called to stop all connections and shut down. def shutdown(self, disconnect=False): Pyro.protocol.TCPServer.shutdown(self) if disconnect: self.__disconnectObjects() def __disconnectObjects(self): # server shutting down, unregister all known objects in the NS if self.NameServer and Pyro and Pyro.constants: with self.nscallMutex: if Pyro.constants.INTERNAL_DAEMON_GUID in self.implementations: del self.implementations[Pyro.constants.INTERNAL_DAEMON_GUID] if self.implementations: Log.warn('Daemon','Shutting down but there are still',len(self.implementations),'objects connected - disconnecting them') for guid in self.implementations.keys(): if guid not in self.persistentConnectedObjs: (obj,name)=self.implementations[guid] if name: try: self.NameServer.unregister(name) except Exception,x: Log.warn('Daemon','Error while unregistering object during shutdown:',x) self.implementations={} def __del__(self): self.__disconnectObjects() # unregister objects try: del self.adapter Pyro.protocol.TCPServer.__del__(self) except (AttributeError, RuntimeError): pass def __str__(self): return '' def __getstate__(self): raise PicklingError('no access to the daemon') def validateHostnameAndIP(self): # Checks if hostname is sensible. Returns None if it is, otherwise a message # telling what's wrong if it isn't too serious. If things are really bad, # expect an exception to be raised. Things are logged too. if not self.hostname: Log.error("Daemon","no hostname known") raise socket.error("no hostname known for daemon") if self.hostname!="localhost": ip = Pyro.protocol.getIPAddress(self.hostname) if ip is None: Log.error("Daemon","no IP address known") raise socket.error("no IP address known for daemon") if not ip.startswith("127.0."): return None # this is good! # 127.0.x.x or 'localhost' is a warning situation! msg="daemon bound on hostname that resolves to loopback address 127.0.x.x" Log.warn("Daemon",msg) Log.warn("Daemon","hostname="+self.hostname) return msg def useNameServer(self,NS): self.NameServer=NS def getNameServer(self): return self.NameServer def setTimeout(self, timeout): self.adapter.setTimeout(timeout) def setAllowedIdentifications(self, ids): self.getNewConnectionValidator().setAllowedIdentifications(ids) def setTransientsCleanupAge(self, secs): self.transientsCleanupAge=secs if self.threaded: Log.msg('Daemon','creating Grim Reaper thread for transients, timeout=',secs) reaper=threading.Thread(target=self._grimReaper) reaper.setDaemon(1) # thread must exit at program termination. reaper.start() def _grimReaper(self): # this runs in a thread. while self.transientsCleanupAge>0: time.sleep(self.transientsCleanupAge/5) self.reapUnusedTransients() def getProxyForObj(self, obj): return DynamicProxy( PyroURI(self.hostname, obj.GUID(), prtcol=self.protocol, port=self.port) ) def getAttrProxyForObj(self, obj): return DynamicProxyWithAttrs( PyroURI(self.hostname, obj.GUID(), prtcol=self.protocol, port=self.port) ) def connectPersistent(self, obj, name=None): # when a persistent entry is found in the NS, that URI is # used instead of the supplied one, if the address matches. if name and self.NameServer: with self.nscallMutex: try: newURI = PyroURI(self.hostname, obj.GUID(), prtcol=self.protocol, port=self.port) URI=self.NameServer.resolve(name) if (URI.protocol,URI.address,URI.port)==(newURI.protocol,newURI.address,newURI.port): # reuse the previous object ID obj.setGUID(URI.objectID) # enter the (object,name) in the known impl. dictionary self.implementations[obj.GUID()]=(obj,name) self.persistentConnectedObjs.append(obj.GUID()) obj.setPyroDaemon(self) return URI else: # name exists, but address etc. is wrong. Remove it. # then continue so it wil be re-registered. try: self.NameServer.unregister(name) except NamingError: pass except NamingError: pass # Register normally. self.persistentConnectedObjs.append(obj.GUID()) return self.connect(obj, name) def connect(self, obj, name=None): URI = PyroURI(self.hostname, obj.GUID(), prtcol=self.protocol, port=self.port) # if not transient, register the object with the NS if name: with self.nscallMutex: if self.NameServer: self.NameServer.register(name, URI) else: Log.warn('Daemon','connecting object without name server specified:',name) # enter the (object,name) in the known implementations dictionary self.implementations[obj.GUID()]=(obj,name) obj.setPyroDaemon(self) return URI def disconnect(self,obj): # obj can be either the object that was registered, or its uid try: if isinstance(obj,Pyro.core.ObjBase): obj_uid=obj.GUID() else: obj_uid=str(obj) if obj_uid==Pyro.constants.INTERNAL_DAEMON_GUID: return # never allow to remove ourselves from the registry if self.NameServer and self.implementations[obj_uid][1]: with self.nscallMutex: # only unregister with NS if it had a name (was not transient) self.NameServer.unregister(self.implementations[obj_uid][1]) del self.implementations[obj_uid] if obj_uid in self.persistentConnectedObjs: self.persistentConnectedObjs.remove(obj_uid) # XXX Clean up connections/threads to this object? # Can't be done because thread/socket is not associated with single object finally: if isinstance(obj,Pyro.core.ObjBase): obj.setPyroDaemon(None) def getRegistered(self): r={} for guid in self.implementations.keys(): r[guid]=self.implementations[guid][1] # keep only the names return r def handleInvocation(self, conn): # overridden from TCPServer # called in both single- and multithreaded mode self.getLocalStorage().caller=conn self.getAdapter().handleInvocation(self, conn) self.reapUnusedTransients() def reapUnusedTransients(self): if not self.transientsCleanupAge: return now=time.time() with self.transientsMutex: for (obj,name) in self.implementations.values()[:]: # use copy of list if not name: # object is transient, reap it if timeout requires so. if (now-obj.lastUsed)>self.transientsCleanupAge: self.disconnect(obj) obj._gotReaped() def handleError(self,conn,onewaycall=False): # overridden from TCPServer try: (exc_type, exc_value, exc_trb) = sys.exc_info() if exc_type==ProtocolError: # Problem with the communication protocol, shut down the connection # XXX is shutting down what we want??? Log.error('Daemon','protocol error occured:',exc_value) Log.error('Daemon','Due to network error: shutting down connection with',conn) self.removeConnection(conn) else: exclist = Pyro.util.formatTraceback(exc_type, exc_value, exc_trb) out =''.join(exclist) Log.warn('Daemon', 'Exception during processing of request from', conn,' type',exc_type, '\n--- traceback of this exception follows:\n', out,'\n--- end of traceback') if exc_type==PyroExceptionCapsule: sys.stdout.flush() # This is a capsuled exception, used with callback objects. # That means we are actually the daemon on the client. # Return the error to the other side and raise exception locally once more. # (with a normal exception, it is not raised locally again!) # only send the exception object if it's not a oneway call. if not onewaycall: self.adapter.returnException(conn,exc_value.excObj,0,exclist) # don't shutdown exc_value.raiseEx() else: # normal exception, only return exception object if it's not a oneway call if not onewaycall: self.adapter.returnException(conn,exc_value,0,exclist) # don't shutdown connection finally: # clean up circular references to traceback info to allow proper GC del exc_type, exc_value, exc_trb def getAdapter(self): # overridden from TCPServer return self.adapter def getLocalObject(self, guid): # return a local object registered with the given guid return self.implementations[guid][0] def getLocalObjectForProxy(self, proxy): # return a local object registered with the guid to which the given proxy points return self.implementations[proxy.objectID][0] def ResolvePYROLOC(self, name): # this gets called from the protocol adapter when # it wants the daemon to resolve a local object name (PYROLOC: protocol) Log.msg('Daemon','resolving PYROLOC name: ',name) for o in self.implementations.keys(): if self.implementations[o][1]==name: return o raise NamingError('no object found by this name',name) ############################################################################# # # Client/Server Init code # ############################################################################# # Has init been performed already? _init_server_done=0 _init_client_done=0 _init_generic_done=0 def _initGeneric_pre(): global _init_generic_done if _init_generic_done: return if Pyro.config.PYRO_TRACELEVEL == 0: return try: out='\n'+'-'*60+' NEW SESSION\n'+time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))+ \ ' Pyro Initializing, version '+Pyro.constants.VERSION+'\n' Log.raw(out) except IOError,e: sys.stderr.write('PYRO: Can\'t write the tracefile '+Pyro.config.PYRO_LOGFILE+'\n'+str(e)) def _initGeneric_post(): global _init_generic_done setattr(Pyro.config, Pyro.constants.CFGITEM_PYRO_INITIALIZED,1) if Pyro.config.PYRO_TRACELEVEL == 0: return try: if not _init_generic_done: out='Configuration settings are as follows:\n' for item in dir(Pyro.config): if item[0:4] =='PYRO': out+=item+' = '+str(Pyro.config.__dict__[item])+'\n' Log.raw(out) Log.raw('Init done.\n'+'-'*70+'\n') except IOError: pass _init_generic_done=1 def initClient(banner=0): global _init_client_done if _init_client_done: return _initGeneric_pre() if Pyro.config.PYRO_TRACELEVEL >0: Log.raw('This is initClient.\n') Pyro.config.finalizeConfig_Client() _initGeneric_post() if banner: print 'Pyro Client Initialized. Using Pyro V'+Pyro.constants.VERSION _init_client_done=1 def initServer(banner=0, storageCheck=1): global _init_server_done if _init_server_done: return _initGeneric_pre() if Pyro.config.PYRO_TRACELEVEL >0: Log.raw('This is initServer.\n') Pyro.config.finalizeConfig_Server(storageCheck=storageCheck) _initGeneric_post() if banner: print 'Pyro Server Initialized. Using Pyro V'+Pyro.constants.VERSION _init_server_done=1 if __name__=="__main__": print "Pyro version:",Pyro.constants.VERSION Pyro-3.14/Pyro/errors.py0000644000076500000240000000663311537206303015456 0ustar irmenstaff00000000000000############################################################################# # # Pyro Exception Types # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# ############################################################################# # PyroError is the Pyro exception type which is used for problems WITHIN Pyro. # User code should NOT use it!! # # NOTE: Any exception occuring in the user code on the server will be catched # and transported to the client, where it is raised just as if it occured # locally. The occurrence is logged in the server side Pyro log. # Pyro will use the [Remote]PyroError exceptions and their subtypes to # indicate that an internal problem occured. ############################################################################# class PyroError(Exception): pass # internal class URIError(PyroError): pass # URI probs class DaemonError(PyroError): pass # daemon probs class ProtocolError(PyroError): pass # protocol adapter class ConnectionClosedError(ProtocolError): pass # connection in adapter is closed class ConnectionDeniedError(ProtocolError): pass # server refused connection class TimeoutError(ConnectionClosedError): pass # communication timeout class NamingError(PyroError): pass # name server class NoModuleError(PyroError): pass # no module found for incoming obj # do NOT use the following yourself: class _InternalNoModuleError(PyroError): def __init__(self, modulename=None, fromlist=None, *args): # note: called without args on Python 2.5+, args will be set by __setstate__ self.modulename=modulename self.fromlist=fromlist PyroError.__init__(* (self,)+args) def __getstate__(self): return { "modulename": self.modulename, "fromlist": self.fromlist } def __setstate__(self, state): self.modulename=state["modulename"] self.fromlist=state["fromlist"] ############################################################################# # # PyroExceptionCapsule - Exception encapsulation. # # This class represents a Pyro exception which can be transported # across the network, and raised on the other side (by invoking raiseEx). # NOTE: the 'real' exception class must be 'known' on the other side! # NOTE2: this class is adapted from exceptions.Exception. # NOTE3: PyroError IS USED FOR ACTUAL PYRO ERRORS. PyroExceptionCapsule # IS ONLY TO BE USED TO TRANSPORT AN EXCEPTION ACROSS THE NETWORK. # NOTE4: It sets a special attribute on the exception that is raised # (constants.TRACEBACK_ATTRIBUTE), this is the *remote* traceback # NOTE5: ---> this class is intentionally not subclassed from Exception, # and also not from object. # Pyro's exception handling depends on this! # ############################################################################# import Pyro.constants class PyroExceptionCapsule: # don't make this a new style class def __init__(self,excObj,args=None): self.excObj = excObj self.args=args # if specified, this is the remote traceback info def raiseEx(self): setattr(self.excObj,Pyro.constants.TRACEBACK_ATTRIBUTE,self.args) raise self.excObj def __str__(self): s=self.excObj.__class__.__name__ if not self.args: return s elif len(self.args) == 1: return s+': '+str(self.args[0]) else: return s+': '+str(self.args) def __getitem__(self, i): return self.args[i] Pyro-3.14/Pyro/EventService/0000755000076500000240000000000011567263336016176 5ustar irmenstaff00000000000000Pyro-3.14/Pyro/EventService/__init__.py0000644000076500000240000000003711563043040020267 0ustar irmenstaff00000000000000# just to make this a package. Pyro-3.14/Pyro/EventService/Clients.py0000644000076500000240000000602411526311451020136 0ustar irmenstaff00000000000000############################################################################# # # Event Service client base classes # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import Pyro.core, Pyro.naming, Pyro.constants import Pyro.EventService.Server from Pyro.EventService.Event import Event from Pyro.errors import * # SUBSCRIBER: subscribes to certain events. class Subscriber(Pyro.core.CallbackObjBase): def __init__(self, ident=None, esURI=None): Pyro.core.CallbackObjBase.__init__(self) Pyro.core.initServer() Pyro.core.initClient() daemon = Pyro.core.Daemon() if esURI: check=Pyro.core.PyroURI(esURI) self.ES_uri=esURI else: locator = Pyro.naming.NameServerLocator(identification=ident) self.NS = locator.getNS(host=Pyro.config.PYRO_NS_HOSTNAME) daemon.useNameServer(self.NS) self.ES_uri = self.NS.resolve(Pyro.constants.EVENTSERVER_NAME) daemon.connect(self) # will also set self.daemon... self.ES_ident=ident self.abortListen=0 self.daemon=daemon # make sure daemon doesn't get garbage collected now def getES(self): # we get a fresh proxy to the ES because of threading issues. # (proxies can not be reused across multiple threads) eventservice=Pyro.core.getProxyForURI(self.ES_uri) eventservice._setIdentification(self.ES_ident) return eventservice def subscribe(self,subjects): # Subscribe to one or more subjects. # It is safe to call this multiple times. self.getES().subscribe(subjects, self.getProxy()) def subscribeMatch(self,subjectPatterns): # Subscribe to one or more subjects (by pattern) # It is safe to call this multiple times. self.getES().subscribeMatch(subjectPatterns, self.getProxy()) def unsubscribe(self, subjects): # Unsubscribe the subscriber for the given subject(s). self.getES().unsubscribe(subjects, self.getProxy()) def abort(self): self.abortListen=1 def setThreading(self, threaded): self.getDaemon().threaded=threaded def listen(self): self.getDaemon().requestLoop(lambda s=self: not s.abortListen) def event(self, event): # callback, override this! print event # PUBLISHER: publishes events. class Publisher(object): def __init__(self, ident=None, esURI=None): Pyro.core.initClient() if esURI: check=Pyro.core.PyroURI(esURI) self.ES_uri=esURI else: locator = Pyro.naming.NameServerLocator(identification=ident) ns = locator.getNS(host=Pyro.config.PYRO_NS_HOSTNAME) self.ES_uri = ns.resolve(Pyro.constants.EVENTSERVER_NAME) ns._release() # be very sure to release the socket self.ES_ident = ident def getES(self): # we get a fresh proxy to the ES because of threading issues. # (proxies can not be reused across multiple threads) eventservice=Pyro.core.getProxyForURI(self.ES_uri) eventservice._setIdentification(self.ES_ident) return eventservice def publish(self, subjects, msg): es=self.getES() es.publish(subjects,msg) es._release() # be very sure to release the socket Pyro-3.14/Pyro/EventService/Event.py0000644000076500000240000000121711526311451017615 0ustar irmenstaff00000000000000############################################################################# # # Event Service client base classes # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import time # EVENT - the thing that is published. Has a subject and contains a message. class Event(object): def __init__(self, subject, msg, creationTime=None): self.msg=msg self.subject=subject self.time=creationTime or time.time() def __str__(self): return "" % (self.subject, time.ctime(self.time), str(self.msg)) Pyro-3.14/Pyro/EventService/Server.py0000644000076500000240000002321511526311451020004 0ustar irmenstaff00000000000000############################################################################# # # Event Service daemon and server classes # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import time, types, re, sys, traceback, os import Pyro.core, Pyro.naming, Pyro.util, Pyro.constants from Pyro.errors import * from Pyro.EventService.Event import Event import Queue from threading import Thread Log=Pyro.util.Log # SUBSCRIBER - each subscriber has one of these worker threads class Subscriber(Thread): def __init__(self, remote): Thread.__init__(self) self.remote=remote # set the callback method to ONEWAY mode: self.remote._setOneway("event") self.queue=Queue.Queue(Pyro.config.PYRO_ES_QUEUESIZE) def run(self): while 1: event=self.queue.get() if isinstance(event,Event): try: self.remote.event(event) except ProtocolError,x: break else: break # it was no Event, so exit # this reads all pending items from the queue so that any # tasks that are blocked on the queue can continue. (queue, self.queue) = (self.queue, None) try: while 1: queue.get(block=0) except Queue.Empty: pass # release the remote connection self.remote._release() del self.remote def send(self, event): if self.queue: self.queue.put(event, block=Pyro.config.PYRO_ES_BLOCKQUEUE) def running(self): return self.queue # The EVENTSERVICE is the actual Pyro server. # # BTW: Subscribers are remembered trough their proxy class. # This class is capable of being a correct key in a dictionary. class EventService(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.subscribers={} # subject -> { threadname-> subscriberthread } self.subscribersMatch={} # subjectPattern -> { threadname->subscriberthread } self.subscriptionWorkers={} # subscriber -> subscription thread object def _mksequence(self, seq): if not (type(seq) in (types.TupleType,types.ListType)): return (seq,) return seq def getSubscriptionWorker(self, subscriber): # If this subscriber doesn't have its own subscription thread, create one. if subscriber not in self.subscriptionWorkers: worker = Subscriber(subscriber) worker.start() self.subscriptionWorkers[subscriber]=worker return worker else: return self.subscriptionWorkers[subscriber] def subscribe(self, subjects, subscriber): if not subjects: return # Subscribe into a dictionary; this way; somebody can subscribe # only once to this subject. Subjects are exact strings. for subject in self._mksequence(subjects): worker=self.getSubscriptionWorker(subscriber) self.subscribers.setdefault(subject.lower(),{}) [worker.getName()]=worker def subscribeMatch(self, subjects, subscriber): if not subjects: return # Subscribe into a dictionary; this way; somebody can subscribe # only once to this subject. Subjects are regex patterns. for subject in self._mksequence(subjects): worker=self.getSubscriptionWorker(subscriber) matcher = re.compile(subject,re.IGNORECASE) self.subscribersMatch.setdefault(matcher,{}) [worker.getName()]=worker def unsubscribe(self, subjects, subscriber): if not subjects: return for subject in self._mksequence(subjects): try: blaat=self.subscribers[subject.lower()] # check for subject worker=self.subscriptionWorkers[subscriber] del self.subscribers[subject.lower()] [worker.getName()] self.killWorkerIfLastSubject(subscriber, worker) except KeyError,x: try: m=re.compile(subject,re.IGNORECASE) worker=self.subscriptionWorkers[subscriber] del self.subscribersMatch[m] [worker.getName()] self.killWorkerIfLastSubject(subscriber,worker) except KeyError,x: pass def publish(self, subjects, message): if not subjects: return # keep the creation time, this must be the same for all events. creationTime=time.time() # publish a message. Subjects must be exact strings for subject in self._mksequence(subjects): event = Event(subject, message, creationTime) subjectLC=subject.lower() try: for (name,s) in self.subscribers[subjectLC].items(): try: if s.running(): s.send(event) else: try: del self.subscribers[subjectLC][name] except KeyError: pass except Queue.Full: pass except KeyError: pass # process the subject patterns for (m,subs) in self.subscribersMatch.items(): if m.match(subject): # send event to all subscribers for (name,s) in subs.items(): try: if s.running(): s.send(event) else: try: del subs[name] except KeyError: pass except Queue.Full: pass def killWorkerIfLastSubject(self, subscriber, worker): item=(worker.getName(),worker) for v in self.subscribers.values(): if item in v.items(): return for v in self.subscribersMatch.values(): if item in v.items(): return worker.send("QUIT") del self.subscriptionWorkers[subscriber] class EventServiceStarter(object): def __init__(self, identification=None): Pyro.core.initServer() self.running=1 self.identification=identification self.started = Pyro.util.getEventObject() def start(self, *args, **kwargs): # see _start for allowed arguments kwargs["startloop"]=1 self._start(*args, **kwargs ) def initialize(self, *args, **kwargs): # see _start for allowed arguments kwargs["startloop"]=0 self._start( *args, **kwargs ) def getServerSockets(self): return self.daemon.getServerSockets() def waitUntilStarted(self,timeout=None): self.started.wait(timeout) return self.started.isSet() def _start(self,hostname='',port=None,startloop=1,useNameServer=1,norange=0): daemon = Pyro.core.Daemon(host=hostname,port=port,norange=norange) if self.identification: daemon.setAllowedIdentifications([self.identification]) print 'Requiring connection authentication.' if useNameServer: locator = Pyro.naming.NameServerLocator(identification=self.identification) ns = locator.getNS() # check if ES already running try: ns.resolve(Pyro.constants.EVENTSERVER_NAME) print 'The Event Server appears to be already running.' print 'You cannot start multiple Event Servers.' ans=raw_input('Start new Event Server anyway (y/n)? ') if ans!='y': return ns.unregister(Pyro.constants.EVENTSERVER_NAME) except NamingError: pass daemon.useNameServer(ns) es = EventService() esURI=daemon.connect(es, Pyro.constants.EVENTSERVER_NAME) print 'URI=',esURI message = daemon.validateHostnameAndIP() if message: print "\nWARNING:",message,"\n" print 'Event Server started.' self.started.set() # signal that we've started. if startloop: Log.msg('ES daemon','This is the Pyro Event Server.') try: if os.name!="java": # I use a timeout here otherwise you can't break gracefully on Windows daemon.setTimeout(20) daemon.requestLoop(lambda s=self: s.running) except KeyboardInterrupt: Log.warn('ES daemon','shutdown on user break signal') print 'Shutting down on user break signal.' self.shutdown(es) except: try: (exc_type, exc_value, exc_trb) = sys.exc_info() out = ''.join(traceback.format_exception(exc_type, exc_value, exc_trb)[-5:]) Log.error('ES daemon', 'Unexpected exception, type',exc_type, '\n--- partial traceback of this exception follows:\n', out,'\n--- end of traceback') print '*** Exception occured!!! Partial traceback:' print out print '*** Resuming operations...' finally: del exc_type, exc_value, exc_trb # delete refs to allow proper GC Log.msg('ES daemon','Shut down gracefully.') print 'Event Server gracefully stopped.' else: # no loop, store the required objects for getServerSockets() self.daemon=daemon self.es=es if os.name!="java": daemon.setTimeout(20) # XXX fixed timeout def mustContinueRunning(self): return self.running def handleRequests(self, timeout=None): # this method must be called from a custom event loop self.daemon.handleRequests(timeout=timeout) def shutdown(self,es): if es: # internal shutdown call with specified ES object daemon=es.getDaemon() else: # custom shutdown call w/o specified ES object, use stored instance daemon=self.daemon es=self.es del self.es, self.daemon try: daemon.disconnect(es) # clean up nicely except NamingError,x: Log.warn('ES daemon','disconnect error during shutdown:',x) except ConnectionClosedError,x: Log.warn('ES daemon','lost connection with Name Server, cannot unregister') self.running=0 daemon.shutdown() def start(argv): Args = Pyro.util.ArgParser() Args.parse(argv,'hNn:p:i:') if Args.hasOpt('h'): print 'Usage: pyro-es [-h] [-n hostname] [-p port] [-N] [-i identification]' print ' where -p = ES server port (0 for auto)' print ' -n = non-default hostname to bind on' print ' -N = do not use the name server' print ' -i = the required authentication ID for ES clients,' print ' also used to connect to other Pyro services' print ' -h = print this help' raise SystemExit hostname = Args.getOpt('n',None) port = Args.getOpt('p',None) useNameServer = not Args.hasOpt('N') ident = Args.getOpt('i',None) if port: port=int(port) norange=(port==0) Args.printIgnored() if Args.args: print 'Ignored arguments:',' '.join(Args.args) print '*** Pyro Event Server ***' starter=EventServiceStarter(identification=ident) starter.start(hostname,port,useNameServer=useNameServer,norange=norange) # allow easy starting of the ES by using python -m if __name__=="__main__": start(sys.argv[1:]) Pyro-3.14/Pyro/ext/0000755000076500000240000000000011567263336014374 5ustar irmenstaff00000000000000Pyro-3.14/Pyro/ext/__init__.py0000644000076500000240000000003711563043040016465 0ustar irmenstaff00000000000000# just to make this a package. Pyro-3.14/Pyro/ext/BasicNTService.py0000644000076500000240000001471711526311451017547 0ustar irmenstaff00000000000000############################################################################# # # An NT service that runs the Pyro Name Server # Author: Syver Enstad; syver-en@online.no # Bugfix for recent win32 builds: David Rushby; woodsplitter@rocketmail.com # # This is part of "Pyro" - Python Remote Objects # Which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import sys import win32serviceutil import threading import win32service import win32api import win32con class BasicNTService(win32serviceutil.ServiceFramework, object): """ Abstract base to help out with building NT services in Python with the win32all(by Mark Hammond) support for python nt services. Remember to set the two following class attributes to something sensible in your subclass _svc_name_ = 'PyroNS' _svc_display_name_ = 'Pyro Naming Service NT service' The following are optional _svc_deps_: This should be set to the list of service names That need to be started before this one. _exe_name_: This should be set to a service .EXE if you're not going to use PythonService.exe _svc_description_ : This is the descriptive string that you find in the services applet To register the service with the SCM the easiest way is to include the following at the bottom of the file where your subclass is defined. if __name__ == '__main__': TheClassYouDerivedFromBasicNTService.HandleCommandLine() """ def __init__(self, args): _redirectSystemStreamsIfNecessary() win32serviceutil.ServiceFramework.__init__(self, args) self._stopEvent = threading.Event() def SvcStop(self): """ Template method from win32serviceutil.ServiceFramework""" # first tell SCM that we have started the stopping process self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self._stopEvent.set() def _shouldStop(self): return self._stopEvent.isSet() def _doRun(self): raise NotImplementedError def _doStop(self): raise NotImplementedError def SvcDoRun(self): """ part of Template method SvcRun from win32serviceutil.ServiceFramework""" self.logStarted() self._doRun() self._stopEvent.wait() self._doStop() self.logTermination() return 0 def logTermination(self): import servicemanager servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, "")) def logStarted(self): import servicemanager servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')) def CustomOptionHandler(cls, opts): #out=open("c:\\log.txt","w") print "Installing the Pyro %s" % cls._svc_name_ args = raw_input("Enter command line arguments for %s: " % cls._svc_name_) try: createRegistryParameters(cls._svc_name_, args.strip()) except Exception,x: print "Error occured when setting command line args in the registry: ",x try: cls._svc_description_ except LookupError: return key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s" % cls._svc_name_) try: win32api.RegSetValueEx(key, "Description", 0, win32con.REG_SZ, cls._svc_description_); finally: win32api.RegCloseKey(key) CustomOptionHandler = classmethod(CustomOptionHandler) def HandleCommandLine(cls): if win32serviceutil.HandleCommandLine(cls, customOptionHandler=cls.CustomOptionHandler) != 0: return # some error occured if sys.argv[1] in ("install", "update"): print "\nYou can configure the command line arguments in the Registry." print "The key is: HKLM\\System\\CurrentControlSet\\Services\\%s" % cls._svc_name_ print "The value under that key is: ", pyroArgsRegkeyName args=getRegistryParameters(cls._svc_name_) if args: print "(it is currently set to: '%s')" % args else: print "(it is currently not set)" print HandleCommandLine = classmethod(HandleCommandLine) pyroArgsRegkeyName = "PyroServiceArguments" def getRegistryParameters(servicename): key=win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\"+servicename) try: try: (commandLine, regtype) = win32api.RegQueryValueEx(key,pyroArgsRegkeyName) return commandLine except: pass finally: key.Close() createRegistryParameters(servicename, pyroArgsRegkeyName) return "" def createRegistryParameters(servicename, parameters): newkey=win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\"+servicename,0,win32con.KEY_ALL_ACCESS) try: win32api.RegSetValueEx(newkey, pyroArgsRegkeyName, 0, win32con.REG_SZ, parameters) finally: newkey.Close() def _redirectSystemStreamsIfNecessary(): # Python programs running as Windows NT services must not send output to # the default sys.stdout or sys.stderr streams, because those streams are # not fully functional in the NT service execution environment. Sending # output to them will eventually (but not immediately) cause an IOError # ("Bad file descriptor"), which can be quite mystifying to the # uninitiated. This problem can be overcome by replacing the default # system streams with a stream that discards any data passed to it (like # redirection to /dev/null on Unix). # # However, the pywin32 service framework supports a debug mode, under which # the streams are fully functional and should not be redirected. shouldRedirect = True try: import servicemanager except ImportError: # If we can't even 'import servicemanager', we're obviously not running # as a service, so the streams shouldn't be redirected. shouldRedirect = False else: # Unlike previous builds, pywin32 builds >= 200 allow the # servicemanager module to be imported even in a program that isn't # running as a service. In such a situation, it would not be desirable # to redirect the system streams. # # However, it was not until pywin32 build 203 that a 'RunningAsService' # predicate was added to allow client code to determine whether it's # running as a service. # # This program logic redirects only when necessary if using any build # of pywin32 except 200-202. With 200-202, the redirection is a bit # more conservative than is strictly necessary. if ( servicemanager.Debugging() or ( hasattr(servicemanager, 'RunningAsService') and not servicemanager.RunningAsService() ) ): shouldRedirect = False if shouldRedirect: sys.stdout = sys.stderr = open('nul', 'w') return shouldRedirect Pyro-3.14/Pyro/ext/daemonizer.py0000644000076500000240000001272011526311451017070 0ustar irmenstaff00000000000000#!/usr/bin/env python ############################################################################# # # Run Pyro servers as daemon processes on Unix/Linux. # This won't work on other operating systems such as Windows. # Author: Jeff Bauer (jbauer@rubic.com) # This software is released under the MIT software license. # Based on an earlier daemonize module by Jeffery Kunce # Updated by Luis Camaano to double-fork-detach. # # DEPRECATED. Don't use this in new code. # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import sys, os, time from signal import SIGINT class DaemonizerException: def __init__(self, msg): self.msg = msg def __str__(self): return self.msg class Daemonizer: """ Daemonizer is a class wrapper to run a Pyro server program in the background as daemon process. The only requirement is for the derived class to implement a main_loop() method. See Test class below for an example. The following command line operations are provided to support typical /etc/init.d startup/shutdown on Unix systems: start | stop | restart In addition, a daemonized program can be called with arguments: status - check if process is still running debug - run the program in non-daemon mode for testing Note: Since Daemonizer uses fork(), it will not work on non-Unix systems. """ def __init__(self, pidfile=None): if not pidfile: self.pidfile = "/tmp/%s.pid" % self.__class__.__name__.lower() else: self.pidfile = pidfile def become_daemon(self, root_dir='/'): if os.fork() != 0: # launch child and ... os._exit(0) # kill off parent os.setsid() os.chdir(root_dir) os.umask(0) if os.fork() != 0: # fork again so we are not a session leader os._exit(0) sys.stdin.close() sys.__stdin__ = sys.stdin sys.stdout.close() sys.stdout = sys.__stdout__ = _NullDevice() sys.stderr.close() sys.stderr = sys.__stderr__ = _NullDevice() for fd in range(1024): try: os.close(fd) except OSError: pass def daemon_start(self, start_as_daemon=1): if start_as_daemon: self.become_daemon() if self.is_process_running(): msg = "Unable to start server. Process is already running." raise DaemonizerException(msg) f = open(self.pidfile, 'w') f.write("%s" % os.getpid()) f.close() self.main_loop() def daemon_stop(self): pid = self.get_pid() try: os.kill(pid, SIGINT) # SIGTERM is too harsh... time.sleep(1) try: os.unlink(self.pidfile) except OSError: pass except IOError: pass def get_pid(self): try: f = open(self.pidfile) pid = int(f.readline().strip()) f.close() except IOError: pid = None return pid def is_process_running(self): pid = self.get_pid() if pid: try: os.kill(pid, 0) return 1 except OSError: pass return 0 def main_loop(self): """NOTE: This method must be implemented in the derived class.""" msg = "main_loop method not implemented in derived class: %s" % \ self.__class__.__name__ raise DaemonizerException(msg) def process_command_line(self, argv, verbose=1): usage = "usage: %s start | stop | restart | status | debug " \ "(run as non-daemon)" % os.path.basename(argv[0]) if len(argv) < 2: print usage raise SystemExit else: operation = argv[1] pid = self.get_pid() if operation == 'status': if self.is_process_running(): print "Server process %s is running." % pid else: print "Server is not running." elif operation == 'start': if self.is_process_running(): print "Server process %s is already running." % pid raise SystemExit else: if verbose: print "Starting server process." self.daemon_start() elif operation == 'stop': if self.is_process_running(): self.daemon_stop() if verbose: print "Server process %s stopped." % pid else: print "Server process %s is not running." % pid raise SystemExit elif operation == 'restart': self.daemon_stop() if verbose: print "Restarting server process." self.daemon_start() elif operation == 'debug': self.daemon_start(0) else: print "Unknown operation:", operation raise SystemExit class _NullDevice: """A substitute for stdout/stderr that writes to nowhere.""" def write(self, s): pass class Test(Daemonizer): def __init__(self): Daemonizer.__init__(self) def main_loop(self): while 1: time.sleep(1) if __name__ == "__main__": test = Test() test.process_command_line(sys.argv) Pyro-3.14/Pyro/ext/ES_NtService.py0000644000076500000240000000607111526311451017226 0ustar irmenstaff00000000000000############################################################################# # # An NT service that runs the Pyro Event Service # Author: Syver Enstad syver-en@online.no # # This is part of "Pyro" - Python Remote Objects # Which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import win32serviceutil import threading import win32service import win32api from BasicNTService import BasicNTService, getRegistryParameters def setConfig(): Pyro.config.PYRO_TRACELEVEL=3 Pyro.config.PYRO_STORAGE = os.path.splitdrive(win32api.GetSystemDirectory())[0]+os.sep Pyro.config.PYRO_LOGFILE = "Pyro_ES_svc.log" import os,sys import Pyro.util setConfig() Log=Pyro.util.Log from Pyro.EventService import Server class PyroESThread(threading.Thread): """ The Pyro Event Service will run in this thread """ def __init__(self, args, stopcallback): threading.Thread.__init__(self) self._args = list(args) self._stopcallback = stopcallback def run(self): self.startPyroES() self._stopcallback() def startPyroES(self): Log.msg("PyroES_svc","Pyro ES service is starting, arguments=",self._args) """ ripped out of Pyro.EventService.Server and slightly changed to accomodate not using sys.argv, but self._args instead """ try: Args = Pyro.util.ArgParser() Args.parse(self._args,'hn:p:i:') if Args.hasOpt('h'): Log.error("PyroES_svc",""" Usage: es [-h] [-n hostname] [-p port] [-i identification] where -p = ES server port -n = non-default hostname to bind on -i = the required authentication ID for ES clients, also used to connect to other Pyro services -h = print this help """) raise SystemExit host = Args.getOpt('n','') port = Args.getOpt('p',None) ident = Args.getOpt('i',None) if port: port=int(port) if Args.ignored: Log.warn("PyroES_svc",'Ignored options:',Args.ignored) if Args.args: Log.warn("PyroES_svc",'Ignored arguments:',Args.args) Log.msg("PyroES_scv", "Starting the Event Server.") self.starter=Server.EventServiceStarter(identification=ident) self.starter.start(host,port) except Exception,x : Log.error("PyroES_scv","COULD NOT START!!!",x) raise SystemExit def shutdown(self): self.starter.running=0 class PyroES_NTService(BasicNTService): _svc_name_ = 'PyroES' _svc_display_name_ = "Pyro Event Service" _svc_description_ = "Provides event topics and publish/subscribe communication for Pyro" def __init__(self, args): super(PyroES_NTService, self).__init__(args) setConfig() try: args = getRegistryParameters(self._svc_name_).split() except Exception,x: Log.error("PyroES_svc","PROBLEM GETTING ARGS FROM REGISTRY:",x) self._esThread = PyroESThread(args, self.SvcStop) def _doRun(self): """ Overriden """ self._esThread.start() def _doStop(self): """ Overridden """ self._esThread.shutdown() self._esThread.join() def SvcStop(self): """Overriden """ super(PyroES_NTService, self).SvcStop() if __name__ == '__main__': PyroES_NTService.HandleCommandLine() Pyro-3.14/Pyro/ext/NS_NtService.py0000644000076500000240000001425711526311451017244 0ustar irmenstaff00000000000000############################################################################# # # An NT service that runs the Pyro Name Server # Author: Syver Enstad syver-en@online.no # # This is part of "Pyro" - Python Remote Objects # Which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import win32serviceutil import threading import win32service import win32api from BasicNTService import BasicNTService, getRegistryParameters def setConfig(): Pyro.config.PYRO_TRACELEVEL=3 Pyro.config.PYRO_STORAGE = os.path.splitdrive(win32api.GetSystemDirectory())[0]+os.sep Pyro.config.PYRO_LOGFILE = "Pyro_NS_svc.log" Pyro.config.PYRO_NS_URIFILE = os.path.join(Pyro.config.PYRO_STORAGE, "Pyro_NS_URI.txt") import os,sys import Pyro.util setConfig() Log=Pyro.util.Log import Pyro.core import Pyro.constants import Pyro.naming from Pyro.naming import NameServer, PersistentNameServer, BroadcastServer, bcRequestHandler BcServerObject = None def startServer(hstn='', nsport=0, bcport=0, keep=0, persistent=0, dbdir=None, Guards=(None,None)): global BcServerObject if not nsport: nsport=Pyro.config.PYRO_NS_PORT if not bcport: bcport=Pyro.config.PYRO_NS_BC_PORT Pyro.core.initServer() PyroDaemon = Pyro.core.Daemon(host=hstn, port=nsport,norange=1) if Guards[0]: PyroDaemon.setNewConnectionValidator(Guards[0]) if persistent: ns=PersistentNameServer(dbdir) PyroDaemon.useNameServer(ns) NS_URI=PyroDaemon.connectPersistent(ns,Pyro.constants.NAMESERVER_NAME) else: ns=NameServer() PyroDaemon.useNameServer(ns) NS_URI=PyroDaemon.connect(ns,Pyro.constants.NAMESERVER_NAME) BcServerObject = BroadcastServer((hstn or '',bcport),bcRequestHandler) if Guards[1]: BcServerObject.setRequestValidator(Guards[1]) BcServerObject.keepRunning(keep) if keep: Log.msg("NS", 'Will ignore shutdown requests.') ns.ignoreShutdown=True else: Log.msg("NS", 'Will accept shutdown requests.') ns.ignoreShutdown=False if Guards[0] or Guards[1]: print 'Using security plugins:' if Guards[0]: print ' NS new conn validator =',Guards[0].__class__.__name__,'from', Guards[0].__class__.__module__ else: print ' default NS new conn validator' if Guards[1]: print ' BC request validator =',Guards[1].__class__.__name__,'from', Guards[1].__class__.__module__ else: print ' default BC request validator' ns.publishURI(NS_URI) BcServerObject.setNS_URI(NS_URI) Log.msg('NS daemon','This is the Pyro Name Server.') if persistent: Log.msg('NS daemon','Persistent mode, database is in',ns.getDBDir()) print 'Persistent mode, database is in',ns.getDBDir() Log.msg('NS daemon','Starting on',PyroDaemon.hostname,'port', PyroDaemon.port, ' broadcast server on port',bcport) # I use a timeout here otherwise you can't break gracefully on Windoze while not BcServerObject.shutdown: try: PyroDaemon.handleRequests(BcServerObject.preferredTimeOut,[BcServerObject],BcServerObject.bcCallback) except KeyboardInterrupt: Log.warn('NS daemon','shutdown on user break signal') BcServerObject.shutdown=1 except: import traceback (exc_type, exc_value, exc_trb) = sys.exc_info() out = ''.join(traceback.format_exception(exc_type, exc_value, exc_trb)[-5:]) Log.error('NS daemon', 'Unexpected exception, type',exc_type, '\n--- partial traceback of this exception follows:\n', out,'\n--- end of traceback') Log.msg('NS daemon','Shut down gracefully.') class PyroNSThread(threading.Thread): """ The Pyro Naming Service will run in this thread """ def __init__(self, args, stopcallback): threading.Thread.__init__(self) Log.msg("PyroNSsvc", "initializing") self._args = list(args) Log.msg("PyroNSsvc", "args are:",self._args) self._stopcallback = stopcallback def run(self): self.startPyroNS() self._stopcallback() def startPyroNS(self): try: """ ripped out of Pyro.naming and slightly changed to accomodate not using sys.argv, but self._args instead """ Args = Pyro.util.ArgParser() Args.parse(self._args,'hkn:p:b:d:s:') try: Args.getOpt('h') Log.error("PyroNS_svc",""" Usage: ns [-h] [-n hostname] [-p port] [-b port] [-d [databasefile]] [-s securitymodule] where -p = NS server port -b = NS broadcast port -n = non-default server hostname -d = use persistent database, provide optional storage directory -s = use given python module with security code -h = print this help """) raise SystemExit except KeyError: pass host = Args.getOpt('n','') port = int(Args.getOpt('p',Pyro.config.PYRO_NS_PORT)) bcport = int(Args.getOpt('b',Pyro.config.PYRO_NS_BC_PORT)) try: dbdir = Args.getOpt('d') persistent = 1 except KeyError: persistent = 0 dbdir = None # we're running as a service, always ignore remote shutdown requests keep=1 try: secmod = __import__(Args.getOpt('s'),locals(),globals()) Guards = (secmod.NSGuard(), secmod.BCGuard()) except ImportError,x: Log.msg("NS", 'Error loading security module:',x) raise SystemExit except KeyError: secmod = None Guards = (None,None) if Args.ignored: Log.warn("PyroNS_svc",'Ignored options:',Args.ignored) if Args.args: Log.warn("PyroNS_svc",'Ignored arguments:',Args.args) Log.msg("PyroNS_svc","Starting the Name Server.") startServer(host,port,bcport,keep,persistent,dbdir,Guards) except Exception,x : Log.error("NS daemon","COULD NOT START!!!",x) raise SystemExit class PyroNS_NTService(BasicNTService): _svc_name_ = 'PyroNS' _svc_display_name_ = "Pyro Naming Service" _svc_description_ = 'Provides name resolution services for Pyro objects' def __init__(self, args): super(PyroNS_NTService, self).__init__(args) setConfig() try: args = getRegistryParameters(self._svc_name_).split() except Exception,x: Log.error("PyroNS_svc","PROBLEM GETTING ARGS FROM REGISTRY:",x) self._nsThread = PyroNSThread(args, self.SvcStop) def _doRun(self): """ Overriden """ self._nsThread.start() def _doStop(self): """ Overridden """ global BcServerObject BcServerObject.shutdown = 1 self._nsThread.join() def SvcStop(self): """Overriden """ super(PyroNS_NTService, self).SvcStop() if __name__ == '__main__': PyroNS_NTService.HandleCommandLine() Pyro-3.14/Pyro/ext/remote.py0000644000076500000240000003026711526311451016234 0ustar irmenstaff00000000000000############################################################################# # # simple Pyro connection module, originally written by John Wiegley # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import UserDict import exceptions import os import re import signal import socket import sys import time import types import Pyro.errors import Pyro.naming import Pyro.core import Pyro.util from Pyro.protocol import ProtocolError true, false = 1, 0 copy_types = false verbose = false pyro_nameserver = None pyro_daemon = None client_initialized = false server_initialized = false daemon_host = '' daemon_port = 0 daemon_objects = [] daemon_types = [] def tb_info(tb): codename = tb.tb_frame.f_code.co_filename lineno = tb.tb_lineno if not (codename == '' or codename.find(".py") > 0): lineno = lineno - 2 return lineno, codename def canonize(e_type, e_val, e_traceback): "Turn the exception into a textual representation." # find the last traceback: tb = e_traceback lineno, codename = tb_info(tb) lines = [ "%s %s" % (codename, lineno) ] found = None if tb.tb_frame.f_code.co_filename[0] == '<': found = tb while tb.tb_next: tb = tb.tb_next if tb.tb_frame.f_code.co_filename[0] == '<': found = tb lineno, codename = tb_info(tb) lines.append("%s %s" % (codename, lineno)) if found: tb = found lineno, codename = tb_info(tb) if codename == '': lines.insert(0, "%s in command: %s" % (e_type, e_val)) elif codename.find(".py") > 0 and e_type == "SyntaxError": lines.insert(0, "%s in: %s" % (e_type, e_val)) else: lines.insert(0, "%s in line %s of %s: %s" % (e_type, lineno, codename, e_val)) return lines def exception_text(): return sys.exc_value def format_exception(): return canonize(*sys.exc_info()) def register_type(t): """Whenever type T goes in or out, wrap/unwrap the type so that the user is always interacting with the server object, or the server interacts with the object directly.""" if t not in daemon_types: daemon_types.append(t) def unregister_objects(): if pyro_daemon: global daemon_objects for obj in daemon_objects: try: pyro_daemon.disconnect(obj) except: pass daemon_objects = [] sys.exitfunc = unregister_objects def host_ipaddr(interface = None): if sys.platform == "win32": return socket.gethostbyname(socket.gethostname()) cmd = "/sbin/ifconfig" if interface: cmd = '%s %s' % (cmd, interface) fd = os.popen(cmd) this_host = None interfaces = {} name = None for line in fd.readlines(): match = re.match("(\S+)", line) if match: name = match.group(1) match = re.search("inet addr:(\S+)", line) if match: addr = match.group(1) if name: interfaces[name] = addr if interfaces.has_key(interface): this_host = interfaces[interface] else: for name, addr in interfaces.items(): if re.match("ppp", name): this_host = addr break elif re.match("eth", name): this_host = addr fd.close() return this_host or socket.gethostbyname(socket.gethostname()) def find_nameserver(hostname = None, portnum = None): if hostname and hostname.find('://') > 0: URI = Pyro.core.PyroURI(hostname) ns = Pyro.naming.NameServerProxy(URI) else: try: if verbose: print 'Searching for Naming Service on %s:%d...' % \ (hostname or 'BROADCAST', portnum or Pyro.config.PYRO_NS_BC_PORT) locator = Pyro.naming.NameServerLocator() ns = locator.getNS(host = hostname, port = portnum) except (Pyro.core.PyroError, socket.error), x: localhost = socket.gethostbyname('localhost') if verbose: print "Error:", x print """ Naming Service not found with broadcast. Trying local host""", localhost, '...', ns = locator.getNS(host = localhost, port = portnum) if verbose: print 'Naming Service found at', ns.URI return ns class Error(Exception): pass class ObjBase(Pyro.core.ObjBase): """This extension of Pyro.core.ObjBase makes sure that any values that get returned to the caller which are of a significant type, get wrapped first in proxies. Likewise, if a proxy class comes back to us, and it's in regard to an object native to this server, unwrap it.""" def __nonzero__(self): return 1 def Pyro_dyncall(self, method, flags, args): try: base = Pyro.core.ObjBase.Pyro_dyncall result = wrap(base(self, method, flags, unwrap(args))) except: result = Error('\n'.join(format_exception())) return result def _r_ga(self, attr): return wrap(Pyro.core.ObjBase._r_ga(self, attr)) def _r_sa(self, attr, value): Pyro.core.ObjBase._r_sa(self, attr, unwrap(value)) class Nameserver: """This helper class allows the server to use Pyro's naming service for publishing certain objects by name. It integrates better with remote.py, than Pyro.naming.NameServer does.""" def __init__(self, ns, ns_port): self.ns = ns self.ns_port = ns_port def __cmp__(self, other): return self.ns == other.ns and self.ns_port == other.ns_port def __str__(self): if self.ns_port: return "%s:%s" % (self.ns, self.ns_port) return self.ns def resolve(self, name): return get_remote_object(name, self.ns, self.ns_port) def register(self, name, object): return provide_local_object(object, name, self.ns, self.ns_port) def unregister(self, object): for obj in daemon_objects[:]: if obj.delegate is object: pyro_daemon.disconnect(obj) daemon_objects.remove(obj) class DynamicProxy(Pyro.core.DynamicProxyWithAttrs): """This version of the proxy just wraps args before making external calls.""" def __nonzero__(self): return true def _invokePYRO(self, *vargs, **kargs): result = unwrap(apply(Pyro.core.DynamicProxyWithAttrs._invokePYRO, tuple([self] + wrap(list(vargs))), wrap(kargs))) if type(result) is types.InstanceType and \ isinstance(result, Error) or \ isinstance(result, Pyro.errors.PyroError) or \ isinstance(result, ProtocolError): msg = str(result) type_name = msg[: msg.find(' ')] if type_name == 'exceptions.IndexError': try: real_type = eval(type_name) msg = msg.split('\n')[0] result = real_type(msg[msg.find(':') + 2 :]) except: pass raise result else: return result def unwrap(value): t = type(value) if t is types.InstanceType and isinstance(value, DynamicProxy): if pyro_daemon: try: return pyro_daemon.getLocalObject(value.objectID) except KeyError: pass return value elif t is types.ListType: for i in range(len(value)): value[i] = unwrap(value[i]) elif t is types.TupleType: value = list(value) for i in range(len(value)): value[i] = unwrap(value[i]) return tuple(value) elif t is types.DictType: for k, v in value.items(): value[k] = unwrap(v) return value def wrap(value): """Wrap the argument, returning a copy -- since otherwise we might alter a local data structure inadvertantly.""" t = type(value) if t is types.InstanceType: matched = false for dt in daemon_types: if isinstance(value, dt): matched = true if not copy_types and not matched and \ not isinstance(value, DynamicProxy): return provide_local_object(value) elif t is types.ListType: value = value[:] for i in range(len(value)): value[i] = wrap(value[i]) elif t is types.TupleType: value = list(value) for i in range(len(value)): value[i] = wrap(value[i]) return tuple(value) elif t is types.DictType: copy = {} for k, v in value.items(): copy[k] = wrap(v) return copy return value def get_remote_object(name, hostname = None, portnum = None): global client_initialized, pyro_nameserver # initialize Pyro -- Python Remote Objects if not client_initialized: Pyro.core.initClient(verbose) client_initialized = true if pyro_nameserver is None or hostname: pyro_nameserver = find_nameserver(hostname, portnum) if verbose: print 'Binding object %s' % name try: URI = pyro_nameserver.resolve(name) if verbose: print 'URI:', URI return DynamicProxy(URI) except Pyro.core.PyroError, x: raise Error("Couldn't bind object, nameserver says:", x) class Cache(UserDict.UserDict): """simple cache that uses least recently accessed time to trim size""" def __init__(self,data=None,size=100): UserDict.UserDict.__init__(self,data) self.size = size def resize(self): """trim cache to no more than 95% of desired size""" trim = max(0, int(len(self.data)-0.95*self.size)) if trim: # don't want self.items() because we must sort list by access time values = map(None, self.data.values(), self.data.keys()) values.sort() for val,k in values[0:trim]: del self.data[k] def __setitem__(self,key,val): if (not self.data.has_key(key) and len(self.data) >= self.size): self.resize() self.data[key] = (time.time(), val) def __getitem__(self,key): """like normal __getitem__ but updates time of fetched entry""" val = self.data[key][1] self.data[key] = (time.time(),val) return val def get(self,key,default=None): """like normal __getitem__ but updates time of fetched entry""" try: return self[key] except KeyError: return default def values(self): """return values, but eliminate access times first""" vals = list(self.data.values()) for i in range(len(vals)): vals[i] = vals[i][1] return tuple(vals) def items(self): return map(None, self.keys(), self.values()) def copy(self): return self.__class__(self.data, self.size) def update(self, otherdict): for k in otherdict.keys(): self[k] = otherdict[k] provided_objects = Cache(size = 100) def provide_local_object(obj, name = None, hostname = None, portnum = None): global server_initialized, pyro_daemon, pyro_nameserver proxy_class = DynamicProxy if not server_initialized: Pyro.core.initServer(verbose) server_initialized = true if pyro_daemon is None: pyro_daemon = Pyro.core.Daemon(host = daemon_host, port = daemon_port) # If no 'name' was specified, don't even bother with the # nameserver. if name: if pyro_nameserver is None or hostname: pyro_nameserver = find_nameserver(hostname, portnum) pyro_daemon.useNameServer(pyro_nameserver) if verbose: print 'Remoting object', name # tell nameserver to forget any earlier use of this name try: if pyro_nameserver.resolve(name): pyro_nameserver.unregister(name) except Pyro.errors.NamingError: pass if not isinstance(obj, Pyro.core.ObjBase): if provided_objects.has_key(obj): obj = provided_objects[obj] else: slave = ObjBase() slave.delegateTo(obj) provided_objects[obj] = slave obj = slave URI = pyro_daemon.connect(obj, name) daemon_objects.append(obj) proxy = proxy_class(URI) return proxy abort = false def interrupt(*args): global abort abort = true if hasattr(signal,'SIGINT'): signal.signal(signal.SIGINT, interrupt) #if hasattr(signal,'SIGHUP'): signal.signal(signal.SIGHUP, interrupt) #if hasattr(signal,'SIGQUIT'): signal.signal(signal.SIGQUIT, interrupt) def handle_requests(wait_time = None, callback = None): global abort abort = false if pyro_daemon is None: raise Error("There is no daemon with which to handle requests") if wait_time: start = time.time() while not abort: try: pyro_daemon.handleRequests(wait_time) if wait_time: now = time.time() if callback and now - start > wait_time: callback() start = now elif callback: callback() # ignore socket and select errors, they are often transient except socket.error: pass except Exception, msg: if verbose: print "Error:", sys.exc_type, msg abort = true except: abort = true return abort def handle_requests_unsafe(wait_time = None, callback = None): global abort abort = false if pyro_daemon is None: raise Error("There is no daemon with which to handle requests") if wait_time: start = time.time() while 1: pyro_daemon.handleRequests(wait_time) if wait_time: now = time.time() if callback and now - start > wait_time: callback() start = now elif callback: callback() return true def unregister_object(obj): if pyro_daemon: try: pyro_daemon.disconnect(obj) except: pass global daemon_objects if obj in daemon_objects: daemon_objects.remove(obj) Pyro-3.14/Pyro/ext/remote_nons.py0000644000076500000240000000612411526311451017264 0ustar irmenstaff00000000000000############################################################################# # # simple Pyro connection module, without requiring Pyro's NameServer # (adapted from John Wiegley's remote.py) # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import signal import sys import time import Pyro.errors import Pyro.naming import Pyro.core import Pyro.util true, false = 1, 0 verbose = false pyro_daemon = None client_initialized = false server_initialized = false daemon_objects = [] from Pyro.protocol import ProtocolError def get_server_object(objectName, hostname , portnum): global client_initialized # initialize Pyro -- Python Remote Objects if not client_initialized: Pyro.core.initClient(verbose) client_initialized = true if verbose: print 'Binding object %s' % objectName try: URI = 'PYROLOC://%s:%d/%s' % (hostname,portnum,objectName) if verbose: print 'URI:', URI return Pyro.core.getAttrProxyForURI(URI) except Pyro.core.PyroError, x: raise Pyro.core.PyroError("Couldn't bind object, Pyro says:", x) def provide_server_object(obj, name = None, hostname = '', portnum = None): global server_initialized, pyro_daemon proxy_class = Pyro.core.DynamicProxyWithAttrs if not server_initialized: Pyro.core.initServer(verbose) server_initialized = true if pyro_daemon is None: pyro_daemon = Pyro.core.Daemon(host = hostname, port = portnum) if not isinstance(obj, Pyro.core.ObjBase): slave = Pyro.core.ObjBase() slave.delegateTo(obj) obj = slave URI = pyro_daemon.connect(obj, name) if verbose: print 'provide_server_object: URI = ', URI daemon_objects.append(obj) proxy = proxy_class(URI) return proxy abort = false def interrupt(*args): global abort abort = true if hasattr(signal,'SIGINT'): signal.signal(signal.SIGINT, interrupt) #if hasattr(signal,'SIGHUP'): signal.signal(signal.SIGHUP, interrupt) #if hasattr(signal,'SIGQUIT'): signal.signal(signal.SIGQUIT, interrupt) def handle_requests(wait_time = None, callback = None): global abort abort = false if pyro_daemon is None: raise Pyro.errors.PyroError("There is no daemon with which to handle requests") return if wait_time: start = time.time() while not abort: try: pyro_daemon.handleRequests(wait_time) if wait_time: now = time.time() if callback and now - start > wait_time: callback() start = now elif callback: callback() except Exception, msg: if verbose: print "Error:", sys.exc_type, msg abort = true except: abort = true return abort Pyro-3.14/Pyro/ext/ServiceTest.py0000644000076500000240000000522111526311451017171 0ustar irmenstaff00000000000000############################################################################# # # A test for the PyroNS_NTService program # Author: Syver Enstad syver-en@online.no # # This is part of "Pyro" - Python Remote Objects # Which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import unittest import win32serviceutil import win32service import time import Pyro.nsc ServiceName = 'PyroNS' class Test(unittest.TestCase): def setUp(self): win32serviceutil.StartService(ServiceName) def testStartPending(self): svcType, svcState, svcControls, err, svcErr, svcCP, svcWH = \ win32serviceutil.QueryServiceStatus(ServiceName) assert svcState & win32service.SERVICE_START_PENDING def testFullyStarted(self): self._waitForStarted() svcType, svcState, svcControls, err, svcErr, svcCP, svcWH = \ win32serviceutil.QueryServiceStatus(ServiceName) assert svcType & win32service.SERVICE_WIN32_OWN_PROCESS assert svcState & win32service.SERVICE_RUNNING assert svcControls & win32service.SERVICE_ACCEPT_STOP def testStop(self): self._waitForStarted() svcType, svcState, svcControls, err, svcErr, svcCP, svcWH = \ win32serviceutil.StopService(ServiceName) assert svcState & win32service.SERVICE_STOPPED assert svcType & win32service.SERVICE_WIN32_OWN_PROCESS def testNameserverAvailable(self): self._waitForStarted() ctrl = Pyro.nsc.PyroNSControl() ctrl.args(None) ctrl.ping() def testNameserverShutdownFromNsc(self): self._waitForStarted() ctrl = Pyro.nsc.PyroNSControl() ctrl.args(None) ctrl.shutdown() for each in range(100): svcType, svcState, svcControls, err, svcErr, svcCP, svcWH = \ win32serviceutil.QueryServiceStatus(ServiceName) if svcState & win32service.SERVICE_STOPPED: return time.sleep(0.20) self.fail() def tearDown(self): for each in range(1000): svcType, svcState, svcControls, err, svcErr, svcCP, svcWH = \ win32serviceutil.QueryServiceStatus(ServiceName) if svcState & win32service.SERVICE_RUNNING: svcType, svcState, svcControls, err, svcErr, svcCP, svcWH = \ win32serviceutil.StopService(ServiceName) time.sleep(0.1) elif svcState & win32service.SERVICE_STOPPED: time.sleep(0.10) break else: time.sleep(0.10) assert svcState & win32service.SERVICE_STOPPED time.sleep(3) def _waitForStarted(self): for each in range(100): svcType, svcState, svcControls, err, svcErr, svcCP, svcWH = \ win32serviceutil.QueryServiceStatus(ServiceName) if svcState & win32service.SERVICE_RUNNING: break else: time.sleep(0.10) if __name__ == '__main__': unittest.main() Pyro-3.14/Pyro/naming.py0000644000076500000240000014534611537206303015420 0ustar irmenstaff00000000000000############################################################################# # # Pyro Name Server # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# from __future__ import with_statement import sys, os, socket, time, traceback, errno import dircache, shutil, SocketServer import Pyro.constants, Pyro.core, Pyro.errors, Pyro.protocol, Pyro.util if Pyro.util.supports_multithreading(): import threading NS_SYSCMD_LOCATION='location' NS_SYSCMD_SHUTDOWN='shutdown' Log = Pyro.util.Log ############################################################################# # # The Pyro NameServer Locator. # Use a broadcast mechanism to find the broadcast server of the NS which # can provide us with the URI of the NS. # Can also perform direct lookup (no broadcast) if the host is specified. # (in that case, the 'port' argument is the Pyro port, not a broadcast port). # ############################################################################# class NameServerLocator(object): def __init__(self, identification=None): Pyro.core._checkInit() # init required self.identification=identification def sendSysCommand(self,request,host=None,port=None,trace=0,logerrors=1,bcaddr=None): try: # Try the 'first' name server. # Note that if no host is specified, a broadcast is used, # and that one is sent to both name servers in parallel. return self.__sendSysCommand(request, host, port, trace, logerrors, Pyro.constants.NSROLE_PRIMARY, bcaddr) except KeyboardInterrupt: raise except (socket.error, Pyro.errors.PyroError): if not port: # the 'first' name server failed, try the second try: result=self.__sendSysCommand(request, host, port, trace, logerrors, Pyro.constants.NSROLE_SECONDARY, bcaddr) # found the second! # switch config for first and second so that the second one (which we found) will now be tried first Pyro.config.PYRO_NS2_HOSTNAME, Pyro.config.PYRO_NS_HOSTNAME = Pyro.config.PYRO_NS_HOSTNAME, Pyro.config.PYRO_NS2_HOSTNAME Pyro.config.PYRO_NS2_PORT, Pyro.config.PYRO_NS_PORT = Pyro.config.PYRO_NS_PORT, Pyro.config.PYRO_NS2_PORT Pyro.config.PYRO_NS2_BC_PORT, Pyro.config.PYRO_NS_BC_PORT = Pyro.config.PYRO_NS_BC_PORT, Pyro.config.PYRO_NS2_BC_PORT Pyro.config.PYRO_NS2_BC_ADDR, Pyro.config.PYRO_NS_BC_ADDR = Pyro.config.PYRO_NS_BC_ADDR, Pyro.config.PYRO_NS2_BC_ADDR return result except (socket.error, Pyro.errors.PyroError): # Could not find using broadcast. Try the current host and localhost as well. # But only if there's no explicit host parameter given. if host: raise Pyro.errors.NamingError("could not find NameServer on host "+host) else: for host in (Pyro.protocol.getHostname(), "localhost"): if trace: print "Trying host",host Log.msg('NameServerLocator','Trying host',host) try: result=self.__sendSysCommand(request, host, port, trace, logerrors, Pyro.constants.NSROLE_PRIMARY) Pyro.config.PYRO_NS_HOSTNAME = host return result except Pyro.errors.ConnectionDeniedError: raise except (socket.error, Pyro.errors.PyroError),x: pass else: raise Pyro.errors.NamingError("could not find NameServer") else: raise def __sendSysCommand(self,request,host=None,port=None,trace=0,logerrors=1,role=Pyro.constants.NSROLE_PRIMARY,bcaddr=None): HPB={Pyro.constants.NSROLE_PRIMARY: (Pyro.config.PYRO_NS_HOSTNAME, Pyro.config.PYRO_NS_PORT, Pyro.config.PYRO_NS_BC_PORT, Pyro.config.PYRO_NS_BC_ADDR), Pyro.constants.NSROLE_SECONDARY: (Pyro.config.PYRO_NS2_HOSTNAME, Pyro.config.PYRO_NS2_PORT, Pyro.config.PYRO_NS2_BC_PORT, Pyro.config.PYRO_NS2_BC_ADDR) } if not host: host=HPB[role][0] if port: port1=port2=port else: if not host: # select the default broadcast ports port1 = HPB[Pyro.constants.NSROLE_PRIMARY][2] port2 = HPB[Pyro.constants.NSROLE_SECONDARY][2] else: # select the default port (normal) port = HPB[role][1] # We must discover the location of the name server. # Pyro's NS can answer to broadcast requests. try: if host: # use direct lookup with PYROLOC: mechanism, no broadcast if trace: print 'Locator: contacting Pyro Name Server...' uri=Pyro.core.PyroURI(host,Pyro.constants.NAMESERVER_NAME,port,'PYROLOC') prox=Pyro.core.getProxyForURI(uri) prox._setIdentification(self.identification) if request==NS_SYSCMD_LOCATION: prox.ping() # force resolving of PYROLOC: uri return prox.URI # return resolved uri elif request==NS_SYSCMD_SHUTDOWN: return prox._shutdown() else: raise ValueError("invalid command specified") # No host specified. Use broadcast mechanism if os.name=='java' and sys.version_info<(2,5): # jythons older than 2.5 don't have working broadcast msg="Skipping UDP broadcast (older jythons don't support this operation)" if trace: print msg raise Pyro.errors.PyroError(msg) if bcaddr: try: socket.gethostbyname(bcaddr) except socket.error: msg="invalid broadcast address '%s'" % bcaddr if trace: print msg raise ValueError(msg) destination1 = (bcaddr, port1) destination2 = (bcaddr, port2) else: destination1 = (Pyro.config.PYRO_NS_BC_ADDR or '', port1) destination2 = (Pyro.config.PYRO_NS2_BC_ADDR or '', port2) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) if hasattr(socket,'SO_BROADCAST'): s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) if trace: print 'Locator: searching Pyro Name Server...' try: bc_retries=Pyro.config.PYRO_BC_RETRIES if bc_retries<0: bc_retries=sys.maxint-1 bc_retries = min(sys.maxint-1, bc_retries) for i in xrange(bc_retries+1): # send request to both Pyro NS (if running in paired mode) s.sendto(request, destination1) if destination2!=destination1: s.sendto(request, destination2) timeout=min(sys.maxint,Pyro.config.PYRO_BC_TIMEOUT) if timeout<0: timeout=None ins,outs,exs = Pyro.protocol.safe_select([s],[],[s],timeout) if s in ins: # return the info of the first NS that responded. reply, fromaddr = s.recvfrom(1000) return reply if trace and i') self.lock=Pyro.util.getLockObject() self.role=role self.otherNS=None self.ignoreShutdown=False if role in (Pyro.constants.NSROLE_PRIMARY, Pyro.constants.NSROLE_SECONDARY): # for paired mode with identification, we need to remember the ident string adapter=Pyro.protocol.getProtocolAdapter("PYRO") adapter.setIdentification(identification) self.identification=adapter.getIdentification() # grab the munged ident # create default groups self.createGroup(':'+'Pyro') self.createGroup(Pyro.config.PYRO_NS_DEFAULTGROUP) Log.msg("NameServer","Running in", {Pyro.constants.NSROLE_SINGLE:"single", Pyro.constants.NSROLE_PRIMARY:"primary", Pyro.constants.NSROLE_SECONDARY:"secondary"}[self.role],"mode" ) def _initialResyncWithTwin(self, twinProxy): if twinProxy: try: Log.msg("NameServer","Initial resync with other NS at",twinProxy.URI.address,"port",twinProxy.URI.port) print "Initial Resync with other NS at",twinProxy.URI.address,"port",twinProxy.URI.port # keep old NS (self) registration oldNSreg=self.resolve(Pyro.constants.NAMESERVER_NAME) proxyForMe=NameServerProxy(self.getProxy().URI,noconnect=1) proxyForMe.adapter.setIdentification(self.identification,munge=False) # set pre-munged ident self.root=twinProxy._resync(proxyForMe) # reset self registration try: self.unregister(Pyro.constants.NAMESERVER_NAME) except: pass self.register(Pyro.constants.NAMESERVER_NAME,oldNSreg) self.otherNS=twinProxy Log.msg("NameServer","database sync complete.") print "Database synchronized." except Pyro.errors.NamingError,x: print x raise def _removeTwinNS(self): self.otherNS=None def register(self,name,URI): (origname,name)=name,self.validateName(name) URI=self.validateURI(URI) with self.lock: (group, name)=self.locateGrpAndName(name) if len(name or "")<1: raise Pyro.errors.NamingError('invalid name',origname) if isinstance(group,NameValue): raise Pyro.errors.NamingError('parent is no group', group.name) try: group.newleaf(name,URI) Log.msg('NameServer','registered',name,'with URI',str(URI)) self._dosynccall("register",origname,URI) except KeyError: Log.msg('NameServer','name already exists:',name) raise Pyro.errors.NamingError('name already exists',name) def unregister(self,name): (origname,name)=name,self.validateName(name) with self.lock: (group, name)=self.locateGrpAndName(name) if len(name or "")<1: raise Pyro.errors.NamingError('invalid name',origname) try: group.cutleaf(name) Log.msg('NameServer','unregistered',name) self._dosynccall("unregister",origname) except KeyError: raise Pyro.errors.NamingError('name not found',name) except ValueError: Log.msg('NameServer','attempt to remove a group:',name) raise Pyro.errors.NamingError('is a group, not an object',name) def resolve(self,name): # not thread-locked: higher performance and not necessary. name=self.validateName(name) try: branch=self.getBranch(name) if isinstance(branch,NameValue): return branch.value else: Log.msg('NameServer','attempt to resolve groupname:',name) raise Pyro.errors.NamingError('attempt to resolve groupname',name) except KeyError: raise Pyro.errors.NamingError('name not found',name) except AttributeError: raise Pyro.errors.NamingError('group not found',name) def flatlist(self): # return a dump with self.lock: r=self.root.flatten() for i in xrange(len(r)): r[i]=(':'+r[i][0], r[i][1]) return r def ping(self): # Just accept a remote invocation. # This method is used to check if NS is still running, # and also by the locator if a direct lookup is needed. pass # --- sync support (twin NS) def _resync(self, twinProxy): if self.role!=Pyro.constants.NSROLE_SINGLE: Log.msg("NameServer","resync requested from NS at",twinProxy.URI.address,"port",twinProxy.URI.port) print "Resync requested from NS at",twinProxy.URI.address,"port",twinProxy.URI.port self.otherNS=twinProxy with self.lock: return self._getSyncDump() else: Log.warn("NameServer","resync requested from",twinProxy.URI,"but not running in correct mode") raise Pyro.errors.NamingError("The (other) NS is not running in 'primary' or 'secondary' mode") # remotely called: def _synccall(self, method, *args): # temporarily disable the other NS oldOtherNS, self.otherNS = self.otherNS, None getattr(self, method) (*args) self.otherNS = oldOtherNS def resync(self): if self.role==Pyro.constants.NSROLE_SINGLE: raise Pyro.errors.NamingError("NS is not running in 'primary' or 'secondary' mode") if self.otherNS: try: self._initialResyncWithTwin(self.otherNS) return except Exception: pass raise Pyro.errors.NamingError("cannot resync: twin NS is unknown or unreachable") # local helper: def _dosynccall(self, method, *args): if self.role!=Pyro.constants.NSROLE_SINGLE and self.otherNS: try: self.otherNS._synccall(method, *args) except Exception,x: Log.warn("NameServer","ignored error in _synccall - but removing other NS",x) self.otherNS=None # --- hierarchical naming support def createGroup(self,groupname): groupname=self.validateName(groupname) if len(groupname)<2: raise Pyro.errors.NamingError('invalid groupname', groupname) with self.lock: (parent,name)=self.locateGrpAndName(groupname) if isinstance(parent,NameValue): raise Pyro.errors.NamingError('parent is no group', groupname) try: parent.newbranch(name) Log.msg('NameServer','created group',groupname) self._dosynccall("createGroup",groupname) except KeyError: raise Pyro.errors.NamingError('group already exists',name) def deleteGroup(self,groupname): groupname=self.validateName(groupname) if groupname==':': Log.msg('NameServer','attempt to deleteGroup root group') raise Pyro.errors.NamingError('not allowed to delete root group') with self.lock: (parent,name)=self.locateGrpAndName(groupname) try: parent.cutbranch(name) Log.msg('NameServer','deleted group',name) self._dosynccall("deleteGroup",groupname) except KeyError: raise Pyro.errors.NamingError('group not found',groupname) except ValueError: raise Pyro.errors.NamingError('is no group',groupname) def list(self,groupname): # not thread-locked: higher performance and not necessary. if not groupname: groupname=':' groupname=self.validateName(groupname) try: return self.getBranch(groupname).list() except KeyError: raise Pyro.errors.NamingError('group not found',groupname) except AttributeError: raise Pyro.errors.NamingError('is no group',groupname) # --- meta info support def setMeta(self, name, meta): name=self.validateName(name) try: branch=self.getBranch(name) branch.setMeta(meta) self._dosynccall("setMeta",name,meta) except KeyError: raise Pyro.errors.NamingError('name not found',name) except AttributeError: raise Pyro.errors.NamingError('group not found',name) def getMeta(self, name): name=self.validateName(name) try: branch=self.getBranch(name) return branch.getMeta() except KeyError: raise Pyro.errors.NamingError('name not found',name) except AttributeError: raise Pyro.errors.NamingError('group not found',name) def _setSystemMeta(self, name, meta): name=self.validateName(name) try: branch=self.getBranch(name) branch.setSystemMeta(meta) self._dosynccall("_setSystemMeta",name,meta) except KeyError: raise Pyro.errors.NamingError('name not found',name) except AttributeError: raise Pyro.errors.NamingError('group not found',name) def _getSystemMeta(self, name): name=self.validateName(name) try: branch=self.getBranch(name) return branch.getSystemMeta() except KeyError: raise Pyro.errors.NamingError('name not found',name) except AttributeError: raise Pyro.errors.NamingError('group not found',name) # --- shut down the server def _shutdown(self): if self.ignoreShutdown: Log.msg('NameServer','received shutdown request, but shutdown is denied') return 'Shutdown request denied' else: Log.msg('NameServer','received shutdown request, will shutdown shortly') self.getDaemon().shutdown() return "Will shut down shortly" # --- private methods follow def _getSyncDump(self): return self.root def locateGrpAndName(self,name): # ASSUME name is absolute (from root) (which is required here) idx=name.rfind('.') if idx>=0: # name is hierarchical grpname=name[:idx] name=name[idx+1:] try: return (self.getBranch(grpname), name) except KeyError: raise Pyro.errors.NamingError('(parent)group not found',grpname) else: # name is in root return (self.root, name[1:]) def getBranch(self,name): # ASSUME name is absolute (from root) (which is required here) name=name[1:] if name: return reduce(lambda x,y: x[y], name.split('.'), self.root) else: return self.root def validateName(self,name): if name[0]==':': if ('' not in name.split('.')): for i in name: if ord(i)<33 or ord(i)>126 or i=='\\': raise Pyro.errors.NamingError('invalid character(s) in name',name) return name else: raise Pyro.errors.NamingError('invalid name',name) else: # name is not absolute. Make it absolute. return _expandName(name) def validateURI(self,URI): if isinstance(URI, Pyro.core.PyroURI): return URI try: return Pyro.core.PyroURI(URI) except: raise Pyro.errors.NamingError('invalid URI',URI) def publishURI(self, uri, verbose=0): # verbose is not used - always prints the uri. uri=str(uri) print 'URI is:',uri try: f=open(Pyro.config.PYRO_NS_URIFILE,'w') f.write(uri+'\n'); f.close() print 'URI written to:',Pyro.config.PYRO_NS_URIFILE Log.msg('NameServer','URI written to',Pyro.config.PYRO_NS_URIFILE) except: Log.warn('NameServer','Couldn\'t write URI to',Pyro.config.PYRO_NS_URIFILE) ############################################################################# # # NamedTree data type. Used for the hierarchical name server. # ############################################################################# class NameSpaceSystemMeta(object): def __init__(self, node, timestamp, owner): self.timestamp=timestamp self.owner=owner if isinstance(node, NamedTree): self.type=0 # tree else: self.type=1 # leaf def __str__(self): return "[type="+str(self.type)+" timestamp="+str(self.timestamp)+" owner="+str(self.owner)+"]" # All nodes in the namespace (groups, or namevalue pairs--leafs) have # a shared set of properties, most notably: meta information. class NameSpaceNode(object): def __init__(self, name, meta, owner): self.name=name self.systemMeta = NameSpaceSystemMeta(self, time.time(), owner) self.userMeta = meta def getMeta(self): return self.userMeta def getSystemMeta(self): return self.systemMeta def setMeta(self,meta): self.userMeta=meta def setSystemMeta(self,meta): if isinstance(meta, NameSpaceSystemMeta): self.systemMeta=meta else: raise TypeError("system meta info must be NameSpaceSystemMeta object") class NameValue(NameSpaceNode): def __init__(self, name, value=None, meta=None, owner=None): NameSpaceNode.__init__(self, name, meta, owner) self.value=value class NamedTree(NameSpaceNode): def __init__(self, name, meta=None, owner=None): NameSpaceNode.__init__(self, name, meta, owner) self.branches={} def newbranch(self,name): if name in self.branches.keys(): raise KeyError,'name already exists' t = NamedTree(name) self.branches[name]=t return t def newleaf(self,name,value=None): if name in self.branches.keys(): raise KeyError,'name already exists' l = NameValue(name,value) self.branches[name]=l return l def cutleaf(self,name): if isinstance(self.branches[name], NameValue): del self.branches[name] else: raise ValueError,'not a leaf' def cutbranch(self,name): if isinstance(self.branches[name], NamedTree): del self.branches[name] else: raise ValueError,'not a branch' def __getitem__(self,name): return self.branches[name] def list(self): l=[] for (k,v) in self.branches.items(): if isinstance(v, NamedTree): l.append( (k,0) ) # tree elif isinstance(v, NameValue): l.append( (k,1) ) # leaf else: raise ValueError('corrupt tree') return l def flatten(self,prefix=''): flat=[] for (k,v) in self.branches.items(): if isinstance(v, NameValue): flat.append( (prefix+k, v.value) ) elif isinstance(v, NamedTree): flat.extend(v.flatten(prefix+k+'.')) return flat ############################################################################# # # The Persistent Name Server (a Pyro Object). # This implementation uses the hierarchical file system to # store the groups (as directories) and objects (as files). # ############################################################################# _PNS_META_SUFFIX=".ns_meta" class PersistentNameServer(NameServer): def __init__(self, dbdir=None, role=Pyro.constants.NSROLE_SINGLE, identification=None): self.dbroot=os.path.join(Pyro.config.PYRO_STORAGE,dbdir or 'Pyro_NS_database') self._initdb_1() try: NameServer.__init__(self, role=role, identification=identification) except Pyro.errors.NamingError: pass self._initdb_2() def _initdb_1(self): # root is not a NamedTree but a directory try: os.mkdir(self.dbroot) except OSError,x: if x.errno not in (errno.EEXIST, errno.EBUSY): raise def _initdb_2(self): # make sure that the 2 initial groups (Pyro and Default) exist try: self.createGroup(':'+'Pyro') except Pyro.errors.NamingError: pass try: self.createGroup(Pyro.config.PYRO_NS_DEFAULTGROUP) except Pyro.errors.NamingError: pass def getDBDir(self): return self.dbroot def _initialResyncWithTwin(self, twinProxy): if twinProxy: Log.msg("NameServer","Initial resync with other NS at",twinProxy.URI.address,"port",twinProxy.URI.port) # keep old NS (self) registration oldNSreg=self.resolve(Pyro.constants.NAMESERVER_NAME) proxyForMe=NameServerProxy(self.getProxy().URI,noconnect=1) proxyForMe.adapter.setIdentification(self.identification,munge=False) # set pre-munged ident syncdump=twinProxy._resync(proxyForMe) self.otherNS = None # temporarily disable twin NS ref # clear the old database Log.msg("NameServer","erasing old database",self.dbroot) shutil.rmtree(self.dbroot) self._initdb_1() # phase 2 (creation of default groups) is not needed here Log.msg("NameServer","store sync database") for group,smeta,umeta in syncdump[0]: try: if group!=':': dirnam = self.translate(group) os.mkdir(dirnam) if smeta: self._setSystemMeta(group,smeta) if umeta: self.setMeta(group,umeta) except EnvironmentError,x: Log.warn("NameServer","problem creating group",group,x) for name,uri,smeta,umeta in syncdump[1]: try: origname,name=name,self.validateName(name) fn=self.translate(name) open(fn,'w').write(uri+'\n') if smeta: self._setSystemMeta(name,smeta) if umeta: self.setMeta(name,umeta) except Pyro.errors.NamingError,x: Log.warn("NameServer","problem registering name",name,x) # reset registration of self try: self.unregister(Pyro.constants.NAMESERVER_NAME) except: pass self.register(Pyro.constants.NAMESERVER_NAME,oldNSreg) self.otherNS=twinProxy Log.msg("NameServer","database sync complete.") print "Database synchronized." def register(self,name,URI): origname,name=name,self.validateName(name) URI=self.validateURI(URI) fn=self.translate(name) with self.lock: if os.access(fn,os.R_OK): Log.msg('NameServer','name already exists:',name) raise Pyro.errors.NamingError('name already exists',name) try: open(fn,'w').write(str(URI)+'\n') self._dosynccall("register",origname,URI) Log.msg('NameServer','registered',name,'with URI',str(URI)) except IOError,x: if x.errno==errno.ENOENT: raise Pyro.errors.NamingError('(parent)group not found') elif x.errno==errno.ENOTDIR: raise Pyro.errors.NamingError('parent is no group') else: raise Pyro.errors.NamingError(str(x)) def unregister(self,name): origname,name=name,self.validateName(name) fn=self.translate(name) with self.lock: try: os.remove(fn) self._dosynccall("unregister",origname) Log.msg('NameServer','unregistered',name) except OSError,x: if x.errno==errno.ENOENT: raise Pyro.errors.NamingError('name not found',name) elif x.errno==errno.EISDIR: Log.msg('NameServer','attempt to remove a group:',name) raise Pyro.errors.NamingError('is a group, not an object',name) else: raise Pyro.errors.NamingError(str(x)) def resolve(self,name): # not thread-locked: higher performance and not necessary. name=self.validateName(name) fn = self.translate(name) try: return Pyro.core.PyroURI(open(fn).read()) except IOError,x: if x.errno==errno.ENOENT: raise Pyro.errors.NamingError('name not found',name) elif x.errno==errno.EISDIR: Log.msg('NameServer','attempt to resolve groupname:',name) raise Pyro.errors.NamingError('attempt to resolve groupname',name) else: raise Pyro.errors.NamingError(str(x)) def flatlist(self): dbroot=self.translate(':') with self.lock: flat=[] for f in self._filelist(dbroot,dbroot): f=self._unescapefilename(f) flat.append((f, self.resolve(f))) return flat # --- hierarchical naming support def createGroup(self,groupname): groupname=self.validateName(groupname) dirnam = self.translate(groupname) with self.lock: try: os.mkdir(dirnam) self._dosynccall("createGroup",groupname) Log.msg('NameServer','created group',groupname) except OSError,x: if x.errno in (errno.EEXIST, errno.EBUSY): raise Pyro.errors.NamingError('group already exists',groupname) elif x.errno == errno.ENOENT: raise Pyro.errors.NamingError('(parent)group not found') else: raise Pyro.errors.NamingError(str(x)) def deleteGroup(self,groupname): groupname=self.validateName(groupname) if groupname==':': Log.msg('NameServer','attempt to deleteGroup root group') raise Pyro.errors.NamingError('not allowed to delete root group') dirnam = self.translate(groupname) with self.lock: if not os.access(dirnam,os.R_OK): raise Pyro.errors.NamingError('group not found',groupname) try: shutil.rmtree(dirnam) self._dosynccall("deleteGroup",groupname) Log.msg('NameServer','deleted group',groupname) except OSError,x: if x.errno==errno.ENOENT: raise Pyro.errors.NamingError('group not found',groupname) elif x.errno==errno.ENOTDIR: raise Pyro.errors.NamingError('is no group',groupname) else: raise Pyro.errors.NamingError(str(x)) def list(self,groupname): if not groupname: groupname=':' groupname=self.validateName(groupname) dirnam=self.translate(groupname) with self.lock: if os.access(dirnam,os.R_OK): if os.path.isfile(dirnam): raise Pyro.errors.NamingError('is no group',groupname) else: l = dircache.listdir(dirnam) entries = [] for e in l: if e.endswith(_PNS_META_SUFFIX): continue else: objname=self._unescapefilename(e) if os.path.isdir(os.path.join(dirnam,e)): entries.append((objname,0)) # dir has code 0 else: entries.append((objname,1)) # leaf has code 1 return entries raise Pyro.errors.NamingError('group not found',groupname) # --- private methods follow def _getSyncDump(self): def visitor(arg,dirname,names): shortdirname=dirname[len(self.dbroot)+len(os.path.sep):] if shortdirname.endswith(_PNS_META_SUFFIX): return name = ':'+shortdirname.replace(os.path.sep,'.') smeta=self._getSystemMeta(name) umeta=self.getMeta(name) arg[0].append( (name, smeta,umeta) ) for n in names: if n.endswith(_PNS_META_SUFFIX): continue n=os.path.join(dirname,n) if os.path.isfile(n): v=open(n,'r').read().strip() name=':'+(n[len(self.dbroot)+len(os.path.sep):]).replace(os.path.sep,'.') smeta=self._getSystemMeta(name) umeta=self.getMeta(name) arg[1].append( (name, v, smeta,umeta) ) result=( [], [] ) # (groups, names) os.path.walk(self.dbroot, visitor, result) return result def _unescapefilename(self, name): parts=name.split('\\') res=[parts[0]] myappend=res.append del parts[0] for item in parts: if item[1:2]: try: myappend(chr(int(item[:2], 16)) + item[2:]) except ValueError: myappend('\\' + item) else: myappend('\\' + item) return "".join(res) def _escapefilename(self,name): name=name.replace(os.path.sep,'\\%02x' % ord(os.path.sep)) # escape path separators in the name name=name.replace(':','\\%02x' % ord(':')) # also get rid of any ':' 's return name # recursive file listing, output is like "find -type f" # but using NS group separator chars def _filelist(self,root,path): try: (filez,dirz) = Pyro.util.listdir(path) except OSError: raise Pyro.errors.NamingError('group not found') files=[] for f in filez: if f.endswith(_PNS_META_SUFFIX): continue elif path==root: files.append(':'+f) else: p=path[len(root):].replace(os.sep, '.') files.append(':'+p+'.'+f) for d in dirz: files.extend(self._filelist(root,os.path.join(path,d))) return files # Pyro NS name to filesystem path translation def translate(self,name): if name[0]==':': name=name[1:] name=self._escapefilename(name) args=[self.dbroot]+name.split('.') return os.path.join(*args) def getBranch(self,name): tr = self.translate(name) if os.path.exists(tr): return PersistentNameSpaceNode(filename=tr+_PNS_META_SUFFIX) else: raise Pyro.errors.NamingError('name not found',name) # XXX this is a bit of a hack. Only metadata is stored here, # and it's only used from getBranch, which in turn is only used # from the set/get meta functions. class PersistentNameSpaceNode(NameSpaceNode): def __init__(self, filename, name=None, meta=None, owner=None): NameSpaceNode.__init__(self, name, meta, owner) self.filename=filename if not name: # init from file try: (sysmeta, usermeta)=Pyro.util.getPickle().load(open(self.filename,"rb")) NameSpaceNode.setSystemMeta(self, sysmeta) NameSpaceNode.setMeta(self, usermeta) except Exception: pass # just use empty meta... else: self._writeToFile() def setMeta(self,meta): NameSpaceNode.setMeta(self, meta) self._writeToFile() def setSystemMeta(self,meta): NameSpaceNode.setSystemMeta(self, meta) self._writeToFile() def _writeToFile(self): Pyro.util.getPickle().dump( (self.getSystemMeta(), self.getMeta()) , open(self.filename,"wb"), Pyro.config.PYRO_PICKLE_FORMAT) ############################################################################# # # The broadcast server which listens to broadcast requests of clients who # want to discover our location, or send other system commands. # ############################################################################# class BroadcastServer(SocketServer.UDPServer): nameServerURI = '' # the Pyro URI of the Name Server def __init__(self, addr, bcRequestHandler,norange=0): if norange: portrange=1 else: portrange=Pyro.config.PYRO_PORT_RANGE (location,port)=addr for port in range(port, port+portrange): try: SocketServer.UDPServer.__init__(self, (location,port), bcRequestHandler) return # got it! except socket.error: continue # try the next port in the list raise # port range exhausted... re-raise the socket error. def server_activate(self): self.requestValidator=lambda x,y: 1 # default: accept all self.shutdown=0 # should the server loop stop? self.preferredTimeOut=3.0 # preferred timeout for the server loop def setNS_URI(self,URI): self.nameServerURI=str(URI) def setRequestValidator(self, validator): self.requestValidator=validator def keepRunning(self, keep): self.ignoreShutdown = keep # ignore shutdown requests (i.e. keep running?) def bcCallback(self,ins): for i in ins: i.handle_request() def verify_request(self, req, addr): return self.requestValidator(req, addr) def getServerSocket(self): return self.socket class bcRequestHandler(SocketServer.BaseRequestHandler): def handle(self): Log.msg('BroadcastServer','incoming request from',str(self.client_address[0])) # request is a simple string cmd = self.request[0] if cmd==NS_SYSCMD_LOCATION: # somebody wants to know our location, give them our URI self.request[1].sendto(self.server.nameServerURI,self.client_address) elif cmd==NS_SYSCMD_SHUTDOWN: # we should die!? if self.server.ignoreShutdown: Log.msg('BroadcastServer','Shutdown ignored.') self.request[1].sendto('Shutdown request denied',self.client_address) else: Log.msg('BroadcastServer','Shutdown received.') print 'BroadcastServer received shutdown request... will shutdown shortly...' self.request[1].sendto('Will shut down shortly',self.client_address) self.server.shutdown=1 else: Log.warn('BroadcastServer','Invalid command ignored:',cmd) # The default BC request validator... accepts everything # You must subclass this for your own validators class BCReqValidator(object): def __call__(self, req, addr): (cmd,self.sock)=req self.addr=addr if cmd==NS_SYSCMD_LOCATION: return self.acceptLocationCmd() elif cmd==NS_SYSCMD_SHUTDOWN: return self.acceptShutdownCmd() else: return 0 def reply(self,msg): self.sock.sendto(msg,self.addr) def acceptLocationCmd(self): return 1 def acceptShutdownCmd(self): return 1 ############################################################################# class NameServerStarter(object): def __init__(self, identification=None): Pyro.core.initServer() self.persistent=False self.identification=identification self.started = Pyro.util.getEventObject() def start(self, *args, **kwargs): # see _start for allowed arguments kwargs["startloop"]=1 self._start( *args, **kwargs ) def initialize(self, *args, **kwargs): # see _start for allowed arguments kwargs["startloop"]=0 self._start( *args, **kwargs ) def getServerSockets(self): result=self.daemon.getServerSockets() if self.bcserver: result.append(self.bcserver.getServerSocket()) return result def waitUntilStarted(self,timeout=None): self.started.wait(timeout) return self.started.isSet() def _start(self,hostname=None, nsport=None, bcport=0, keep=0, persistent=0, dbdir=None, Guards=(None,None), allowmultiple=0, dontlookupother=0, verbose=0, startloop=1, role=(Pyro.constants.NSROLE_SINGLE,None), bcaddr=None, nobroadcast=False ): if nsport is None: if role[0]==Pyro.constants.NSROLE_SECONDARY: nsport=Pyro.config.PYRO_NS2_PORT else: nsport=Pyro.config.PYRO_NS_PORT if not bcport: if role[0]==Pyro.constants.NSROLE_SECONDARY: bcport=Pyro.config.PYRO_NS2_BC_PORT else: bcport=Pyro.config.PYRO_NS_BC_PORT if not bcaddr: if role[0]==Pyro.constants.NSROLE_SECONDARY: bcaddr=Pyro.config.PYRO_NS2_BC_ADDR else: bcaddr=Pyro.config.PYRO_NS_BC_ADDR otherNSuri=None try: if not dontlookupother: retries=Pyro.config.PYRO_BC_RETRIES timeout=Pyro.config.PYRO_BC_TIMEOUT Pyro.config.PYRO_BC_RETRIES=1 Pyro.config.PYRO_BC_TIMEOUT=0.7 try: otherNSuri=NameServerLocator().detectNS(bcaddr=bcaddr) except Pyro.errors.PyroError: pass else: print 'The Name Server appears to be already running on this segment.' print '(host:',otherNSuri.address,' port:',otherNSuri.port,')' if allowmultiple: print 'WARNING: starting another Name Server in the same segment!' elif role[0] in (Pyro.constants.NSROLE_PRIMARY, Pyro.constants.NSROLE_SECONDARY): pass else: msg='Cannot start multiple Name Servers in the same network segment.' print msg raise Pyro.errors.NamingError(msg) if role[0]!=Pyro.constants.NSROLE_SINGLE: print "Locating twin NameServer." # Do this before starting our own daemon, otherwise possible deadlock! # This step is done here to make pretty certain that one of both name # servers finds the other either *now*, or else later on (below). # If we omit this step here, deadlock may occur on the attempt below! otherNS = self.locateTwinNS(role, otherNSuri) if otherNS: print "Found twin NameServer at",otherNS.URI.address,"port",otherNS.URI.port role=(role[0], otherNS) Pyro.config.PYRO_BC_RETRIES=retries Pyro.config.PYRO_BC_TIMEOUT=timeout daemon = Pyro.core.Daemon(host=hostname, port=nsport,norange=1) except Pyro.errors.DaemonError,x: print 'The Name Server appears to be already running on this host.' print '(or somebody else occupies our port,',nsport,')' if hostname: print 'It could also be that the address \''+hostname+'\' is not correct.' print 'Name Server was not started!' raise if self.identification: daemon.setAllowedIdentifications([self.identification]) print 'Requiring connection authentication.' if Guards[0]: daemon.setNewConnectionValidator(Guards[0]) if persistent: ns=PersistentNameServer(dbdir,role=role[0], identification=self.identification) daemon.useNameServer(ns) NS_URI=daemon.connectPersistent(ns,Pyro.constants.NAMESERVER_NAME) self.persistent=True else: ns=NameServer(role=role[0], identification=self.identification) daemon.useNameServer(ns) NS_URI=daemon.connect(ns,Pyro.constants.NAMESERVER_NAME) self.persistent=False self.bcserver=None if nobroadcast: Log.msg('NS daemon','Not starting broadcast server due to config option') if verbose: print "Not starting broadcast server." else: # Try to start the broadcast server. Binding on the magic "" # address should work, but on some systems (windows) it doesn't. # Therefore we first try "", if that fails, try "". # If any address override is in place, use that ofcourse. notStartedError="" msg = daemon.validateHostnameAndIP() if msg: Log.msg('NS daemon','Not starting broadcast server because of issue with daemon IP address.') if verbose: print "Not starting broadcast server." else: if bcaddr: broadcastAddresses=[bcaddr] else: broadcastAddresses=["", "", "255.255.255.255"] for bc_bind in broadcastAddresses: try: self.bcserver = BroadcastServer((bc_bind,bcport),bcRequestHandler,norange=1) break except socket.error,x: notStartedError += str(x)+" " if not self.bcserver: print 'Cannot start broadcast server. Is somebody else occupying our broadcast port?' print 'The error(s) were:',notStartedError print '\nName Server was not started!' raise Pyro.errors.NamingError("cannot start broadcast server") if Guards[1]: self.bcserver.setRequestValidator(Guards[1]) self.bcserver.keepRunning(keep) if keep: ns.ignoreShutdown=True if verbose: print 'Will ignore shutdown requests.' else: ns.ignoreShutdown=False if verbose: print 'Will accept shutdown requests.' print 'Name server listening on:',daemon.sock.getsockname() if self.bcserver: print 'Broadcast server listening on:',self.bcserver.socket.getsockname() message = daemon.validateHostnameAndIP() if message: print "\nWARNING:",message,"\n" if Guards[0] or Guards[1]: if verbose: print 'Using security plugins:' if Guards[0]: clazz=Guards[0].__class__ if verbose: print ' NS new conn validator =',clazz.__name__,'from', clazz.__module__, ' ['+sys.modules.get(clazz.__module__).__file__+']' elif verbose: print ' default NS new conn validator' if Guards[1]: clazz=Guards[1].__class__ if verbose: print ' BC request validator =',clazz.__name__,'from', clazz.__module__, ' ['+sys.modules.get(clazz.__module__).__file__+']' elif verbose: print ' default BC request validator' ns.publishURI(NS_URI,verbose) if self.bcserver: self.bcserver.setNS_URI(NS_URI) Log.msg('NS daemon','This is the Pyro Name Server.') if persistent: Log.msg('NS daemon','Persistent mode, database is in',ns.getDBDir()) if verbose: print 'Persistent mode, database is in',ns.getDBDir() Log.msg('NS daemon','Starting on',daemon.hostname,'port', daemon.port) if self.bcserver: Log.msg('NS daemon','Broadcast server on port',bcport) else: Log.msg('NS daemon','No Broadcast server') if role[0]==Pyro.constants.NSROLE_PRIMARY: print "Primary", elif role[0]==Pyro.constants.NSROLE_SECONDARY: print "Secondary", print 'Name Server started.' # If we run in primary or secondary mode, resynchronize # the NS database with the other name server. # Try again to look it up if it wasn't found before. if role[0]!=Pyro.constants.NSROLE_SINGLE: if not otherNS: # try again to contact the other name server print "Locating twin NameServer again." otherNS = self.locateTwinNS(role, otherNSuri) role=(role[0], otherNS) if otherNS: # finally got it, resync! print "Found twin NameServer at",otherNS.URI.address,"port",otherNS.URI.port ns._initialResyncWithTwin(otherNS) self.started.set() # signal that we've started (for external threads) self.daemon=daemon if os.name!="java": daemon.setTimeout(20) if startloop: # I use a timeout here otherwise you can't break gracefully on Windoze try: if self.bcserver: daemon.requestLoop(lambda s=self: not s.bcserver.shutdown, self.bcserver.preferredTimeOut,[self.bcserver],self.bcserver.bcCallback) if self.bcserver.shutdown: self.shutdown(ns) else: daemon.requestLoop() except KeyboardInterrupt: Log.warn('NS daemon','shutdown on user break signal') print 'Shutting down on user break signal.' self.shutdown(ns) except: try: (exc_type, exc_value, exc_trb) = sys.exc_info() out = ''.join(traceback.format_exception(exc_type, exc_value, exc_trb)[-5:]) Log.error('NS daemon', 'Unexpected exception, type',exc_type, '\n--- partial traceback of this exception follows:\n', out,'\n--- end of traceback') print '*** Exception occured!!! Partial traceback:' print out print '*** Resuming operations...' finally: del exc_type, exc_value, exc_trb # delete frame refs to allow proper GC Log.msg('NS daemon','Shut down gracefully.') print 'Name Server gracefully stopped.' def locateTwinNS(self, role, otherNSuri): try: retries=Pyro.config.PYRO_BC_RETRIES timeout=Pyro.config.PYRO_BC_TIMEOUT Pyro.config.PYRO_BC_RETRIES=1 Pyro.config.PYRO_BC_TIMEOUT=1 try: if role[1]: (host,port)=(role[1]+':').split(':')[:2] if len(port)==0: port=None else: port=int(port) otherNS=NameServerLocator(self.identification).getNS(host,port,trace=0) else: if otherNSuri: otherNS=NameServerLocator(self.identification).getNS(host=otherNSuri.address, port=otherNSuri.port, trace=0) else: if role[0]==Pyro.constants.NSROLE_PRIMARY: port=Pyro.config.PYRO_NS2_BC_PORT else: port=Pyro.config.PYRO_NS_BC_PORT otherNS=NameServerLocator(self.identification).getNS(host=None,port=port,trace=0) Log.msg("NameServerStarted","Found twin NS at",otherNS.URI) return otherNS except Pyro.errors.ConnectionDeniedError,x: raise except Exception,x: print "WARNING: Cannot find twin NS yet: ",x Log.msg("NameServerStarter","Cannot find twin NS yet:",x) return None finally: Pyro.config.PYRO_BC_RETRIES=retries Pyro.config.PYRO_BC_TIMEOUT=timeout def handleRequests(self,timeout=None): # this method must be called from a custom event loop if self.bcserver: self.daemon.handleRequests(timeout, [self.bcserver], self.bcserver.bcCallback) if self.bcserver.shutdown: self.shutdown() else: self.daemon.handleRequests(timeout) def shutdown(self, ns=None): if ns: # internal shutdown call with specified NS object daemon=ns.getDaemon() else: # custom shutdown call w/o specified NS object, use stored instance daemon=self.daemon ns=daemon.getNameServer() del self.daemon ns._removeTwinNS() if not self.persistent: daemon.disconnect(ns) # clean up nicely only if not running in persistent mode if self.bcserver: self.bcserver.shutdown=1 daemon.shutdown() def main(argv): Args = Pyro.util.ArgParser() Args.parse(argv,'hkmrvxn:p:b:c:d:s:i:1:2:') if Args.hasOpt('h'): print 'Usage: pyro-ns [-h] [-k] [-m] [-r] [-x] [-n hostname] [-p port] [-b bcport] [-c bcaddr]' print ' [-i identification] [-d [databaselocation]] [-s securitymodule]' print ' [-1 [host:port]] [-2 [host:port]] [-v]' print ' where -p = NS server port (0 for auto)' print ' -n = non-default hostname to bind on' print ' -b = NS broadcast port' print ' -c = NS broadcast address override' print ' -x = do not start a broadcast listener' print ' -m = allow multiple instances in network segment' print ' -r = don\'t attempt to find already existing nameservers' print ' -k = keep running- do not respond to shutdown requests' print ' -d = use persistent database, provide optional storage directory' print ' -s = use given python module with security plugins' print ' -i = specify the required authentication ID' print ' -1 = runs this NS as primary, opt. specify where secondary is' print ' -2 = runs this NS as secondary, opt. specify where primary is' print ' -v = verbose output' print ' -h = print this help' raise SystemExit host = Args.getOpt('n',None) port = Args.getOpt('p',None) if port: port=int(port) bcport = int(Args.getOpt('b',0)) bcaddr = Args.getOpt('c',None) nobroadcast = Args.hasOpt('x') role=Pyro.constants.NSROLE_SINGLE roleArgs=None if Args.hasOpt('1'): role=Pyro.constants.NSROLE_PRIMARY roleArgs=Args.getOpt('1') if Args.hasOpt('2'): role=Pyro.constants.NSROLE_SECONDARY roleArgs=Args.getOpt('2') ident = Args.getOpt('i',None) verbose = Args.hasOpt('v') keep=Args.hasOpt('k') allowmultiple=Args.hasOpt('m') dontlookupother=Args.hasOpt('r') try: dbdir = Args.getOpt('d') persistent = 1 except KeyError: persistent = 0 dbdir = None try: secmod = __import__(Args.getOpt('s'),locals(),globals()) Guards = (secmod.NSGuard(), secmod.BCGuard()) except ImportError,x: print 'Error loading security module:',x print '(is it in your python import path?)' raise SystemExit except KeyError: secmod = None Guards = (None,None) Args.printIgnored() if Args.args: print 'Ignored arguments:', ' '.join(Args.args) print '*** Pyro Name Server ***' if ident: starter=NameServerStarter(identification=ident) else: starter=NameServerStarter() try: starter.start(host,port,bcport,keep,persistent,dbdir,Guards,allowmultiple,dontlookupother,verbose,role=(role,roleArgs),bcaddr=bcaddr,nobroadcast=nobroadcast) except (Pyro.errors.NamingError, Pyro.errors.DaemonError),x: # this error has already been printed, just exit. pass # allow easy starting of the NS by using python -m if __name__=="__main__": main(sys.argv[1:]) Pyro-3.14/Pyro/nsc.py0000644000076500000240000001413211537206303014716 0ustar irmenstaff00000000000000############################################################################# # # Pyro Name Server Control Tool # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import Pyro.constants import Pyro.util import Pyro.core import Pyro.errors from Pyro.naming import NameServerLocator from Pyro.errors import NamingError, ConnectionDeniedError, PyroError from Pyro.protocol import getHostname class PyroNSControl(object): def args(self, args): self.Args = Pyro.util.ArgParser() self.Args.parse(args,'h:p:c:i:') self.Args.printIgnored() if self.Args.args: cmd = self.Args.args[0] del self.Args.args[0] return cmd return None def connect(self, sysCmd=None): host = self.Args.getOpt('h',None) bcaddr = self.Args.getOpt('c',None) port = int(self.Args.getOpt('p', 0)) ident = self.Args.getOpt('i',None) if port==0: port=None locator = NameServerLocator(identification=ident) if not sysCmd: self.NS = locator.getNS(host,port,1,bcaddr=bcaddr) print 'NS is at',self.NS.URI.address,'('+(getHostname(self.NS.URI.address) or '??')+') port',self.NS.URI.port self.NS._setIdentification(ident) else: result = locator.sendSysCommand(sysCmd,host,port,1,bcaddr=bcaddr) print 'Result from system command',sysCmd,':',result def handleError(self, msg, exc): print "## %s: " % msg, if isinstance(exc.args, (list, tuple)): print "; ".join(exc.args[:-1]), else: print exc.args, print " ##" def ping(self): self.connect() self.NS.ping() print 'NS is up and running!' def listall(self): self.connect() flat=self.NS.flatlist() flat.sort() print '-------------- START DATABASE' for (name,val) in flat: print name,' --> ',str(val) print '-------------- END' def list(self): self.connect() if not self.Args.args: # list the current group print self.NS.fullName(''),'-->', self.printList(self.NS.list(None)) else: # list all subpaths for n in self.Args.args: print self.NS.fullName(n),' -->', try: self.printList(self.NS.list(n)) except NamingError,x: self.handleError("can't list", x) def printList(self,list): list.sort() print '(', for (n,t) in list: if t==0: print '['+n+']', elif t==1: print n, print ')' def resolve(self): self.connect() if not self.Args.args: print 'No arguments, nothing to resolve' else: for n in self.Args.args: print n,' -->', try: print self.NS.resolve(n) except NamingError,x: self.handleError("can't resolve", x) def register(self): self.connect() try: self.NS.register(self.Args.args[0],self.Args.args[1]) uri=Pyro.core.PyroURI(self.Args.args[1]) print 'registered',self.Args.args[0],' --> ',uri except NamingError,x: self.handleError('Error from NS',x) except IndexError: print 'Register needs 2 args: name URI' def remove(self): self.connect() for n in self.Args.args: try: self.NS.unregister(n) print n,'unregistered.' except NamingError,x: self.handleError("Can't unregister", x) def creategroup(self): self.connect() for n in self.Args.args: try: self.NS.createGroup(n) print n,'created.' except NamingError,x: self.handleError("Can't create group '"+n+"'",x) def deletegroup(self): self.connect() for n in self.Args.args: try: self.NS.deleteGroup(n) print n,'deleted.' except NamingError,x: self.handleError("Can't delete group '"+n+"'",x) def showmeta(self): self.connect() if not self.Args.args: print 'No arguments, nothing to show meta of' for n in self.Args.args: try: print "META INFO OF",self.NS.fullName(n) print "system meta info :",self.NS._getSystemMeta(n) print " user meta info :",self.NS.getMeta(n) except NamingError,x: self.handleError("Can't get metadata",x) def setmeta(self): self.connect() try: if len(self.Args.args)>2: raise IndexError name=self.Args.args[0] meta=self.Args.args[1] self.NS.setMeta(name,meta) print "Metadata of",name,"set." except IndexError: print 'Setmeta needs 2 args: name metadata' def resync(self): self.connect() self.NS.resync() print 'resync done' def shutdown(self): self.connect(sysCmd='shutdown') def usage(): print 'PyroNS control program - usage is as follows;' print '>> pyro-nsc [-h host] [-p port] [-c bcaddr] [-i identification] command [args...]' print 'where command is one of: ping, list, listall, resolve, register, remove, creategroup, deletegroup, showmeta, setmeta, resync, shutdown' print ' host is the host where the NS should be contacted' print ' port is the non-standard Pyro NS broadcast port' print ' (if host is specified, it is the Pyro port instead)' print ' bcaddr allows you to override the broadcast address' print ' identification is the authentication ID to connect to the server' print ' args... depend on the command.' raise SystemExit def main(argv): ctrl = PyroNSControl() cmd=ctrl.args(argv) if not cmd: usage() try: # nice construct to map commands to the member function to call call= { 'ping': ctrl.ping, 'list': ctrl.list, 'listall': ctrl.listall, 'resolve': ctrl.resolve, 'register': ctrl.register, 'remove': ctrl.remove, 'creategroup': ctrl.creategroup, 'deletegroup': ctrl.deletegroup, 'shutdown': ctrl.shutdown, 'showmeta': ctrl.showmeta, 'setmeta': ctrl.setmeta, 'resync': ctrl.resync } [cmd] except KeyError: usage() try: Pyro.core.initClient(banner=0) call() except ConnectionDeniedError,arg: print 'Could not connect to the server:',arg if str(arg)==Pyro.constants.deniedReasons[Pyro.constants.DENIED_SECURITY]: print "Supply correct authentication ID?" except PyroError,arg: print 'There is a problem:',arg except Exception,x: print 'CAUGHT ERROR, printing Pyro traceback >>>>>>',x print ''.join(Pyro.util.getPyroTraceback(x)) print '<<<<<<< end of Pyro traceback' # allow easy usage with python -m if __name__=="__main__": import sys main(sys.argv[1:]) Pyro-3.14/Pyro/protocol.py0000644000076500000240000013334511566312344016011 0ustar irmenstaff00000000000000############################################################################# # # Pyro Protocol Adapters # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# from __future__ import with_statement import socket, struct, os, time, sys, hmac, types, random, errno, select import imp, marshal, new, __builtin__ try: import hashlib md5=hashlib.md5 except ImportError: import md5 md5=md5.md5 import Pyro.constants, Pyro.util from Pyro.errors import * from Pyro.errors import _InternalNoModuleError pickle = Pyro.util.getPickle() Log = Pyro.util.Log if Pyro.util.supports_multithreading(): from threading import Thread,currentThread _has_threading = 1 else: _has_threading = 0 if Pyro.util.supports_compression(): import zlib _has_compression = 1 else: _has_compression = 0 try: from M2Crypto import SSL from M2Crypto.SSL import SSLError if _has_threading: import M2Crypto M2Crypto.threading.init() except ImportError: class SSLError(Exception): pass #------ Get the hostname (possibly of other machines) (returns None on error) def getHostname(ip=None): try: if ip: (hn,alias,ips) = socket.gethostbyaddr(ip) return hn else: return socket.gethostname() except socket.error: return None #------ Get IP address (return None on error) def getIPAddress(host=None): try: return socket.gethostbyname(host or getHostname()) except socket.error: return None #------ Socket helper functions for sending and receiving data correctly. # process optional timeout on socket. # notice the check for M2Crypto SSL sockets: if there's data pending, # a select on them will fail. So we avoid calling select in that case. def _sock_timeout_send(sock, timeout): if timeout and (not hasattr(sock,'pending') or sock.pending()==0): r,w,e=safe_select([],[sock],[],timeout) if not w: raise TimeoutError('connection timeout sending') def _sock_timeout_recv(sock, timeout): if timeout and (not hasattr(sock,'pending') or sock.pending()==0): r,w,e=safe_select([sock],[],[],timeout) if not r: raise TimeoutError('connection timeout receiving') # Receive a precise number of bytes from a socket. Raises the # ConnectionClosedError if that number of bytes was not available. # (the connection has probably been closed then). # Never will this function return an empty message (if size>0). # We need this because 'recv' isn't guaranteed to return all desired # bytes in one call, for instance, when network load is high. # Use a list of all chunks and join at the end: faster! # Handle EINTR states (interrupted system call) by just retrying. def sock_recvmsg(sock, size, timeout=0): while True: try: return _recv_msg(sock,size,timeout) except socket.timeout: raise TimeoutError("connection timeout receiving") except socket.error,x: if x.args[0] == errno.EINTR or (hasattr(errno, 'WSAEINTR') and x.args[0] == errno.WSAEINTR): # interrupted system call, just retry continue raise ConnectionClosedError('connection lost: %s' % x) except SSLError,x: raise ConnectionClosedError('connection lost: %s' % x) # select the optimal recv() implementation if hasattr(socket,"MSG_WAITALL") and not Pyro.config.PYRO_BROKEN_MSGWAITALL: def _recv_msg(sock,size,timeout): _sock_timeout_recv(sock,timeout) try: chunk=sock.recv(size, socket.MSG_WAITALL) # receive all data in one call except TypeError: # M2Crypto sock.recv() doesn't support MSG_WAITALL parameter return __recv_msg_compat(sock,size,timeout) else: if len(chunk)!=size: err=ConnectionClosedError('connection lost') err.partialMsg=chunk # store the message that was received until now raise err return chunk else: def _recv_msg(sock,size,timeout): _sock_timeout_recv(sock, timeout) return __recv_msg_compat(sock,size,timeout) def __recv_msg_compat(sock,size,timeout): # compatibility implementation for non-MSG_WAITALL / M2Crypto msglen=0 msglist=[] # Receive chunks of max. 60kb size: # (rather arbitrary limit, but it avoids memory/buffer problems on certain OSes -- VAX/VMS, Windows) while msglen", "exec") else: code = marshal.loads(module[8:]) importer=None try: loaded = 0 # XXX probably want maxtries here... while not loaded: # install a custom importer to intercept any extra needed modules # when executing the module code just obtained from the server imp.acquire_lock() importer = agent_import(__builtin__.__import__) __builtin__.__import__ = importer imp.release_lock() try: exec code in mod.__dict__ loaded = 1 except ImportError: mname = importer.name if importer is not None: __builtin__.__import__ = importer.orig_import importer = None # XXX probably want maxrecursion here... self._retrieveCode(mname, level+1) finally: if importer is not None: __builtin__.__import__ = importer.orig_import finally: imp.release_lock() # release the global import lock def _remoteInvocationMobileCode(self, method, flags, *args): # special trimmed-down version for mobile code methods (no locking etc) body=pickle.dumps((self.URI.objectID,method,flags,args),Pyro.config.PYRO_PICKLE_FORMAT) sock_sendmsg(self.conn.sock, self.createMsg(body), self.timeout) ver,answer,pflags = self.receiveMsg(self.conn,1) if answer is None: raise ProtocolError('incorrect answer received') answer=pickle.loads(answer) if isinstance(answer,PyroExceptionCapsule): if isinstance(answer.excObj,_InternalNoModuleError): # server couldn't load module, supply it return self.processMissingModuleError(answer.excObj, method, flags, args) else: # we have an encapsulated exception, raise it again. answer.raiseEx() return answer def remoteInvocation(self, method, flags, *args): with self.lock: # only 1 thread at a time may use this connection to call a remote method try: self.__pyrocallbusy=True return self._remoteInvocation(method, flags, *args) self.__pyrocallbusy=False finally: if self.__pyrocallbusy: # the call has been aborted before completion, close the connection # to avoid corrupt transfers on the next call self.release() def _remoteInvocation(self, method, flags, *args): if 'conn' not in self.__dict__.keys(): Log.error('PYROAdapter','no connection available in remoteinvocation') raise ProtocolError('no connection available in remoteinvocation') if method in self.onewayMethods: flags |= Pyro.constants.RIF_Oneway body=pickle.dumps((self.URI.objectID,method,flags,args),Pyro.config.PYRO_PICKLE_FORMAT) try: sock_sendmsg(self.conn.sock, self.createMsg(body), self.timeout) except (socket.error, ProtocolError, KeyboardInterrupt): # Communication error during write. To avoid corrupt transfers, we close the connection. # Otherwise we might receive the previous reply as a result of a new methodcall! # Special case for keyboardinterrupt: people pressing ^C to abort the client # may be catching the keyboardinterrupt in their code. We should probably be on the # safe side and release the proxy connection in this case too, because they might # be reusing the proxy object after catching the exception... self.release() raise else: if flags & Pyro.constants.RIF_Oneway: self.__pyrocallbusy=False return None # no answer required, return immediately ver,answer,pflags = self.receiveMsg(self.conn,1) # read the server's response, send no further replies self.__pyrocallbusy=False if answer is None: raise ProtocolError('incorrect answer received') # Try to get the answer from the server. # If there are import problems, try to get those modules from # the server too (if mobile code is enabled). if not Pyro.config.PYRO_MOBILE_CODE: answer = pickle.loads(answer) else: importer=None try: imp.acquire_lock() loaded = 0 # XXX maxtries here... while not loaded: # install a custom importer to intercept any extra needed modules # when unpickling the answer just obtained from the server imp.acquire_lock() importer = agent_import(__builtin__.__import__) __builtin__.__import__ = importer imp.release_lock() try: answer = pickle.loads(answer) loaded = 1 except ImportError: mname = importer.name if importer is not None: __builtin__.__import__ = importer.orig_import importer = None self._retrieveCode(mname, 0) finally: if importer is not None: __builtin__.__import__ = importer.orig_import imp.release_lock() if isinstance(answer,PyroExceptionCapsule): if isinstance(answer.excObj,_InternalNoModuleError): # server couldn't load the module, send it return self.processMissingModuleError(answer.excObj, method, flags, args) else: # we have an encapsulated exception, raise it again. answer.raiseEx() return answer def processMissingModuleError(self, errorinfo, method, flags, args): # server couldn't load module, supply it # XXX this code is ugly. and duplicated in remote_retrieve_code in core.py Log.msg('PYROAdapter',"server can't load module: "+errorinfo.modulename) try: importmodule=new.module('-agent-import-') mname=errorinfo.modulename # not used: fromlist=errorinfo.fromlist try: exec 'import '+mname in importmodule.__dict__ except ImportError: Log.error('PYROAdapter','Server wanted a non-existing module:',mname) raise PyroError('Server wanted a non-existing module',mname) m=eval('importmodule.'+mname) bytecode=None if hasattr(m,"_PYRO_bytecode"): # use the bytecode that was put there earlier, # this avoids recompiles of the source .py if we don't have .pyc bytecode available bytecode=m._PYRO_bytecode else: # try to load the module's compiled source, or the real .py source if that fails. # note that the source code (.py) is opened with universal newline mode if not hasattr(m,"__file__"): raise PyroError("cannot read module source code",mname) (filebase,ext)=os.path.splitext(m.__file__) if ext.startswith(".PY"): exts = ( (".PYO","rb"), (".PYC","rb"), (".PY","rU") ) # uppercase else: exts = ( (".pyo","rb"), (".pyc","rb"), (".py","rU") ) # lowercase for ext,mode in exts: try: bytecode=open(filebase+ext, mode).read() break except EnvironmentError: pass if bytecode: Log.msg('PYROAdapter',"sending module to server: "+mname) self._remoteInvocationMobileCode("remote_supply_code",0,mname, bytecode, self.conn.sock.getsockname()) # retry the method invocation return self._remoteInvocation(* (method, flags)+args) # use the non-locking call Log.error("PYROAdapter","cannot read module source code for module:", mname) raise PyroError("cannot read module source code",mname) finally: del importmodule # (private) receives a socket message, returns: (protocolver, message, protocolflags) def receiveMsg(self,conn,noReply=0): try: msg=sock_recvmsg(conn.sock, self.headerSize, self.timeout) (hid, ver, hsiz, bsiz, pflags, crc) = struct.unpack(self.headerFmt,msg) # store in the connection what pickle method this is if pflags&PFLG_XMLPICKLE_GNOSIS: conn.pflags|=PFLG_XMLPICKLE_GNOSIS if ver!=self.version: msg='incompatible protocol version' Log.error('PYROAdapter',msg) if not noReply: # try to report error to client, but most likely the connection will terminate: self.returnException(conn, ProtocolError(msg)) raise ProtocolError(msg) if hid!=self.headerID or hsiz!=self.headerSize: msg='invalid header' Log.error('PYROAdapter',msg) Log.error('PYROAdapter','INVALID HEADER DETAILS: ',conn,( hid, ver, hsiz, bsiz,pflags)) if not noReply: # try to report error to client, but most likely the connection will terminate: self.returnException(conn, ProtocolError(msg), shutdown=1) raise ProtocolError(msg) body=sock_recvmsg(conn.sock, bsiz, self.timeout) if pflags&PFLG_CHECKSUM: if _has_compression: if crc!=zlib.adler32(body): msg='checksum error' Log.error('PYROAdapter',msg) if not noReply: self.returnException(conn, ProtocolError(msg)) raise ProtocolError(msg) else: raise ProtocolError('cannot perform checksum') if pflags&PFLG_COMPRESSED: if _has_compression: body=zlib.decompress(body) else: # We received a compressed message but cannot decompress. # Is this really a server error? We now throw an exception on the server... raise ProtocolError('compression not supported') return ver,body,pflags except (socket.error, ProtocolError, KeyboardInterrupt),x: # Communication error during read. To avoid corrupt transfers, we close the connection. # Otherwise we might receive the previous reply as a result of a new methodcall! # Special case for keyboardinterrupt: people pressing ^C to abort the client # may be catching the keyboardinterrupt in their code. We should probably be on the # safe side and release the proxy connection in this case too, because they might # be reusing the proxy object after catching the exception... self.release() raise def _unpickleRequest(self, pflags, body): if pflags&PFLG_XMLPICKLE_GNOSIS: if Pyro.config.PYRO_XML_PICKLE=='gnosis': return pickle.loads(body) else: return Pyro.util.getXMLPickle('gnosis').loads(body) elif Pyro.config.PYRO_XML_PICKLE: Log.error('PYROAdapter','xml pickle required, got other pickle') raise ProtocolError('xml pickle required, got other pickle') else: return pickle.loads(body) def handleInvocation(self,daemon,conn): ver,body,pflags = self.receiveMsg(conn) if not body: # something went wrong even before receiving the full message body return if ver!=self.version: Log.error('PYROAdapter','incompatible protocol version') self.returnException(conn, ProtocolError('incompatible protocol version')) return # Unpickle the request, which is a tuple: # (object ID, method name, flags, (arg1,arg2,...)) importer=fromlist=None try: if Pyro.config.PYRO_MOBILE_CODE: # install a custom importer to intercept any extra needed modules # when unpickling the request just obtained from the client try: imp.acquire_lock() importer=agent_import(__builtin__.__import__) __builtin__.__import__=importer req=self._unpickleRequest(pflags, body) finally: __builtin__.__import__=importer.orig_import imp.release_lock() else: # no mobile code; just unpickle the stuff without a custom importer. req=self._unpickleRequest(pflags, body) if type(req)!=tuple or len(req)!=4 or type(req[3])!=tuple: # sanity check failed raise ProtocolError("invalid request data format") except ImportError,x: if Pyro.config.PYRO_MOBILE_CODE: # return a special exception that will be processed by client; # it will call the internal 'remote_supply_code' member if importer: modname=importer.name fromlist=importer.fromlist else: modname = x.args[0][16:] fromlist=None self.returnException(conn, _InternalNoModuleError(modname,fromlist),0) # don't shutdown! else: Log.error('PYROAdapter','code problem with incoming object: '+str(x)) self.returnException(conn, NoModuleError(* x.args)) return try: # find the object in the implementation database of our daemon o=daemon.getLocalObject(req[0]) except (KeyError, TypeError) ,x: Log.warn('PYROAdapter','Invocation to unknown object ignored:',x) self.returnException(conn, ProtocolError('unknown object ID')) return else: # Do the invocation. We are already running in our own thread. if req[2]&Pyro.constants.RIF_Oneway and Pyro.config.PYRO_ONEWAY_THREADED and daemon.threaded: # received a oneway call, run this in its own thread. thread=Thread(target=self._handleInvocation2, args=(daemon,req,pflags,conn,o,True)) thread.setDaemon(1) # thread must exit at program termination. thread.localStorage=LocalStorage() # set local storage for the new thread thread.start() else: # not oneway or not in threaded mode, just do the invocation synchronously self._handleInvocation2(daemon,req,pflags,conn,o,False) def _handleInvocation2(self, daemon, req, pflags, conn, obj, mustInitTLS=False): if mustInitTLS: daemon.initTLS(daemon.getLocalStorage()) try: flags=req[2] importer=None if not Pyro.config.PYRO_MOBILE_CODE: res = obj.Pyro_dyncall(req[1],flags,req[3]) # (method,flags,args) else: try: # install a custom importer to intercept any extra needed modules # when executing the remote method. (using the data passed in by # the client may trigger additional imports) imp.acquire_lock() importer=agent_import(__builtin__.__import__) __builtin__.__import__=importer res = obj.Pyro_dyncall(req[1],flags,req[3]) # (method,flags,args) finally: __builtin__.__import__=importer.orig_import imp.release_lock() if flags&Pyro.constants.RIF_Oneway: return # no result, return immediately # reply the result to the caller if pflags&PFLG_XMLPICKLE_GNOSIS: replyflags=PFLG_XMLPICKLE_GNOSIS if Pyro.config.PYRO_XML_PICKLE=='gnosis': body=pickle.dumps(res,Pyro.config.PYRO_PICKLE_FORMAT) else: body=Pyro.util.getXMLPickle('gnosis').dumps(res,Pyro.config.PYRO_PICKLE_FORMAT) else: replyflags=0 body=pickle.dumps(res,Pyro.config.PYRO_PICKLE_FORMAT) sock_sendmsg(conn.sock, self.createMsg(body,replyflags),self.timeout) except ImportError,ix: if Pyro.config.PYRO_MOBILE_CODE: # Return a special exception that will be processed by client; # it will call the internal 'remote_supply_code' member. # We have to use this seemingly complex way to signal the client # to supply us some code, but it is only a proxy! We can't *call* it! if importer: # grab the import info from our importer name=importer.name fromlist=importer.fromlist else: # XXX the importerror sometimes doesn't contain the package :-( name=ix.args[0][16:] fromlist=None Log.msg('PYROAdapter','failed to import',name) self.returnException(conn, _InternalNoModuleError(name,fromlist),0) # don't shutdown! else: Log.error('PYROAdapter','code problem with incoming object: '+str(ix)) self.returnException(conn, NoModuleError(* ix.args)) except Exception: # Handle the exception. Pass in if it was a oneway call, # those calls don't need any response to be sent. daemon.handleError(conn, bool(flags&Pyro.constants.RIF_Oneway)) def returnException(self, conn, exc, shutdown=1, args=None): # return an encapsulated exception to the client if conn.pflags&PFLG_XMLPICKLE_GNOSIS: pic=Pyro.util.getXMLPickle('gnosis') else: pic=pickle try: body=pic.dumps(PyroExceptionCapsule(exc,args),Pyro.config.PYRO_PICKLE_FORMAT) except Exception,x: # hmm, pickling the exception failed... pickle the string instead body=pic.dumps(PyroExceptionCapsule(PyroError(str(x)),args),Pyro.config.PYRO_PICKLE_FORMAT) sock_sendmsg(conn.sock, self.createMsg(body),self.timeout) if shutdown: conn.close() def handleConnection(self, conn, tcpserver): # Server-side connection stuff. Use auth code from tcpserver's validator. try: # Validate the connection source (host) immediately, # if it's ok, send authentication challenge, and read identification data to validate. (ok,reasonCode) = tcpserver.newConnValidator.acceptHost(tcpserver,conn) if ok: challenge=tcpserver.newConnValidator.createAuthChallenge(tcpserver,conn) if len(challenge)!=self.AUTH_CHALLENGE_SIZE: raise ValueError("Auth challenge must be exactly "+`self.AUTH_CHALLENGE_SIZE`+" bytes") sock_sendmsg(conn.sock, self.createMsg(challenge),self.timeout) ver,body,pflags = self.receiveMsg(conn) # only process the message if it makes a bit of sense if ver==self.version and body.startswith(self.connectMSG): token=body[len(self.connectMSG):] (ok,reasonCode) = tcpserver.newConnValidator.acceptIdentification(tcpserver,conn,token,challenge) if ok: self.sendAccept(conn) conn.connected=1 return 1 else: self.sendDeny(conn,reasonCode) else: self.sendDeny(conn,reasonCode) return 0 except ProtocolError: # ignore the message if it caused protocol errors return 0 # import wrapper class to help with importing remote modules class agent_import(object): def __init__(self, orig_import): self.orig_import=orig_import def __call__(self,name,iglobals={},ilocals={},fromlist=None, *rest, **krest): if os.name=="java": # workaround for odd Jython bug, iglobals and ilocals may not exist in this scope...(?!) iglobals=vars().get("iglobals",{}) ilocals=vars().get("ilocals",{}) # save the import details: self.name=name # note: this must be a str object self.fromlist=fromlist return self.orig_import(name,iglobals,ilocals,fromlist, *rest, **krest) # # The SSL adapter that handles SSL connections instead of regular sockets. # class PYROSSLAdapter(PYROAdapter): def __init__(self): PYROAdapter.__init__(self) try: from M2Crypto import SSL except ImportError: raise ProtocolError('SSL not available') self.ctx = SSL.Context('sslv23') if Pyro.config.PYROSSL_KEY: keyfile = os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_KEY) else: keyfile = None self.ctx.load_cert(os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_CERT), keyfile) self.ctx.load_client_ca(os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_CA_CERT)) self.ctx.load_verify_info(os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_CA_CERT)) self.ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert,10) self.ctx.set_allow_unknown_ca(1) Log.msg('PYROSSLAdapter','SSL Context initialized') def setTimeout(self, timeout): PYROAdapter.setTimeout(self, timeout) def bindToURI(self,URI): if URI.protocol not in ('PYROSSL','PYROLOCSSL'): Log.error('PYROSSLAdapter','incompatible protocol in URI:',URI.protocol) raise ProtocolError('incompatible protocol in URI') with self.lock: # only 1 thread at a time can bind the URI try: self.URI=URI sock = SSL.Connection(self.ctx,socket.socket(socket.AF_INET, socket.SOCK_STREAM)) if not Pyro.config.PYROSSL_POSTCONNCHECK: sock.postConnectionCheck=None sock.connect((URI.address, URI.port)) conn=TCPConnection(sock, sock.getpeername()) # receive the authentication challenge string, and use that to build the actual identification string. authChallenge=self.recvAuthChallenge(conn) # reply with our ident token, generated from the ident passphrase and the challenge msg = self._sendConnect(sock,self.newConnValidator.createAuthToken(self.ident, authChallenge, conn.addr, self.URI, None) ) if msg==self.acceptMSG: self.conn=conn self.conn.connected=1 Log.msg('PYROSSLAdapter','connected to',str(URI)) if URI.protocol=='PYROLOCSSL': self.resolvePYROLOC_URI("PYROSSL") # updates self.URI elif msg[:len(self.denyMSG)]==self.denyMSG: try: raise ConnectionDeniedError(Pyro.constants.deniedReasons[int(msg[-1])]) except (KeyError,ValueError): raise ConnectionDeniedError('invalid response') except socket.error: Log.msg('PYROSSLAdapter','connection failed to URI',str(URI)) raise ProtocolError('connection failed') def _sendConnect(self, sock, ident): return PYROAdapter._sendConnect(self, sock, ident) def getProtocolAdapter(protocol): if protocol in ('PYRO', 'PYROLOC'): return PYROAdapter() elif protocol in ('PYROSSL', 'PYROLOCSSL'): return PYROSSLAdapter() else: Log.error('getProtocolAdapter','unsupported protocol:',protocol) raise ProtocolError('unsupported protocol') #-------- TCPConnection object for TCPServer class class TCPConnection(object): def __init__(self, sock, addr): self.sock = sock set_sock_keepalive(self.sock) # enable tcp/ip keepalive on this socket self.addr = addr self.connected=0 # connected? self.pflags=0 # protocol flags def __del__(self): self.close() def fileno(self): return self.sock.fileno() def close(self): #self.sock.makefile().flush() self.sock.close() self.connected=0 def shutdown(self): #self.sock.makefile().flush() self.sock.shutdown(2) # no further send/receives def __str__(self): return 'TCPConnection with '+str(self.addr)+' connected='+str(self.connected) #-------- The New Connection Validators: #-------- DefaultConnValidator checks max number of connections & identification #-------- and ident check is done using hmac-md5 secure hash of passphrase+challenge. #-------- Contains client- & server-side auth code. class DefaultConnValidator(object): def __init__(self): self.setAllowedIdentifications(None) # default=accept all (None means all!) def acceptHost(self,daemon,connection): if len(daemon.connections)>=Pyro.config.PYRO_MAXCONNECTIONS: Log.msg('DefaultConnValidator','Too many open connections, closing',connection,'#conns=',len(daemon.connections)) return (0, Pyro.constants.DENIED_SERVERTOOBUSY) return (1,0) def acceptIdentification(self, daemon, connection, token, challenge): if "all" in self.allowedIDs: return (1,0) for authid in self.allowedIDs[:]: if self.createAuthToken(authid, challenge, connection.addr, None, daemon) == token: return (1,0) Log.warn('DefaultConnValidator','connect authentication failed on conn ',connection) return (0,Pyro.constants.DENIED_SECURITY) def createAuthToken(self, authid, challenge, peeraddr, URI, daemon): # Called from both client and server, is used to be able to validate the token. # client: URI & peeraddr provided, daemon is None # server: URI is None, peeraddr and daemon provided. # Return hmac-md5 secure hash of our authentication phrase & the challenge. return hmac.new(challenge, authid).digest() def createAuthChallenge(self, tcpserver, conn): # Server-side only, when new connection comes in. # Challenge is secure hash of: server IP, process ID, timestamp, random value # (NOTE: MUST RETURN EXACTLY AUTH_CHALLENGE_SIZE(=16) BYTES!) try: pid=os.getpid() except: pid=id(self) # at least jython has no getpid() string = '%s-%d-%.20f-%.20f' %(str(getIPAddress()), pid, time.time(), random.random()) return md5(string).digest() def mungeIdent(self, ident): # munge the identification string into something else that's # not easily guessed or recognised, like the md5 hash: return md5(ident).digest() def setAllowedIdentifications(self, ids): if ids is not None: if type(ids) in (types.TupleType, types.ListType): self.allowedIDs=map(self.mungeIdent, ids) # don't store ids themselves else: raise TypeError("ids must be a list") else: self.allowedIDs=["all"] # trick: allow all incoming authentications. #-------- basic SSL connection validator, a specialized default validator. class BasicSSLValidator(DefaultConnValidator): def __init__(self): DefaultConnValidator.__init__(self) def acceptHost(self,daemon,connection): (ok,code) = DefaultConnValidator.acceptHost(self, daemon, connection) if ok: peercert=connection.sock.get_peer_cert() return self.checkCertificate(peercert) return (ok,code) def checkCertificate(self,cert): # do something interesting with the cert here, in a subclass :) if cert is None: return (0,Pyro.constants.DENIED_SECURITY) return (1,0) #-------- Helper class for local storage. class LocalStorage(object): def __init__(self): self.caller=None #-------- TCPServer base class class TCPServer(object): def __init__(self, port, host='', threaded=_has_threading,prtcol='PYRO'): self._ssl_server = 0 self.connections = [] # connection threads self.initTLS=lambda tls: None # default do-nothing func if host: socket.gethostbyname(host) # validate hostname try: if prtcol=='PYROSSL': try: from M2Crypto import SSL except ImportError: raise ProtocolError('SSL not available') try: self.ctx = SSL.Context('sslv23') if Pyro.config.PYROSSL_KEY: keyfile = os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_KEY) else: keyfile = None self.ctx.load_cert(os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_CERT), keyfile) self.ctx.load_client_ca(os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_CA_CERT)) self.ctx.load_verify_info(os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_CA_CERT)) self.ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert,10) self.ctx.set_allow_unknown_ca(1) self._ssl_server = 1 Log.msg('TCPServer','SSL Context initialized') except: Log.warn('TCPServer','SSL Context could not be initialized !!!') self.setNewConnectionValidator(BasicSSLValidator()) else: self.setNewConnectionValidator(DefaultConnValidator()) # create server socket for new connections self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) set_reuse_addr(self.sock) set_sock_no_inherit(self.sock) self.sock.bind((host,port)) self.sock.listen(Pyro.config.PYRO_TCP_LISTEN_BACKLOG) if self._ssl_server: self.sock = SSL.Connection(self.ctx,self.sock) # wrap server socket as SSL socket # rest of members self.threaded = threaded self.mustShutdown=0 # global shutdown self.localStorage=LocalStorage() # TLS for systems that don't have threads return except socket.error,msg: raise ProtocolError(msg) Log.msg('TCPServer','initialized') def __del__(self): self.closedown(nolog=1) def setInitTLS(self, initTLS): if not callable(initTLS): raise TypeError("initTLS must be callable object") self.initTLS=initTLS # if in single thread mode, (re-)init the TLS right away. if not Pyro.config.PYRO_MULTITHREADED: self.initTLS(self.localStorage) def closedown(self, nolog=0): # explicit closedown request if len(self.connections)>0: if not nolog: Log.warn('TCPServer','Shutting down but there are still',len(self.connections),'active connections') for c in self.connections[:]: if isinstance(c,TCPConnection): c.close() if isinstance(c,Thread): c.join() self.connections=[] if hasattr(self,'sock'): self.sock.close() del self.sock def setNewConnectionValidator(self,validator): if not isinstance(validator, DefaultConnValidator): raise TypeError("validator must be specialization of DefaultConnValidator") self.newConnValidator=validator def getNewConnectionValidator(self): return self.newConnValidator def connectionHandler(self, conn): # Handle the connection and all requests that arrive on it. # This is only called in multithreading mode. self.initTLS(self.getLocalStorage()) try: if self.getAdapter().handleConnection(conn, self): Log.msg('TCPServer','new connection ',conn, ' #conns=',len(self.connections)) while not self.mustShutdown: try: if not conn.connected: # connection has been closed in the meantime! raise ConnectionClosedError() self.handleInvocation(conn) except ConnectionClosedError: # client went away. Exit immediately self.removeConnection(conn) return except (PyroExceptionCapsule, Exception): self.handleError(conn) else: # log entry has already been written by newConnValidator self.removeConnection(conn) finally: # exiting thread. self._removeFromConnectionList(None) def _removeFromConnectionList(self, obj): if self.threaded and currentThread: obj=currentThread() try: self.connections.remove(obj) except ValueError: pass # this is the preferred way of dealing with the request loop. def requestLoop(self, condition=lambda:1, timeout=3, others=[], callback=None): while condition() and not self.mustShutdown: self.handleRequests(timeout,others,callback) def handleRequests(self, timeout=None, others=[], callback=None): if others and not callback: raise ProtocolError('callback required') if self.threaded: self._handleRequest_Threaded(timeout,others,callback) else: self._handleRequest_NoThreads(timeout,others,callback) def _handleRequest_NoThreads(self,timeout,others,callback): # self.connections is used to keep track of TCPConnections socklist = self.connections+[self.sock]+others ins,outs,exs = safe_select(socklist,[],[],timeout) if self.sock in ins: # it was the server socket, new incoming connection ins.remove(self.sock) if self._ssl_server: try: csock, addr = self.sock.accept() #if not Pyro.config.PYROSSL_POSTCONNCHECK: # csock.postConnectionCheck=None except SSL.SSLError,error: Log.warn('TCPServer','SSL error: '+str(error)) return else: csock, addr = self.sock.accept() conn=TCPConnection(csock,addr) if self.getAdapter().handleConnection(conn, self): Log.msg('TCPServer','new connection ',conn, ' #conns=',len(self.connections)) self.connections.append(conn) else: # connection denied, log entry has already been written by newConnValidator self.removeConnection(conn) for c in ins[0:]: if isinstance(c,TCPConnection): ins.remove(c) try: self.handleInvocation(c) if not c.connected: self.removeConnection(c) except ConnectionClosedError: # client went away. self.removeConnection(c) except: self.handleError(c) if ins and callback: # the 'others' must have fired... callback(ins) # def handleInvocation(self, conn): .... abstract method (implemented in subclass) def _handleRequest_Threaded(self,timeout,others,callback): # self.connections is used to keep track of connection Threads socklist = [self.sock]+others ins,outs,exs = safe_select(socklist,[],[],timeout) if self.sock in ins: # it was the server socket, new incoming connection if self._ssl_server: try: csock, addr = self.sock.accept() #if not Pyro.config.PYROSSL_POSTCONNCHECK: # csock.postConnectionCheck=None except SSL.SSLError,error: Log.warn('TCPServer','SSL error: '+str(error)) return else: csock, addr = self.sock.accept() conn=TCPConnection(csock,addr) thread=Thread(target=self.connectionHandler, args=(conn,)) thread.setDaemon(1) # thread must exit at program termination. thread.localStorage=LocalStorage() self.connections.append(thread) thread.start() elif callback: # the 'others' must have fired... callback(ins) def getLocalStorage(self): # return storage object for this thread. if self.threaded: return currentThread().localStorage else: return self.localStorage # to be called if a dropped connection is detected: def removeConnection(self, conn): conn.close() self._removeFromConnectionList(conn) Log.msg('TCPServer','removed connection ',conn,' #conns=',len(self.connections)) # to be called to stop all connections and shut down. def shutdown(self): self.mustShutdown=1 def getAdapter(self): raise NotImplementedError,'must be overridden to return protocol adapter' def handleError(self,conn,onewaycall=False): raise NotImplementedError,'must be overridden' def getServerSockets(self): if self.threaded: return [self.sock] else: return map(lambda conn: conn.sock, self.connections)+[self.sock] # Sometimes safe_select() raises an select.error exception with the EINTR # errno flag set, which basically tells the caller to try again later. # This safe_select method works around this case and indeed just tries again. _selectfunction=select.select if os.name=="java": from select import cpython_compatible_select as _selectfunction def safe_select(r,w,e,timeout=None): delay=timeout while True: try: # Make sure we don't delay longer than requested start=time.time() if delay is not None: return _selectfunction(r,w,e,delay) else: return _selectfunction(r,w,e) except select.error,x: if x.args[0] == errno.EINTR or (hasattr(errno, 'WSAEINTR') and x.args[0] == errno.WSAEINTR): delay=max(0.0,time.time()-start) else: raise Pyro-3.14/Pyro/test/0000755000076500000240000000000011567263336014553 5ustar irmenstaff00000000000000Pyro-3.14/Pyro/test/__init__.py0000644000076500000240000000003711563043040016644 0ustar irmenstaff00000000000000# just to make this a package. Pyro-3.14/Pyro/test/echoserver.py0000644000076500000240000000651211526320251017257 0ustar irmenstaff00000000000000############################################################################# # # Pyro Echo Server, for test purposes # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import sys import time from threading import Thread import Pyro.core import Pyro.naming import Pyro.errors class EchoServer(Pyro.core.ObjBase): verbose=False def echo(self, args): if self.verbose: print ("%s - echo: %s" % (time.asctime(), args)) return args def error(self): if self.verbose: print ("%s - error: generating exception" % time.asctime()) return 1//0 # division by zero error class NameServer(Thread): def __init__(self, hostname): Thread.__init__(self) self.setDaemon(1) self.starter = Pyro.naming.NameServerStarter() self.hostname=hostname def run(self): self.starter.start(hostname=self.hostname, dontlookupother=True) def waitUntilStarted(self): return self.starter.waitUntilStarted() def getHostAndPort(self): d=self.starter.daemon return d.hostname, d.port def shutdown(self): self.starter.shutdown() def startNameServer(host): ns=NameServer(host) ns.start() ns.waitUntilStarted() return ns def main(args): from optparse import OptionParser parser=OptionParser() parser.add_option("-H","--host", default="localhost", help="hostname to bind server on (default=localhost)") parser.add_option("-p","--port", type="int", default=0, help="port to bind server on") parser.add_option("-n","--naming", action="store_true", default=False, help="register with nameserver") parser.add_option("-N","--nameserver", action="store_true", default=False, help="also start a nameserver") parser.add_option("-v","--verbose", action="store_true", default=False, help="verbose output") options,args = parser.parse_args(args) nameserver=None if options.nameserver: options.naming=True nameserver=startNameServer(options.host) print("") print ("Starting Pyro's built-in test echo server.") d=Pyro.core.Daemon(host=options.host, port=options.port, norange=True) echo=EchoServer() echo.verbose=options.verbose objectName=":Pyro.test.echoserver" if options.naming: host,port=None,None if nameserver is not None: host,port=nameserver.getHostAndPort() ns=Pyro.naming.NameServerLocator().getNS(host,port) try: ns.createGroup(":Pyro.test") except Pyro.errors.NamingError: pass d.useNameServer(ns) if options.verbose: print ("using name server at %s" % ns.URI) else: if options.verbose: print ("not using a name server.") uri=d.connect(echo, objectName) print ("object name = %s" % objectName) print ("echo uri = %s" % uri) print ("echo uri = PYROLOC://%s:%d/%s" % (d.hostname, d.port, objectName)) print ("echoserver running.") try: d.requestLoop() finally: d.shutdown(disconnect=True) if nameserver is not None: #nameserver.shutdown() pass if __name__=="__main__": main(sys.argv[1:]) Pyro-3.14/Pyro/util.py0000644000076500000240000004240611537206303015115 0ustar irmenstaff00000000000000############################################################################# # # Pyro Utilities # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# from __future__ import with_statement import os, sys, traceback import time, random, linecache import socket, binascii import Pyro.constants from Pyro.util2 import * # bring in 'missing' util functions # bogus lock class, for systems that don't have threads. class BogusLock(object): def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): pass def acquire(self): pass def release(self): pass def getLockObject(): if supports_multithreading(): # XXX from threading import Lock return Lock() else: return BogusLock() def getRLockObject(): if supports_multithreading(): from threading import RLock return RLock() else: return BogusLock() # bogus event class, for systems that don't have threads class BogusEvent(object): def __init__(self): self.flag=0 def isSet(self): return self.flag==1 def set(self): self.flag=1 def clear(self): self.flag=0 def wait(self,timeout=None): raise RuntimeError("cannot wait in non-threaded environment") def getEventObject(): if supports_multithreading(): from threading import Event return Event() else: return BogusEvent() # Logging stuff. # Select the logging implementation to use! if Pyro.config.PYRO_STDLOGGING: # new-style logging using logging module, python 2.3+ import logging, logging.config cfgfile=Pyro.config.PYRO_STDLOGGING_CFGFILE if not os.path.isabs(cfgfile): Pyro.config.PYRO_STDLOGGING_CFGFILE=os.path.join(Pyro.config.PYRO_STORAGE, cfgfile) cfgfile=Pyro.config.PYRO_STDLOGGING_CFGFILE externalConfig=0 try: open(cfgfile).close() logging.config.fileConfig(cfgfile) externalConfig=1 except IOError,x: # Config file couldn't be read! Use builtin config. # First make the logfiles absolute paths: if not os.path.isabs(Pyro.config.PYRO_LOGFILE): Pyro.config.PYRO_LOGFILE=os.path.join(Pyro.config.PYRO_STORAGE, Pyro.config.PYRO_LOGFILE) if not os.path.isabs(Pyro.config.PYRO_USER_LOGFILE): Pyro.config.PYRO_USER_LOGFILE=os.path.join(Pyro.config.PYRO_STORAGE, Pyro.config.PYRO_USER_LOGFILE) class LoggerBase(object): if externalConfig: def __init__(self): self.logger=logging.getLogger(self._getLoggerName()) else: def __init__(self): self.logger=logging.getLogger("Pyro."+str(id(self))) # each time a different logger ... self.setLevel(self._getPyroLevel()) handler=logging.FileHandler(self._logfile()) handler.setFormatter(logging.Formatter("%(asctime)s [%(process)d:%(thread)d] ** %(levelname)s ** %(message)s")) self.logger.addHandler(handler) def setLevel(self, pyroLevel): if pyroLevel>=3: self.logger.setLevel(logging.DEBUG) elif pyroLevel>=2: self.logger.setLevel(logging.WARN) elif pyroLevel>=1: self.logger.setLevel(logging.ERROR) else: self.logger.setLevel(999) def msg(self,source,*args): self.setLevel(self._getPyroLevel()) if not args: (args, source) = ([source], "N/A") self.logger.info("%s ** %s", source, reduce(lambda x,y: str(x)+' '+str(y),args)) def warn(self,source,*args): self.setLevel(self._getPyroLevel()) if not args: (args, source) = ([source], "N/A") self.logger.warn("%s ** %s", source, reduce(lambda x,y: str(x)+' '+str(y),args)) def error(self,source,*args): self.setLevel(self._getPyroLevel()) if not args: (args, source) = ([source], "N/A") self.logger.error("%s ** %s", source, reduce(lambda x,y: str(x)+' '+str(y),args)) def raw(self,ztr): self.logger.log(999,ztr.rstrip()) def _logfile(self): raise NotImplementedError,'must override' def _getlevel(self): raise NotImplementedError,'must override' class SystemLogger(LoggerBase): def _getLoggerName(self): return "Pyro.system" def _getPyroLevel(self): return Pyro.config.PYRO_TRACELEVEL def _logfile(self): return Pyro.config.PYRO_LOGFILE class UserLogger(LoggerBase): def _getLoggerName(self): return "Pyro.user" def _getPyroLevel(self): return Pyro.config.PYRO_USER_TRACELEVEL def _logfile(self): return Pyro.config.PYRO_USER_LOGFILE else: # classic Pyro logging. class LoggerBase(object): # Logger base class. Subclasses must override _logfile and _checkTraceLevel. def __init__(self): self.lock=getLockObject() def msg(self,source,*args): if self._checkTraceLevel(3): self._trace('NOTE',source, args) def warn(self,source,*args): if self._checkTraceLevel(2): self._trace('WARN',source, args) def error(self,source,*args): if self._checkTraceLevel(1): self._trace('ERR!',source, args) def raw(self,str): with self.lock: f=open(self._logfile(),'a') f.write(str) f.close() def _trace(self,typ,source, arglist): with self.lock: if not arglist: (arglist, source) = ([source], "N/A") try: tf=open(self._logfile(),'a') try: pid=os.getpid() pidinfo=" ["+str(os.getpid()) except: pidinfo=" [" # at least jython has no getpid() if supports_multithreading(): pidinfo+=":"+threading.currentThread().getName() pidinfo+="] " tf.write(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))+ pidinfo+'** '+typ+' ** '+str(source)+' ** '+reduce(lambda x,y: str(x)+' '+str(y),arglist)+'\n') tf.close() except Exception,x: pass def _logfile(self): raise NotImplementedError,'must override' def _checkTraceLevel(self,level): raise NotImplementedError,'must override' class SystemLogger(LoggerBase): def _checkTraceLevel(self, level): return Pyro.config.PYRO_TRACELEVEL >= level def _logfile(self): filename=Pyro.config.PYRO_LOGFILE if not os.path.isabs(filename): Pyro.config.PYRO_LOGFILE=os.path.join(Pyro.config.PYRO_STORAGE, filename) return Pyro.config.PYRO_LOGFILE class UserLogger(LoggerBase): def _checkTraceLevel(self, level): return Pyro.config.PYRO_USER_TRACELEVEL >= level def _logfile(self): filename=Pyro.config.PYRO_USER_LOGFILE if not os.path.isabs(filename): Pyro.config.PYRO_USER_LOGFILE=os.path.join(Pyro.config.PYRO_STORAGE, filename) return Pyro.config.PYRO_USER_LOGFILE # The logger object 'Log'. Log = SystemLogger() # Caching directory lister, outputs (filelist,dirlist) tuple # Based upon dircache.py, but implemented in a callable object # that has a thread-safe cache. class DirLister(object): def __init__(self): self.lock=getLockObject() self.__listdir_cache = {} def __call__(self,path): with self.lock: try: cached_mtime, files, directories = self.__listdir_cache[path] del self.__listdir_cache[path] except KeyError: cached_mtime, files, directories = -1, [], [] mtime = os.stat(path)[8] if mtime <> cached_mtime: files=[] directories=[] for e in os.listdir(path): if os.path.isdir(os.path.join(path,e)): directories.append(e) else: files.append(e) with self.lock: self.__listdir_cache[path] = mtime, files, directories return files,directories listdir = DirLister() # callable object # Fairly simple argument options parser. Like getopt(3). class ArgParser(object): def __init__(self): pass def parse(self, args, optionlist): # optionlist is a string such as "ab:c" which means # we search for 3 options (-a, -b, -c) of which -b has an argument. self.options={} # public, the option->value dictionary self.args=[] # public, the rest of the arguments self.ignored=[] # public, ignored options optionlist+=' ' # add sentinel if type(args)==type(''): args=args.split() while args: arg=args[0] del args[0] if arg[0]=='-': if len(arg)>=2: # arg is an option. Check our list idx = optionlist.find(arg[1]) if idx>=0: if optionlist[idx+1]==':': # option requires argument. if len(arg)>=3: # argument is appended. Use this. self.options[arg[1]]=arg[2:] continue # fetch argument from next string if len(args)>=1: self.options[arg[1]]=args[0] del args[0] continue else: # missing arg, substitute None self.options[arg[1]]=None else: # option requires no argument, use None self.options[arg[1]]=None else: # didn't find this option, skip it self.ignored.append(arg[1]) else: # arg is a single '-'. Stop parsing. for a in args: self.args.append(a) args=None else: # arg is no option, add it to the residu list and continue self.args.append(arg) def hasOpt(self, option): return self.options.has_key(option) def getOpt(self, option, default=Exception()): try: return self.options[option] except KeyError: if not isinstance(default,Exception): return default raise KeyError('no such option') def printIgnored(self): if self.ignored: print 'Ignored options:', for o in self.ignored: print '-'+o, print _getGUID_counter=0 # extra safeguard against double numbers _getGUID_lock=getLockObject() if os.name=='java': # define jython specific stuff # first, the guid stuff. try java5 uuid first. try: from java.util import UUID def getGUID(): return str(UUID.randomUUID()) except ImportError: # older java, use rmi's vmid instead from java.rmi.dgc import VMID def getGUID(): return str(VMID().toString().replace(':','-').replace('--','-')) import imp if not hasattr(imp,"acquire_lock"): # simulate missing imp.acquire_lock() from jython 2.2 (fixed in jython 2.5) imp_lock=getLockObject() def imp_acquire_lock(): return imp_lock.acquire() def imp_release_lock(): return imp_lock.release() imp.acquire_lock=imp_acquire_lock imp.release_lock=imp_release_lock elif sys.platform=='cli': import System def getGUID(): # IronPython uses .NET guid call return System.Guid.NewGuid().ToString() else: def getGUID(): # Generate readable GUID string. # The GUID is constructed as follows: hexlified string of # AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC (a 128-bit number in hex) # where A=network address, B=timestamp, C=random. # The 128 bit number is returned as a string of 16 8-bits characters. # For A: should use the machine's MAC ethernet address, but there is no # portable way to get it... use the IP address + 2 bytes process id. try: ip=socket.gethostbyname(socket.gethostname()) networkAddrStr=binascii.hexlify(socket.inet_aton(ip))+"%04x" % os.getpid() except socket.error: # can't get IP address... use another value, like our Python id() and PID Log.warn('getGUID','Can\'t get IP address') try: ip=os.getpid() except: ip=0 ip += id(getGUID) networkAddrStr = "%08lx%04x" % (ip, os.getpid()) with _getGUID_lock: # cannot generate multiple GUIDs at once global _getGUID_counter t1=time.time()*100 +_getGUID_counter _getGUID_counter+=1 t2=int((t1*time.clock())%sys.maxint) & 0xffffff t1=int(t1%sys.maxint) timestamp = (long(t1) << 24) | t2 r2=(random.randint(0,sys.maxint//2)>>4) & 0xffff r3=(random.randint(0,sys.maxint//2)>>5) & 0xff return networkAddrStr+'%014x%06x' % (timestamp, (r2<<8)|r3 ) def genguid_scripthelper(argv): p=ArgParser() p.parse(argv,'') if p.args or p.ignored: print 'Usage: genguid (no arguments)' print 'This tool generates Pyro UIDs.' raise SystemExit print getGUID() # Get the configured pickling module. # Currently supported: cPickle, pickle, gnosis.xml.pickle (@paranoia 0 or -1). def getPickle(): if Pyro.config.PYRO_XML_PICKLE: # user requires xml pickle. Fails if that is not available! return getXMLPickle() else: try: import cPickle return cPickle except ImportError: # Fall back on pickle if cPickle isn't available import pickle return pickle _xmlpickle={} def getXMLPickle(impl=None): # load & config the required xml pickle. # Currently supported: Gnosis Utils' gnosis.xml.pickle. global _xmlpickle if not impl: impl=Pyro.config.PYRO_XML_PICKLE if impl in _xmlpickle: return _xmlpickle[impl] try: if impl=='gnosis': import gnosis.xml.pickle import gnosis.version gnosisVer=(gnosis.version.MAJOR, gnosis.version.MINOR) if gnosisVer==(1,2): # gnosis 1.2 style pickling, with paranoia setting _xmlpickle[impl]=gnosis.xml.pickle gnosis.xml.pickle.setParanoia(Pyro.config.PYRO_GNOSIS_PARANOIA) # default paranoia level is too strict for Pyro gnosis.xml.pickle.setParser('SAX') # use fastest parser (cEXPAT?) return gnosis.xml.pickle elif gnosisVer>=(1,3): from gnosis.xml.pickle import SEARCH_ALL, SEARCH_STORE, SEARCH_NO_IMPORT, SEARCH_NONE if Pyro.config.PYRO_GNOSIS_PARANOIA<0: class_search_flag = SEARCH_ALL # allow import of needed modules elif Pyro.config.PYRO_GNOSIS_PARANOIA==0: class_search_flag = SEARCH_NO_IMPORT # dont import new modules, only use known else: class_search_flag = SEARCH_STORE # only use class store # create a wrapper class to be able to pass additional args into gnosis methods class GnosisPickle: def dumps(data, *args,**kwargs): return gnosis.xml.pickle.dumps(data, allow_rawpickles=0) dumps=staticmethod(dumps) def loads(xml, *args, **kwargs): return gnosis.xml.pickle.loads(xml, allow_rawpickles=0, class_search=class_search_flag) loads=staticmethod(loads) def dump(data, file, *args,**kwargs): return gnosis.xml.pickle.dump(data, file, allow_rawpickles=0) dump=staticmethod(dump) def load(file, *args, **kwargs): return gnosis.xml.pickle.load(file, allow_rawpickles=0, class_search=class_search_flag) load=staticmethod(load) _xmlpickle[impl]=GnosisPickle return GnosisPickle else: raise NotImplementedError('no supported Gnosis tools version found (need at least 1.2). Found '+gnosis.version.VSTRING) else: raise ImportError('unsupported xml pickle implementation requested: %s' % impl) except ImportError: Log.error('xml pickling implementation (%s) is not available' % impl) raise NotImplementedError('xml pickling implementation (%s) is not available' % impl) # Pyro traceback printing def getPyroTraceback(exc_obj): def formatRemoteTraceback(remote_tb_lines) : result=[] result.append(" +--- This exception occured remotely (Pyro) - Remote traceback:") for line in remote_tb_lines : if line.endswith("\n"): line=line[:-1] lines = line.split("\n") for line in lines : result.append("\n | ") result.append(line) result.append("\n +--- End of remote traceback") return result try: exc_type, exc_value, exc_trb=sys.exc_info() remote_tb=getattr(exc_obj,Pyro.constants.TRACEBACK_ATTRIBUTE,None) local_tb=formatTraceback(exc_type, exc_value, exc_trb) if remote_tb: remote_tb=formatRemoteTraceback(remote_tb) return local_tb + remote_tb else: # hmm. no remote tb info, return just the local tb. return local_tb finally: # clean up cycle to traceback, to allow proper GC del exc_type, exc_value, exc_trb def formatTraceback(ex_type=None, ex_value=None, tb=None): if ex_type is None and tb is None: ex_type,ex_value,tb=sys.exc_info() if Pyro.config.PYRO_DETAILED_TRACEBACK: get_line_number = traceback.tb_lineno res = ['-'*50+ "\n", " <%s> RAISED : %s\n" % (str(ex_type), str(ex_value)), " Extended Stacktrace follows (most recent call last)\n", '-'*50+'\n' ] try: # Do some manipulation shit of stack if tb != None: frame_stack = [] line_number_stack = [] #tb = sys.exc_info()[2] while 1: line_num = get_line_number(tb) line_number_stack.append(line_num) if not tb.tb_next: break tb = tb.tb_next f = tb.tb_frame for x in line_number_stack: frame_stack.append(f) f = f.f_back frame_stack.reverse() lines = iter(line_number_stack) seen_crap = 0 for frame in frame_stack: # Get items flocals = frame.f_locals.items()[:] line_num = lines.next() filename = frame.f_code.co_filename name = None for key, value, in flocals: if key == "self": name = "%s::%s" % (value.__class__.__name__, frame.f_code.co_name) if name == None: name = frame.f_code.co_name res.append('File "%s", line (%s), in %s\n' % (filename, line_num, name)) res.append("Source code:\n") code_line = linecache.getline(filename, line_num) if code_line: res.append(' %s\n' % code_line.strip()) if not seen_crap: seen_crap = 1 continue res.append("Local values:\n") flocals.sort() fcode=frame.f_code for key, value, in flocals: if key in fcode.co_names or key in fcode.co_varnames or key in fcode.co_cellvars: local_res=" %20s = " % key try: local_res += repr(value) except: try: local_res += str(value) except: local_res += "" res.append(local_res+"\n") res.append('-'*50 + '\n') res.append(" <%s> RAISED : %s\n" % (str(ex_type), str(ex_value))) res.append('-'*50+'\n') return res except: return ['-'*50+"\nError building extended traceback!!! :\n", ''.join(traceback.format_exception(* sys.exc_info() ) ) + '-'*50 + '\n', 'Original Exception follows:\n', ''.join(traceback.format_exception(ex_type, ex_value, tb)) ] else: # default traceback format. return traceback.format_exception(ex_type, ex_value, tb) Pyro-3.14/Pyro/util2.py0000644000076500000240000000150311537206303015170 0ustar irmenstaff00000000000000############################################################################# # # Pyro Utilities (part 2, to avoid circular dependencies) # User code should never import this, always use Pyro.util! # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# _supports_mt=None _supports_comp=None def supports_multithreading(): global _supports_mt if _supports_mt is None: try: from threading import Thread, Lock _supports_mt=1 except: _supports_mt=0 return _supports_mt def supports_compression(): global _supports_comp if _supports_comp is None: try: import zlib _supports_comp=1 except: _supports_comp=0 return _supports_comp if supports_multithreading(): import threading Pyro-3.14/Pyro/wxnsc.py0000644000076500000240000007566611537206303015320 0ustar irmenstaff00000000000000#!/usr/bin/env python """ A wxPython gui to nsc (Pyro Name Server Control tool). This gui doesn't have as many features as the xnsc that ships with Pyro, but it has some nice features that the xnsc doesn't have ;) 'Pyro' - Python Remote Objects is (c) Irmen de Jong - irmen@razorvine.net This file 'wxnsc.py' is (c) Jan Finell - finell@users.sourceforge.net Usage (from the commandline): # to use set PYRO environment variables or broadcasting # for finding the nameserver host... > wxnsc.py """ __author__ = "Jan Finell" __date__ = "$Date: 2009/03/27 14:30:29 $" __revision__ = "$Revision: 1.10.2.6 $" # # Standard modules # import os, sys, socket import traceback, cStringIO # # GUI modules # import wx # # Pyro modules # from Pyro.naming import NameServerLocator from Pyro.errors import NamingError, ConnectionClosedError,\ ConnectionDeniedError import Pyro.core #----------------------------------------------------------------------# # Module constants DEFAULT_GROUPNAME = ':Default' PROTECTED_GROUPS = [DEFAULT_GROUPNAME, ':Pyro'] GROUP_XPM = [ "16 16 9 1", " c None", ". c #FFFFFF", "+ c #000000", "@ c #C3C3C3", "# c #A0A0A0", "$ c #8080FF", "% c #585858", "& c #FFFFFF", "* c #808080", " +%+ ", " +%$$++ ", " +&%%$$+++++ ", " +&&@%%$$$$$+ ", " +&@&@@%$$$$% ", " +@&@@@@%%%%$+ ", " +&@@@@@@@#@+$% ", " +@@@@@@@#@#+$% ", " +@@@#@@#@##+$% ", " +@@@@@#@###+$% ", " ++*@@#@####+$% ", " ++*@#####+$% ", " ++#####+$% ", " ++###+$++", " ++#+$++", " ++++ "] GROUP_OPEN_XPM =[ "16 16 12 1", " c None", ". c #FFFFFF", "+ c #000000", "@ c #8080FF", "# c #808080", "$ c #C3C3C3", "% c #C0C0FF", "& c #A0A0A0", "* c #303030", "= c #FFFFFF", "- c #DCDCDC", "; c #585858", " +++ ", " +@#++ ", " +@%@#++++ ", "+++ +@%%%%%%@+ ", "+&&*;@@@%%%%%@+ ", "+#&&&#@@@@%%%@#+", " *&&&&##@@@@%@#+", " +#&&$$&##@@@@#+", " *&$&$$$$##@@#+", " +#$$$$-$-&@@#+", " *#-$$$-=%;@#+", " +*&--===&@#+", " +*$===%;#+", " +*$==##+", " +*$=#+", " +**+"] ITEM_XPM = [ "16 16 11 1", " c None", ". c #FFFFFF", "+ c #FFFFFF", "@ c #000000", "# c #585858", "$ c #DCDCDC", "% c #FFFFC0", "& c #FFDCA8", "* c #303030", "= c #C3C3C3", "- c #A0A0A0", " #####**@@ ", " #$$$$$=*=@ ", " #$+++++#+$@ ", " #$+++++#$+=@", " #$+++++###*@", " #$++++++$=-@", " #$++++++%+$@", " #$+++++++++@", " #$++++%+%+%@", " #$+++++++&+@", " #$++%+%+%+%@", " #$+++++&+&+@", " #$%+%+%+%+%@", " #$+++&+&+&%@", " #$%+%+%+%%%@", " *@@@@@@@@@@@"] #----------------------------------------------------------------------# # Helper functions def cmp_name(n1, n2): return cmp(n1[0], n2[0]) def show_message_dialog(parent, msg, title, style): if sys.platform[:3] == 'win': dlg = WinMessageDialog(parent, msg, title, style) else: dlg = wx.MessageDialog(parent, msg, title, style) dlg.CentreOnParent() retval = dlg.ShowModal() dlg.Destroy() return retval #----------------------------------------------------------------------# ## Classes class wx_StdoutLog(wx.TextCtrl): """ :Purpose: A simple text ctrl that can be used for logging standard out """ def write(self, data): if data.strip(): wx.TextCtrl.AppendText(self, '%s\n' % data) class wx_NSC(wx.Frame): """ :Purpose: The main frame of the GUI. """ def __init__(self, nsHost, nsPort, bcAddr): """ :Parameters: - `nsHost`: the name server host to connect to. This is the name of the host or the ip. - `nsPort`: the name server port. By default the Pyro name server port is 9090 - `bcAddr`: override for the broadcast address. """ wx.Frame.__init__(self, None, -1, 'Pyro Name Server') self.nsHost = nsHost self.nsPort = nsPort self.bcAddr = bcAddr self.NS = None self._build() imageList = wx.ImageList(16,16) self.__idGroup = imageList.Add(wx.BitmapFromXPMData(GROUP_XPM)) self.__idItem = imageList.Add(wx.BitmapFromXPMData(ITEM_XPM)) self.__idGroupOpen = imageList.Add(wx.BitmapFromXPMData(GROUP_OPEN_XPM)) self.treeCtrlItems.SetImageList(imageList) self.__imageList = imageList self._bindEvents() # binding stdout to my own txtCtrl Log. sys.stdout = self.txtCtrlLog self._log("Pyro version: "+Pyro.constants.VERSION) self.nsc_findNS() if self.NS: self.update() #-- public methods --# def enable(self, enable=True): """ Enabling/disabling some of the buttons. """ self.buttonDeleteGroup.Enable(enable) self.buttonCreateGroup.Enable(enable) self.buttonDeleteSelected.Enable(enable) self.buttonRegisterItem.Enable(enable) self.buttonShowMeta.Enable(enable) self.buttonSetMeta.Enable(enable) def update(self): """ """ tree = self.treeCtrlItems tree.DeleteAllItems() root = tree.AddRoot(':') tree.SetItemImage(root, self.__idGroup) tree.SetItemImage(root, self.__idGroupOpen, wx.TreeItemIcon_Expanded) self._populate_tree(tree, root, ':') # enabling/disabling buttons, depending on the current state. self.enable(self.NS != None) def _populate_tree(self, tree, parent, group): subgroupsL = self.nsc_list_groups(group) subgroupsL.sort() itemsL = self.nsc_list_items(group) itemsL.sort() for subgroup in subgroupsL: groupB = tree.AppendItem(parent, subgroup) tree.SetPyData(groupB, 0) tree.SetItemImage(groupB, self.__idGroup) tree.SetItemImage(groupB, self.__idGroupOpen, wx.TreeItemIcon_Expanded) self._populate_tree(tree, groupB, subgroup) for item in itemsL: itemB = tree.AppendItem(parent, item) tree.SetPyData(itemB, 1) tree.SetItemImage(itemB, self.__idItem) tree.SetItemImage(itemB, self.__idItem, wx.TreeItemIcon_Selected) #-- nsc methods --# def nsc_findNS(self, ident=None): """ Locating the Name Server by using given nsHost and nsPort """ locator = NameServerLocator(identification=ident) try: if self.nsHost: self._log('connecting to Name Server (%s:%s)' % (self.nsHost, self.nsPort)) self.NS = locator.getNS(self.nsHost, self.nsPort, trace=1, bcaddr=self.bcAddr) else: self._log('broadcasting to find Name Server') self.NS = locator.getNS(None, None, trace = 1, bcaddr=self.bcAddr) self.nsHost = self.NS.URI.address self.nsPort = self.NS.URI.port self.NS._setIdentification(ident) self._log('Name Server found, URI = %s' % self.NS.URI) self._setNSData() except ConnectionDeniedError, e: if str(e).find( Pyro.constants.deniedReasons[Pyro.constants.DENIED_SECURITY] ) != -1: msg = 'Authentication required:' dlg = wx.TextEntryDialog(self, msg, 'Authentication', style=wx.OK|wx.CANCEL|wx.TE_PASSWORD) dlg.CentreOnParent() if dlg.ShowModal() == wx.ID_OK: ident = dlg.GetValue() self.nsc_findNS(ident) else: self.NS = None self._log('Connection to Name Server denied!','error') else: self.NS = None self._logError('Unable to connect to Name Server') except: self.NS = None self._logError('Name Server not found!') def nsc_list_groups(self, ingroup): """ Returns a list of group names inside given group. """ return self._nsc_list(ingroup, 0) def nsc_list_items(self, ingroup): """ Returns a list of item names inside given group. """ return self._nsc_list(ingroup, 1) def _nsc_list(self, ingroup, type): """ Generic method for listing either groups or items inside a given group. type = 0 : group type = 1 : item """ items = [] if self.NS: for name, t in self.NS.list(ingroup): if t == type: if type == 1: uri = self.NS.resolve('%s.%s' % (ingroup,name)) name = '%s (%s)' % (name, uri) elif ingroup != ':': name = '%s.%s' % (ingroup, name) else: name = '%s%s' % (ingroup, name) items.append(name) return items def nsc_create_group(self, groupName): """ Creating given group """ if self.NS: try: self.NS.createGroup(groupName) self._log('created group (%s)' % (groupName)) return 1 except NamingError, e: self._logError('unable to create group %s because %s' % (groupName, e)) return 0 def nsc_delete_group(self, groupName): """ Deleting given group """ if self.NS: try: self.NS.deleteGroup(groupName) self._log('group %s deleted' % groupName) return 1 except NamingError, e: self._logError('unable to delete group %s because %s' % (groupName, e)) return 0 def nsc_ping(self): """ Ping the current Name Server """ if self.NS: try: self.NS.ping() self._log('ping Name Server (%s): up and running' % self.nsHost) except: self._logError('Name Server not responding.') else: self._logError('Name Server not responding') def nsc_delete(self, name): """Removing given name from the Name Server. :Parameters: - `name`: the name to delete from the Name Server """ try: self.NS.unregister(name) self._log('%s deleted successfully' % name) return 1 except NamingError, e: self._logError('unable to delete %s because %s' % (name, e)) except: self._logError('deletion of %s failed' % name) return 0 def nsc_register_item(self, name, uri): """ Registering new item with given name and uri """ try: self.NS.register(name, uri) uri = Pyro.core.PyroURI(uri) self._log('registered %s with %s' % (name, uri)) return 1 except NamingError, e: self._logError('unable to register,\nName Server error: %s' % e) except Exception, e: self._logError('unable to register, error: %s' % e) return 0 def nsc_set_meta(self, name, meta): """ Set user meta data """ try: self.NS.setMeta(name, meta) self._log('set user meta data on '+name) return 1 except NamingError, e: self._logError('unable to set user meta data,\nName Server error: %s' % e) except Exception, e: self._logError('unable to set user meta data, error: %s' % e) return 0 def nsc_show_meta(self, name): fullName = self.NS.fullName(name) try: self._log('"%s" system meta info: %s' % (fullName, self.NS._getSystemMeta(fullName))) self._log('"%s" user meta info: %s' % (fullName, self.NS.getMeta(name))) except NamingError, e: self._logError('unable to get meta info,\nName Server error: %s' % e) except Exception, e: self._logError('unable to get meta info, error: %s' % e) #-- gui event methods --# def OnCheckNS(self, event): if self._checkNS(): self.update() def OnClose(self, event): sys.stdout = sys.__stdout__ #restoring the stdout self.Destroy() def OnCreateGroup(self, event): """ Creating group in selected parent """ tree = self.treeCtrlItems items = tree.GetSelections() if items: if tree.GetPyData(items[0]) == 0: # use the selected group parentGroupI = items[0] parentGroupName = tree.GetItemText(parentGroupI) else: # take the parent parentGroupI = tree.GetItemParent(items[0]) parentGroupName = tree.GetItemText(parentGroupI) else: parentGroupI = tree.GetRootItem() parentGroupName = ':' msg = 'Create group in "%s", with name:' % parentGroupName dlg = wx.TextEntryDialog(self, msg, 'Enter group name') dlg.CentreOnParent() if dlg.ShowModal() == wx.ID_OK: if parentGroupName != ':': groupName = '%s.%s' % (parentGroupName, dlg.GetValue()) else: groupName = ':%s' % (dlg.GetValue()) if self.nsc_create_group(groupName): groupI = tree.AppendItem(parentGroupI, groupName) tree.SetPyData(groupI, 0) tree.SetItemImage(groupI, self.__idGroup) tree.SetItemImage(groupI, self.__idGroupOpen, wx.TreeItemIcon_Expanded) tree.Expand(parentGroupI) def OnSetMeta(self, ev=None): """ set user meta on selected groups + items """ tree = self.treeCtrlItems itemsL, groupsL = self._getSelections() namesL = itemsL + groupsL if namesL: namesS = ',\n '.join(namesL) msg = 'User meta data string for:\n %s' % namesS dlg = wx.TextEntryDialog(self, msg, 'Enter meta data') dlg.CentreOnParent() if dlg.ShowModal() == wx.ID_OK: meta=dlg.GetValue() for name in namesL: self.nsc_set_meta(name,meta) def OnDelete(self, ev=None): """ Deleting selected items. """ tree = self.treeCtrlItems itemsL = tree.GetSelections() namesL = [] deleteL = [] for i in itemsL: # only items (ie. no groups) if tree.GetPyData(i) == 1: parent = tree.GetItemParent(i) parentName = tree.GetItemText(parent) name = tree.GetItemText(i).split()[0] #only name namesL.append('%s.%s' % (parentName, name)) deleteL.append(i) if namesL: namesS = ',\n '.join(namesL) ret = show_message_dialog(self, 'Really delete following name(s)?:\n %s' % namesS, '-- Confirm --', wx.YES|wx.NO|wx.ICON_QUESTION) if ret == wx.ID_YES: for name, i in zip(namesL,deleteL): if self.nsc_delete(name): tree.Delete(i) def OnDeleteGroup(self, ev=None): """ Deleting selected groups. """ tree = self.treeCtrlItems itemsL = tree.GetSelections() namesL = [] deleteL = [] for i in itemsL: # only groups (ie. no items) if tree.GetPyData(i) == 0: name = tree.GetItemText(i) if name not in PROTECTED_GROUPS and tree.GetChildrenCount(i)==0: namesL.append(name) deleteL.append(i) if namesL: namesS = ',\n'.join(namesL) ret = show_message_dialog(self, 'Really delete following group(s)?:\n %s' % namesS, '-- Confirm --', wx.YES|wx.NO|wx.ICON_QUESTION) if ret == wx.ID_YES: for name, i in zip(namesL, deleteL): if self.nsc_delete_group(name): tree.Delete(i) def OnKeyPressed(self, event): """ Calling delete for both items and groups """ if event.GetKeyCode() == 127: # deleting both selected groups and items self.OnDelete() self.OnDeleteGroup() if event.GetKeyCode() == 105: # 105 == 'i' # showing meta information on selected item self.OnShowMeta() event.Skip() def OnPing(self, event): if self._checkNS(): self.update() self.nsc_ping() def OnRegisterItem(self, event): """ Registering item in selected parent. """ tree = self.treeCtrlItems items = tree.GetSelections() if items: if tree.GetPyData(items[0]) == 0: # use the selected group parentGroupI = items[0] parentGroupName = tree.GetItemText(parentGroupI) else: parentGroupI = tree.GetItemParent(items[0]) parentGroupName = tree.GetItemText(parentGroupI) else: parentGroupI = tree.GetRootItem() parentGroupName = ':' msg = 'Register new item in "%s", with:\n ' % parentGroupName dlg = wx.TextEntryDialog(self, msg, 'Register item') dlg.CentreOnParent() if dlg.ShowModal() == wx.ID_OK: try: itemName, uri = dlg.GetValue().split() except: self._log('Invalid arguments, use ', 'error') else: if parentGroupName != ':': itemName = '%s.%s' % (parentGroupName, itemName) else: itemName = ':%s' % (itemName) if self.nsc_register_item(itemName, uri): label = '%s (%s)' % (dlg.GetValue().split()[0], uri) itemI = tree.AppendItem(parentGroupI, label) tree.SetPyData(itemI, 1) tree.SetItemImage(itemI, self.__idItem) tree.SetItemImage(itemI, self.__idItem, wx.TreeItemIcon_Selected) tree.Expand(parentGroupI) def OnUpdate(self, event): self._checkNS() self.update() def OnShowMeta(self, event=None): itemsL, groupsL = self._getSelections() for name in itemsL + groupsL: self.nsc_show_meta(name) #-- protected methods --# def _checkNS(self): """ Reads the new values from the txtCtrlNSHost and txtCtrlNSPort. If changed, it tries to connect to the new Name Server. """ changed = 0 if self.txtCtrlNSHost.IsModified(): self.nsHost = self.txtCtrlNSHost.GetValue() changed = 1 if self.txtCtrlNSPort.IsModified(): try: port = int(self.txtCtrlNSPort.GetValue()) self.nsPort = port except ValueError: self._logError('Integer required for port') changed = 1 if changed: self.nsc_findNS() return changed def _log(self, line, status='info'): """Writing given line to the log-textCtrl. :Parameters: - `line`: text to log - `status`: status should be 'info' or 'error'. If 'info' the text will be colored blue, if 'error' the text will be red. """ start = self.txtCtrlLog.GetLastPosition() self.txtCtrlLog.AppendText('%s\n' % line) color = wx.BLACK if status == 'info': color = wx.BLUE elif status == 'error': color = wx.RED self.txtCtrlLog.SetStyle(start, self.txtCtrlLog.GetLastPosition(), wx.TextAttr(color)) def _logError(self, line): """ Getting the traceback of previous error, and logging this. """ a, b, tb = sys.exc_info() if a == ConnectionClosedError: self.NS = None self._log('Connection with Name Server lost', 'error') self.enable(False) buf = cStringIO.StringIO() traceback.print_exc(file = buf) self._log('%s:\n%s' % (line, buf.getvalue()), 'error') def _setNSData(self): """ Updates the display of current Name Server information. """ try: ns_name, t, ns_ip = socket.gethostbyaddr(self.nsHost) ns_ip = ns_ip[0] except: ns_name, ns_ip = self.nsHost, '' self.txtCtrlNSHost.SetValue('%s' % ns_name) self.txtCtrlNSPort.SetValue('%s' % self.nsPort) self.SetTitle('Pyro Name Server ( %s - %s )' % (ns_name, ns_ip)) def _getSelections(self): tree = self.treeCtrlItems selectionsL = tree.GetSelections() itemsL = [] groupsL = [] for i in selectionsL: if tree.GetPyData(i) == 0: # group groupsL.append(tree.GetItemText(i)) elif tree.GetPyData(i) == 1: # item parentName = tree.GetItemText(tree.GetItemParent(i)) name = tree.GetItemText(i).split()[0] itemsL.append('%s.%s' % (parentName, name)) return itemsL, groupsL #-- build / bind methods --# def _bindEvents(self): """ Binding events to the gui widgets. """ wx.EVT_BUTTON(self, self.buttonPing.GetId(), self.OnPing) wx.EVT_BUTTON(self, self.buttonUpdate.GetId(), self.OnUpdate) wx.EVT_BUTTON(self, self.buttonClose.GetId(), self.OnClose) wx.EVT_BUTTON(self, self.buttonDeleteGroup.GetId(), self.OnDeleteGroup) wx.EVT_BUTTON(self, self.buttonCreateGroup.GetId(), self.OnCreateGroup) wx.EVT_BUTTON(self, self.buttonDeleteSelected.GetId(), self.OnDelete) wx.EVT_BUTTON(self, self.buttonRegisterItem.GetId(), self.OnRegisterItem) wx.EVT_BUTTON(self, self.buttonShowMeta.GetId(), self.OnShowMeta) wx.EVT_BUTTON(self, self.buttonSetMeta.GetId(), self.OnSetMeta) wx.EVT_TEXT_ENTER(self, self.txtCtrlNSHost.GetId(), self.OnCheckNS) wx.EVT_TEXT_ENTER(self, self.txtCtrlNSPort.GetId(), self.OnCheckNS) wx.EVT_CHAR(self.treeCtrlItems, self.OnKeyPressed) def _build(self): """ Building widgets and setting static widget data. """ parent = wx.Panel(self, -1) sizer0 = wx.BoxSizer(wx.VERTICAL) sizer0.Add(self._buildTopBar(parent), 0, wx.ALIGN_LEFT|wx.GROW, 5) splitter = wx.SplitterWindow(parent, -1) #- TOP PART --------------------------------------------------------# topParent = wx.Panel(splitter, -1) topSizer = wx.BoxSizer(wx.VERTICAL) self.treeCtrlItems = wx.TreeCtrl(topParent, -1, style = wx.TR_TWIST_BUTTONS|wx.TR_LINES_AT_ROOT|wx.TR_HAS_BUTTONS|wx.TR_HIDE_ROOT|wx.TR_MULTIPLE) topSizer.Add(self.treeCtrlItems, 1, wx.EXPAND, 5) topParent.SetAutoLayout( True ) topParent.SetSizer(topSizer ) topSizer.Fit(topParent) topSizer.SetSizeHints(topParent) #-------------------------------------------------------------------# #- BOTTOM PART -----------------------------------------------------# bottomParent = wx.Panel(splitter,-1) bottomSizer = wx.BoxSizer(wx.VERTICAL) self.txtCtrlLog=wx_StdoutLog(bottomParent, -1, "", size= wx.Size(-1, 10), style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH) bottomSizer.Add(self.txtCtrlLog, 1, wx.EXPAND, 5) bottomParent.SetAutoLayout( True ) bottomParent.SetSizer(bottomSizer ) bottomSizer.Fit(bottomParent) bottomSizer.SetSizeHints(bottomParent) #-------------------------------------------------------------------# splitter.SplitHorizontally(topParent,bottomParent, -100) sizer0.Add(splitter, 1, wx.EXPAND|wx.ALIGN_CENTRE, 5) self.buttonClose = wx.Button(parent, -1, 'Close') # buttonClose sizer0.Add(self.buttonClose, 0, wx.ALIGN_CENTRE|wx.ALL, 5) parent.SetAutoLayout( True ) parent.SetSizer( sizer0 ) sizer0.Fit( parent) sizer0.SetSizeHints( parent) def _buildTopBar(self, parent): """ Widget building """ sizer0 = wx.BoxSizer(wx.VERTICAL) #-- sizer1 = wx.BoxSizer(wx.HORIZONTAL) txt1 = wx.StaticText(parent, -1, 'Name Server:') txt1.SetForegroundColour(wx.BLUE) sizer1.Add(txt1, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE|wx.ALL, 5) self.txtCtrlNSHost = wx.TextCtrl(parent, -1, '', size=wx.Size(300,-1), style=wx.TE_PROCESS_ENTER) sizer1.Add(self.txtCtrlNSHost, 0, wx.ALIGN_LEFT|wx.ALIGN_BOTTOM|wx.TOP|wx.BOTTOM, 5) txtColon = wx.StaticText(parent, -1, ':') txtColon.SetForegroundColour(wx.BLUE) sizer1.Add(txtColon, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE|wx.TOP|wx.BOTTOM, 5) self.txtCtrlNSPort = wx.TextCtrl(parent, -1, '', size=wx.Size(50,-1), style=wx.TE_PROCESS_ENTER) sizer1.Add(self.txtCtrlNSPort, 0, wx.ALIGN_LEFT|wx.ALIGN_BOTTOM|wx.TOP|wx.BOTTOM, 5) self.buttonUpdate = wx.Button(parent, -1, 'Update') # buttonUpdate sizer1.Add(self.buttonUpdate, 0, wx.ALIGN_LEFT|wx.ALL, 5) self.buttonPing = wx.Button(parent, -1, 'Ping') # buttonPing sizer1.Add(self.buttonPing, 0, wx.ALIGN_LEFT|wx.ALL, 5) sizer0.Add(sizer1, 0, wx.ALIGN_LEFT|wx.GROW, 5) #-- lineH1 = wx.StaticLine(parent, -1, style=wx.LI_HORIZONTAL) sizer0.Add(lineH1, 0, wx.GROW|wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT, 5) #-- sizer2 = wx.BoxSizer(wx.HORIZONTAL) self.buttonDeleteGroup = wx.Button(parent, -1, ' Delete group(s) ') sizer2.Add(self.buttonDeleteGroup, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.TOP|wx.LEFT|wx.BOTTOM, 5) self.buttonCreateGroup = wx.Button(parent, -1, ' Create group... ') sizer2.Add(self.buttonCreateGroup, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.TOP|wx.LEFT|wx.BOTTOM, 5) lineV1 = wx.StaticLine(parent, -1, style=wx.LI_VERTICAL) sizer2.Add(lineV1, 0, wx.ALL|wx.GROW, 5) self.buttonDeleteSelected = wx.Button(parent, -1, ' Delete item(s) ') sizer2.Add(self.buttonDeleteSelected, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.ALL, 5) self.buttonRegisterItem = wx.Button(parent, -1, ' Register item... ') sizer2.Add(self.buttonRegisterItem, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, 5) lineV2 = wx.StaticLine(parent, -1, style=wx.LI_VERTICAL) sizer2.Add(lineV2, 0, wx.ALL|wx.GROW, 5) self.buttonShowMeta = wx.Button(parent, -1, ' Show meta ') sizer2.Add(self.buttonShowMeta, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, 5) self.buttonSetMeta = wx.Button(parent, -1, ' Set meta ') sizer2.Add(self.buttonSetMeta, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, 5) sizer0.Add(sizer2, 0, wx.ALIGN_LEFT, 5) #-- return sizer0 #----------------------------------------------------------------------# class WinMessageDialog(wx.Dialog): ''' :Purpose: Message dialog for MS Win. The parameters are the same as for wx.MessageDialog :Detail: On Windows the native wx.MessageDialog can not be centered on top of the parent or positioned, ie. it will always be centered on the screen. ''' def __init__(self, parent=None, message='Message:', caption='Message', style=wx.OK|wx.CANCEL,pos=wx.DefaultPosition): wx.Dialog.__init__(self, parent, -1, caption, size=wx.DefaultSize, style=wx.CAPTION, pos=pos) self._build(message, style) self.Fit() def OnButton(self, ev): self.EndModal(ev.GetId()) def _build(self, msg, style): parent = wx.Panel(self, -1) sizer = wx.BoxSizer(wx.VERTICAL) #-- icon and message --# msgSizer = wx.BoxSizer(wx.HORIZONTAL) # icon # artID = None if style & wx.ICON_EXCLAMATION == wx.ICON_EXCLAMATION \ or style & wx.ICON_HAND == wx.ICON_HAND: artID = wx.ART_WARNING elif style & wx.ICON_ERROR == wx.ICON_ERROR: artID = wx.ART_ERROR elif style & wx.ICON_QUESTION == wx.ICON_QUESTION: artID = wx.ART_QUESTION elif style & wx.ICON_INFORMATION == wx.ICON_INFORMATION: artID = wx.ART_INFORMATION if artID: bmp = wx.ArtProvider_GetBitmap(artID, wx.ART_MESSAGE_BOX, (48,48)) bmpIcon = wx.StaticBitmap(parent, -1, bmp) msgSizer.Add(bmpIcon, 0, wx.ALIGN_CENTRE|wx.ALL, 5) # msg # txtMsg = wx.StaticText(parent, -1, msg, style=wx.ALIGN_CENTRE) msgSizer.Add(txtMsg, 0, wx.ALIGN_CENTRE|wx.ALL, 5) sizer.Add(msgSizer, 0, wx.ALIGN_CENTRE, 5) line = wx.StaticLine(parent, -1, style=wx.LI_HORIZONTAL) sizer.Add(line, 0, wx.GROW|wx.ALL, 5) #-- buttons --# btnSizer = wx.BoxSizer(wx.HORIZONTAL) if style & wx.YES_NO == wx.YES_NO: btnYes = wx.Button(parent, wx.ID_YES, 'Yes') btnSizer.Add(btnYes, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT, 10) btnNo = wx.Button(parent, wx.ID_NO, 'No') btnSizer.Add(btnNo, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT, 10) if style & wx.YES_DEFAULT == wx.YES_DEFAULT: btnYes.SetDefault() elif style & wx.NO_DEFAULT == wx.NO_DEFAULT: btnNo.SetDefault() wx.EVT_BUTTON(self, wx.ID_YES, self.OnButton) wx.EVT_BUTTON(self, wx.ID_NO, self.OnButton) else: if style & wx.OK == wx.OK: btnOK = wx.Button(parent, wx.ID_OK, 'OK') btnOK.SetDefault() btnSizer.Add(btnOK, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT, 10) if style & wx.CANCEL == wx.CANCEL: btnCancel = wx.Button(parent, wx.ID_CANCEL, 'Cancel') btnSizer.Add(btnCancel, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT, 10) sizer.Add(btnSizer, 0, wx.ALIGN_CENTRE|wx.TOP, 5) #-- parent.SetAutoLayout( True ) parent.SetSizer(sizer ) sizer.Fit( parent ) sizer.SetSizeHints( parent ) #----------------------------------------------------------------------# def main(argv): """ The default host will be None if the environment variable PYRO_NS_HOSTNAME is not set. The default port will be 9090 (Pyro.config.PYRO_NS_PORT) if PYRO_NS_BC_PORT environment variable is not set. """ nsHost = os.getenv('PYRO_NS_HOSTNAME') nsPort = os.getenv('PYRO_NS_BC_PORT') or Pyro.config.PYRO_NS_PORT bcAddr = Pyro.config.PYRO_NS_BC_ADDR if bcAddr: bcAddr=bcAddr.strip() bcAddr=bcAddr or None class wx_NSCApp(wx.App): def OnInit(self): Pyro.core.initClient() frame = wx_NSC(nsHost, nsPort, bcAddr) frame.SetSize(wx.Size(630,500)) frame.Show(True) return True app = wx_NSCApp(0) app.MainLoop() # allow easy usage with python -m if __name__=="__main__": main(sys.argv) Pyro-3.14/Pyro/xnsc.py0000644000076500000240000002606211537206303015113 0ustar irmenstaff00000000000000############################################################################# # # Pyro Name Server Control Tool with GUI # # This is part of "Pyro" - Python Remote Objects # which is (c) Irmen de Jong - irmen@razorvine.net # ############################################################################# import sys, time from Tkinter import * from Pyro.naming import NameServerLocator from Pyro.errors import NamingError, ConnectionClosedError import Pyro.core class xnscFrame(object): def quit(self): self.master.quit() def clearOutput(self): self.text_out.delete('1.0',AtEnd()) self.outputln(time.asctime()) def output(self,txt): self.text_out.insert(AtEnd(),txt) self.text_out.yview(AtEnd()) def outputln(self,txt): self.output(txt+'\n') def b_clearoutput(self, event=None): self.clearOutput() def b_findNS(self,event=None): self.clearOutput() hst,prt = None,None self.authID = self.entry_AuthID.get() if event: # Pressed in entry box addr = self.entry_NSloc.get().split(':') hst=addr[0] if len(addr)>1: prt=int(addr[1]) # We need to keep the host/port for the shutdown button... self.NShost = hst self.NSport = prt self.outputln('*** finding NS') locator=NameServerLocator(identification=self.authID) bcaddr=self.entry_BCAddr.get().strip() or None try: self.NS=locator.getNS(hst,prt,trace=1,bcaddr=bcaddr) self.entry_NSloc.delete(0,AtEnd()) self.entry_NSloc.insert(AtEnd(),self.NS.URI.address+':'+str(self.NS.URI.port)) self.entry_AuthID.delete(0,AtEnd()) self.entry_AuthID.insert(AtEnd(),'****') self.enable_buttons() self.outputln(' found, URI='+str(self.NS.URI)) except: self.disable_buttons() self.outputln(' not found:'); a,b = sys.exc_info()[:2] self.outputln(' '+str(a)+' : '+str(b)) self.outputln('See standard output for trace messages.') def handle_comm_error(self,name): # Handle a communication error: disable buttons and print exception a,b = sys.exc_info()[:2] self.outputln('*** '+name+': exception occured:') self.outputln(' '+str(a)+' : '+str(b)) if a==ConnectionClosedError: self.disable_buttons() self.outputln('*** Connection with NS lost - reconnect') def printError(self, msg, exc): line="## %s: " % msg if isinstance(exc.args, (list, tuple)): line+="; ".join(exc.args[:-1]) else: line+=exc.args line+=" ##" self.outputln(line) def b_list(self,event=None): names = self.entry_arg.get().split() try: if names: self.outputln('*** List groups:') for n in names: self.output(' '+self.NS.fullName(n)+' --> ') try: self.printList(self.NS.list(n)) except NamingError,x: self.printError("can't list",x) else: self.outputln('*** List default group:') self.printList(self.NS.list(None)) except: self.handle_comm_error('list') def printList(self,lst): out='( ' lst.sort() for (n,t) in lst: if t==0: out+='['+n+'] ' elif t==1: out+=n+' ' self.outputln(out+')') def b_listall(self,event=None): try: flat=self.NS.flatlist() flat.sort() self.outputln('--------- Flat dump of namespace') for (name,val) in flat: self.outputln(' '+name+' --> '+str(val)) self.outputln('--------- End dump') except: self.handle_comm_error('listall') def b_register(self,event=None): self.outputln('*** registering with NS:') try: (name,uri) = self.entry_arg.get().split() try: self.NS.register(name,uri) uri=Pyro.core.PyroURI(uri) self.outputln(' '+name+' --> '+str(uri)) except NamingError,x: self.printError("Error from NS", x) except: self.handle_comm_error('register') except ValueError: self.outputln(' Invalid arguments, use " ".') def b_resolve(self,event=None): self.outputln('*** resolving:') name=self.entry_arg.get() if not name: self.outputln(' Invalid arguments, use "".') else: try: uri=self.NS.resolve(name) self.outputln(' '+name+' --> '+str(uri)) except NamingError,x: self.printError("can't resolve '"+name+"'", x) except: self.handle_comm_error('resolve') def b_remove(self,event=None): self.outputln('*** removing:') name=self.entry_arg.get() if not name: self.outputln(' Invalid arguments, use "".') else: try: self.NS.unregister(name) self.outputln('*** removed: '+name) except NamingError,x: self.printError("Can't remove '"+name+"'", x) except: self.handle_comm_error('remove') def b_ping(self,event=None): try: self.NS.ping() self.outputln('*** ping NS: up and running!') except: self.handle_comm_error('ping') def b_creategroup(self,event=None): name=self.entry_arg.get() if not name: self.outputln(' Invalid arguments, use "".') else: try: self.NS.createGroup(name) self.outputln('*** group created: '+name) except Exception,x: self.printError("Can't create group",x) def b_deletegroup(self,event=None): name=self.entry_arg.get() if not name: self.outputln(' Invalid arguments, use "".') else: try: self.NS.deleteGroup(name) self.outputln('*** group deleted: '+name) except Exception,x: self.printError("Can't delete group",x) def b_showmeta(self,event=None): name=self.NS.fullName(self.entry_arg.get()) self.outputln('*** showing meta info of: '+name) try: self.outputln("system meta info : "+str(self.NS._getSystemMeta(name))) self.outputln(" user meta info : "+str(self.NS.getMeta(name))) except NamingError,x: self.printError("Can't get Meta info",x) except: self.handle_comm_error('showmeta') def b_setmeta(self,event=None): self.outputln('*** setting user meta data:') try: (name,meta) = self.entry_arg.get().split(None,1) try: self.NS.setMeta(name,meta) self.outputln(' '+name+' META='+meta) except NamingError,x: self.printError("Error from NS", x) except: self.handle_comm_error('setmeta') except ValueError: self.outputln(' Invalid arguments, use " ".') def b_resync(self,event=None): self.outputln("*** resync NS with twin") try: self.NS.resync() except NamingError,x: self.printError("Can't resync",x) except: self.handle_comm_error('resync') def b_shutdown(self,event=None): locator = NameServerLocator(self.authID) try: result = locator.sendSysCommand('shutdown',self.NShost,self.NSport,0) self.outputln('*** The NS replied to the shutdown message: '+str(result)) except: self.disable_buttons() self.outputln(' not found:'); a,b = sys.exc_info()[:2] self.outputln(' '+str(a)+' : '+str(b)) def enable_buttons(self): self.enable_disable_buttons(NORMAL) def disable_buttons(self): self.enable_disable_buttons(DISABLED) def enable_disable_buttons(self,state): self.but_ping['state']=state self.but_list['state']=state self.but_listall['state']=state self.but_resolve['state']=state self.but_register['state']=state self.but_remove['state']=state self.but_shutdown['state']=state self.but_showmeta['state']=state self.but_setmeta['state']=state self.but_resync['state']=state self.but_creategroup['state']=state self.but_deletegroup['state']=state def createWidgets(self): frame_top = Frame(self.master,borderwidth=2,relief=GROOVE) frame_top1 = Frame(frame_top,borderwidth=0) Label(frame_top1,text='Name Server Location (host:port)').pack(side=LEFT,anchor=W) self.entry_NSloc=Entry(frame_top1) self.entry_NSloc.bind('',self.b_findNS) self.entry_NSloc.pack(expand=1,fill=X,side=LEFT) Label(frame_top1,text='(press enter)').pack(side=LEFT,anchor=W) frame_top1.pack(fill=X) frame_top2 = Frame(frame_top,borderwidth=0) frame_top3 = Frame(frame_top,borderwidth=0) Label(frame_top2,text='Authorization ID:').pack(side=LEFT,anchor=W) self.entry_AuthID=Entry(frame_top2) self.entry_AuthID.bind('',self.b_findNS) self.entry_AuthID.pack(expand=1,fill=X,side=LEFT) Label(frame_top3,text='Broadcast address:').pack(side=LEFT,anchor=W) self.entry_BCAddr=Entry(frame_top3) self.entry_BCAddr.pack(expand=1,fill=X,side=LEFT) self.but_findNS=Button(frame_top3,text='Auto Discover NS',command=self.b_findNS) self.QUIT=Button(frame_top3,text='QUIT',command=self.quit) self.QUIT.pack(side=RIGHT) self.but_findNS.pack(side=RIGHT) frame_top2.pack(fill=X) frame_top3.pack(fill=X) frame_top.pack(fill=X) frame_cmds=Frame(self.master) frame_cmds1=Frame(frame_cmds) frame_cmds2=Frame(frame_cmds) self.but_ping=Button(frame_cmds1,text='Ping',state=DISABLED,command=self.b_ping) self.but_list=Button(frame_cmds1,text='List',state=DISABLED,command=self.b_list) self.but_listall=Button(frame_cmds1,text='List All',state=DISABLED,command=self.b_listall) self.but_register=Button(frame_cmds2,text='Register',state=DISABLED,command=self.b_register) self.but_resolve=Button(frame_cmds1,text='Resolve',state=DISABLED,command=self.b_resolve) self.but_remove=Button(frame_cmds2,text='Remove',state=DISABLED,command=self.b_remove) self.but_creategroup=Button(frame_cmds2,text='Create Group',state=DISABLED,command=self.b_creategroup) self.but_deletegroup=Button(frame_cmds2,text='Delete Group',state=DISABLED,command=self.b_deletegroup) self.but_showmeta=Button(frame_cmds1,text='Show Meta',state=DISABLED,command=self.b_showmeta) self.but_setmeta=Button(frame_cmds1,text='Set Meta',state=DISABLED,command=self.b_setmeta) self.but_resync=Button(frame_cmds1,text='ReSync',state=DISABLED,command=self.b_resync) self.but_shutdown=Button(frame_cmds1,text='Shutdown',state=DISABLED,command=self.b_shutdown) self.but_clearoutput=Button(frame_cmds2,text='Clear output',command=self.b_clearoutput) Label(frame_cmds,text='NS commands:').pack(side=LEFT) self.but_ping.pack(side=LEFT) self.but_list.pack(side=LEFT) self.but_listall.pack(side=LEFT) self.but_register.pack(side=LEFT) self.but_resolve.pack(side=LEFT) self.but_remove.pack(side=LEFT) self.but_creategroup.pack(side=LEFT) self.but_deletegroup.pack(side=LEFT) self.but_showmeta.pack(side=LEFT) self.but_setmeta.pack(side=LEFT) self.but_resync.pack(side=LEFT) self.but_shutdown.pack(side=LEFT) self.but_clearoutput.pack(side=RIGHT) frame_args=Frame(self.master,borderwidth=2) self.entry_arg=Entry(frame_args) Label(frame_args,text='Command arguments').pack(side=LEFT) self.entry_arg.pack(expand=1,fill=X) frame_output=Frame(self.master) ys=Scrollbar(frame_output,orient=VERTICAL) self.text_out=Text(frame_output,yscrollcommand=ys.set,width=90,height=20) ys['command']=self.text_out.yview ys.pack(fill=Y,side=LEFT) self.text_out.pack(side=LEFT,expand=1,fill=BOTH) # pack root children: frame_cmds1.pack(fill=X) frame_cmds2.pack(fill=X) frame_cmds.pack(fill=X) frame_args.pack(fill=X) frame_output.pack(fill=BOTH,expand=1) def __init__(self, master=None): self.master = master self.createWidgets() def main(argv): Pyro.core.initClient() root=Tk() root.title('xnsc - Pyro Name Server control tool - Pyro version '+Pyro.constants.VERSION) app=xnscFrame(root) root.protocol('WM_DELETE_WINDOW',root.quit) root.mainloop() # allow easy usage with python -m if __name__=="__main__": import sys main(sys.argv) Pyro-3.14/Pyro.conf0000644000076500000240000000274411517657604014452 0ustar irmenstaff00000000000000# # External Pyro configuration file # # This is a skeleton config file, all items are empty. # Note that environment variable settings override the # settings in this config file. So the priority sequence is: # 1. overrides all: explicit setting inside your code # 2. environment variable # 3. this config file # 4. if not set otherwise: built-in default settings. # # For each empty (or unspecified) item the default is used. # Your own config file need only specify the items that # are different than the default. If you don't have a # config file, all items are set to their default value # (unless changed otherwise ofcourse). # # Please consult the manual for the meaning of the config items. # PYRO_STORAGE= PYRO_HOST= PYRO_PUBLISHHOST= PYRO_PORT= PYRO_PORT_RANGE= PYRO_NS_HOSTNAME= PYRO_NS_PORT= PYRO_NS_BC_ADDR= PYRO_NS_BC_PORT= PYRO_NS2_HOSTNAME= PYRO_NS2_PORT= PYRO_NS2_BC_ADDR= PYRO_NS2_BC_PORT= PYRO_NS_URIFILE= PYRO_NS_DEFAULTGROUP= PYRO_BC_RETRIES= PYRO_BC_TIMEOUT= PYRO_PICKLE_FORMAT= PYRO_XML_PICKLE= PYRO_GNOSIS_PARANOIA= PYRO_TRACELEVEL= PYRO_USER_TRACELEVEL= PYRO_LOGFILE= PYRO_USER_LOGFILE= PYRO_DETAILED_TRACEBACK= PYRO_STDLOGGING= PYRO_STDLOGGING_CFGFILE= PYRO_MAXCONNECTIONS= PYRO_TCP_LISTEN_BACKLOG= PYRO_BROKEN_MSGWAITALL= PYRO_MULTITHREADED= PYRO_COMPRESSION= PYRO_CHECKSUM= PYRO_SOCK_KEEPALIVE= PYRO_MOBILE_CODE= PYRO_DNS_URI= PYRO_ES_QUEUESIZE= PYRO_ES_BLOCKQUEUE= PYRO_ONEWAY_THREADED= PYROSSL_CERT= PYROSSL_KEY= PYROSSL_CA_CERT= PYROSSL_CERTDIR= PYROSSL_POSTCONNCHECK= Pyro-3.14/README.txt0000644000076500000240000000070511517657604014343 0ustar irmenstaff00000000000000Pyro - Python Remote Objects This software is copyright © by Irmen de Jong. This software is released under the MIT software license. This license, including disclaimer, is available in the 'LICENSE' file. Please give me credit if you use this software. The manual, including installation instructions, is in the docs/ directory. It's in HTML and the title section is called 'PyroManual.html'. Irmen de Jong irmen@razorvine.net Pyro-3.14/setup.cfg0000644000076500000240000000007311517657604014464 0ustar irmenstaff00000000000000[bdist_rpm] doc_files = LICENSE,docs [install] optimize=1 Pyro-3.14/setup.py0000644000076500000240000000356311564574070014361 0ustar irmenstaff00000000000000#!/usr/bin/env python # # Pyro setup script # from distutils.core import setup import sys,os,glob import sets if __name__ == '__main__' : scripts=sets.Set(glob.glob("bin/pyro-*.cmd")) if sys.platform != 'win32': scripts=sets.Set(glob.glob("bin/pyro-*")) - scripts # extract version string from Pyro/constants.py code=compile(open(os.path.join('Pyro','constants.py')).read(), "constants", "exec") constants={} exec code in constants version=constants["VERSION"] print 'Pyro Version =',version setup(name="Pyro", version= version, license="MIT", description = "distributed object middleware for Python (IPC/RPC), version 3.x", long_description = """Pyro stands for PYthon Remote Objects. It is an advanced and powerful Distributed Object Technology system written entirely in Python, that is designed to be very easy to use. This is version 3.x of Pyro, the stable version. For a more modern version with new features, look at Pyro4 instead.""", author = "Irmen de Jong", author_email="irmen@razorvine.net", keywords="distributed objects, middleware, network communication, DOT, RMI, IPC", url = "http://www.xs4all.nl/~irmen/pyro3/", packages=['Pyro','Pyro.EventService','Pyro.ext','Pyro.test'], scripts = list(scripts), platforms="any", classifiers=[ "Development Status :: 5 - Production/Stable", "Development Status :: 6 - Mature", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Topic :: Software Development :: Object Brokering", "Topic :: System :: Distributed Computing", "Topic :: System :: Networking" ] )