Pyro-3.14/ 0000755 0000765 0000024 00000000000 11567263340 012636 5 ustar irmen staff 0000000 0000000 Pyro-3.14/bin/ 0000755 0000765 0000024 00000000000 11567263337 013414 5 ustar irmen staff 0000000 0000000 Pyro-3.14/bin/pyro-es 0000644 0000765 0000024 00000000141 11517657576 014740 0 ustar irmen staff 0000000 0000000 #!/usr/bin/env python import sys from Pyro.EventService import Server Server.start(sys.argv[1:]) Pyro-3.14/bin/pyro-es.cmd 0000644 0000765 0000024 00000000152 11517657576 015504 0 ustar irmen staff 0000000 0000000 @echo off python -tt -c "from Pyro.EventService import Server; import sys; Server.start(sys.argv[1:])" %* Pyro-3.14/bin/pyro-esd 0000644 0000765 0000024 00000001422 11517657576 015107 0 ustar irmen staff 0000000 0000000 #!/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.cmd 0000644 0000765 0000024 00000000233 11517657576 016220 0 ustar irmen staff 0000000 0000000 @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-genguid 0000644 0000765 0000024 00000000140 11517657576 015752 0 ustar irmen staff 0000000 0000000 #!/usr/bin/env python import sys import Pyro.util Pyro.util.genguid_scripthelper(sys.argv[1:]) Pyro-3.14/bin/pyro-genguid.cmd 0000644 0000765 0000024 00000000140 11517657576 016514 0 ustar irmen staff 0000000 0000000 @echo off python -tt -c "import Pyro.util,sys; Pyro.util.genguid_scripthelper(sys.argv[1:])" %* Pyro-3.14/bin/pyro-ns 0000644 0000765 0000024 00000000124 11517657576 014752 0 ustar irmen staff 0000000 0000000 #!/usr/bin/env python import sys import Pyro.naming Pyro.naming.main(sys.argv[1:]) Pyro-3.14/bin/pyro-ns.cmd 0000644 0000765 0000024 00000000124 11517657576 015514 0 ustar irmen staff 0000000 0000000 @echo off python -tt -c "import Pyro.naming,sys; Pyro.naming.main(sys.argv[1:])" %* Pyro-3.14/bin/pyro-nsc 0000644 0000765 0000024 00000000116 11517657576 015116 0 ustar irmen staff 0000000 0000000 #!/usr/bin/env python import sys import Pyro.nsc Pyro.nsc.main(sys.argv[1:]) Pyro-3.14/bin/pyro-nsc.cmd 0000644 0000765 0000024 00000000116 11517657576 015660 0 ustar irmen staff 0000000 0000000 @echo off python -tt -c "import Pyro.nsc,sys; Pyro.nsc.main(sys.argv[1:])" %* Pyro-3.14/bin/pyro-nsd 0000644 0000765 0000024 00000001403 11517657576 015117 0 ustar irmen staff 0000000 0000000 #!/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.cmd 0000644 0000765 0000024 00000000233 11517657576 016231 0 ustar irmen staff 0000000 0000000 @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-wxnsc 0000644 0000765 0000024 00000000122 11517657576 015472 0 ustar irmen staff 0000000 0000000 #!/usr/bin/env python import sys import Pyro.wxnsc Pyro.wxnsc.main(sys.argv[1:]) Pyro-3.14/bin/pyro-wxnsc.cmd 0000644 0000765 0000024 00000000122 11517657576 016234 0 ustar irmen staff 0000000 0000000 @echo off python -tt -c "import Pyro.wxnsc,sys; Pyro.wxnsc.main(sys.argv[1:])" %* Pyro-3.14/bin/pyro-xnsc 0000644 0000765 0000024 00000000120 11517657576 015301 0 ustar irmen staff 0000000 0000000 #!/usr/bin/env python import sys import Pyro.xnsc Pyro.xnsc.main(sys.argv[1:]) Pyro-3.14/bin/pyro-xnsc.cmd 0000644 0000765 0000024 00000000120 11517657576 016043 0 ustar irmen staff 0000000 0000000 @echo off python -tt -c "import Pyro.xnsc,sys; Pyro.xnsc.main(sys.argv[1:])" %* Pyro-3.14/docs/ 0000755 0000765 0000024 00000000000 11567263337 013574 5 ustar irmen staff 0000000 0000000 Pyro-3.14/docs/1-intro.html 0000644 0000765 0000024 00000021213 11556576221 015747 0 ustar irmen staff 0000000 0000000
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
*args
and **keywords
).To get an idea of how Pyro works, here is a scenario:
test
' containing a class 'testclass
', which will be accessed
remotely.testclass
', and registers them with the Pyro Name
Server.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.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:
pyro-xnsc
tool requires the Tkinter Python extension module, with Tcl/Tk installed.pyro-wxnsc
tool requires the WxPython extension module with WxWindows.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.html 0000644 0000765 0000024 00000055626 11552620353 016217 0 ustar irmen staff 0000000 0000000The 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:
util.getPyroTraceback
function for instance.PYRO_DETAILED_TRACEBACK
config item. This one is disabled
by default because of security reasons.OSError
-- is raised when Pyro needs but can't create or open the
storage directory specified by PYRO_STORAGE
.
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.
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.
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) |
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).
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. |
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:
daemon=Pyro.core.Daemon(host="my.external.ip.address")
PYRO_HOST
PYRO_DNS_URI
PYRO_PUBLISHHOST
PYRO_NS_HOSTNAME
/etc/hosts
file on your system and fix the IP address that's associated with your hostnameIn 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.html 0000644 0000765 0000024 00000041265 11526311451 017720 0 ustar irmen staff 0000000 0000000Nevertheless this chapter is a nice starting point to explore the different parts that make up Pyro.
bin/*
- Command-line tools__init__.py
- Pyro package initializationconfiguration.py
- Configuration codeconstants.py
- Global constants used troughout Pyrocore.py
- Core codenaming.py
- Name Serverprotocol.py
- Protocol adaptersutil.py
- Utility codeerrors.py
- Exception definitionsPyro.EventService
- Event Server packagePyro.ext
- Extensions package (deprecated, don't use in new code)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.
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 |
__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.
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.
protocol
module contains this. What the core module does
contain is all stuff that realize the core functions of Pyro:
ObjBase
Pyro_dyncall
) and remote attribute access
(remote_getattr
etc.)PyroURI
PyroURI
can be converted to and from a -
human readable - string.DynamicProxy
and DynamicProxyWithAttrs
_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
Daemon
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.initClient
and initServer
functions perform necessary initialization. For
instance, the server init code first checks the availability of the PYRO_STORAGE
directory.NameServerLocator
getNS
method and you get a Pyro Proxy for the Name Server back.NameServerProxy
NameServerLocator.getNS
.NameServer
and PersistentNameServer
ns
script that starts the Name Server.NameValue
and a NamedTree
recursive data structure.BroadcastServer
LoggerBase
SystemLogger
Pyro.util.Log
and this is Pyro's system logger
that writes to Pyro's system logfile.UserLogger
ArgParser
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
genguid_scripthelper
genguid
command line utility, which prints a new GUID.getPyroTraceback
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).
Pyro/EventService/Clients.py
Subscriber
and Publisher
base classes defined that you can use to easily
build publisher or listener clients for the ES.Pyro/EventService/Server.py
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.Pyro/ext/remote.py
Pyro/ext/remote_nons.py
Pyro/ext/daemonizer.py
Pyro/ext/BasicNTService.py
Pyro/ext/NS_NtService.py
Pyro/ext/ES_NtService.py
Pyro/ext/ServiceTest.py
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 :-)
core.getProxyForURI/DynamicProxy
.__init__.py
.__init__.py
. They are accessed using
'Pyro.config.<item>
'.DynamicProxy
and
getProxyForURI
util.ArgVParser
.bat
files now use %$
util.py
, which contains a basic sys.argv
parser like
getopt()
.SocketServer
module. Pyro
now requires this new SocketServer
module (which works with previous Python versions, too, so you
could only replace the SocketServer
module and keep the rest of your Python installation intact).getUUID
function, and a script genuuid
.id()
. This should
prevent nasty object ID problems, especially later when persistent naming and object activation will be
implemented.ProtocolError, URIError, DaemonError, NamingError
...)NameServerLocator
, which returns a regular Pyro proxy for the NS. No NameServerClient
is
needed anymore.Pyroc.bat
now uses %$
, too (oops!)xnsc
, a naming service control tool with a GUI (uses Tkinter
).Pyro.config
: PYRO_PATH
and
PYRO_STORAGE
.ObjBase
to set a fixed (persistent) object UUID.PyroURI
, it can now be created directly from a URI string,
instead of using initFromString
.Daemon
now has preliminary support for persistent objects:
connectPersistent
and disconnectPersistent
methods were added. LIKELY TO CHANGE!PersistentNameServer
. Also added some code to deal
with this when using the 'ns
' command line tool: a new option '-db
' has been added to
specify persistent mode.nsc
(and xnsc
) now print a sorted list (and have the shutdown command)..bat
files now won't echo the python command anymore.ArgParser
changed to use None
instead of 1
when no arguments are
given to a certain option.testclient
will use dynamic proxy automatically if no static proxy is found. Also, it will
try a direct connect if the NS lookup by broadcast fails.pyroc
!errors.py
and
protocol.py
.Logger
object, Log
. It is used throughout Pyro to log messages,
warnings and errors. You can configure the logfile and the trace level settings with some new configuration
options. Ofcourse you can use it for your own logging purposes too.PYRO_NS_NAME
because that is a registered name and should not be changed.genuuid
scripts have been renamed to genguid
.self
' argument of a member function had a different name. Pyroc
now uses 'S
' everywhere.__xxx__
. They are not allowed in the proxy
code.core.py
to protocol.py
nsc
and xnsc
tools are stricter with the names and URIs they
allow.PYRO_USE_WIN32GUID
. This is not the default and
will work only on the Windows platform, because the Python COM extension is used for this.genguid
script with '-w
' argument to generate a Windows GUID if possible.PyroURI
class has a new optimizeLocalhost
method, which -if possible-
optimizes the URI for localhost addressing.Pyro.core.ObjBase
, you just create a Pyro.core.ObjBase
object and
tell it to use another object as a delegate.ArgParser
in util.py
, to support more convenient script arguments. A few scripts
have slightly altered syntaxes now, which should be more convenient. The old syntax should still work however.Daemon
inherits from the new TCPServer
base class (in
protocol.py
). This server replaces the SocketServer.TCPServer
. The new server keeps a
list of active connections, while the old one operated like a webserver: it created a new socket connection for
each request.#*
instead of #$
(which didn't work on the standard
Windows95/NT command prompt).xnsc
handles communication errors better.test
directory and added some readme files. Also added some more
tests/examples.protocol.py
. Now only naming.py
and
protocol.py
have socket code.NULLAdapter
.protocol.py
for robust data transmission. The new
protocol code uses them. Performance is lower but the new code should guarantee correct behaviour even at high
network loads with many packet losses and fragmentations.SocketClosedError
and ServerFullError
.RemotePyroException
(unused).xnsc
GUI: better resizingPYRO_MAXCONNECTIONS
that sets this
limit. Currently the default is 200.unregister
method of the naming service now raises a NamingError
when the name is
not known. I added this because usually this signals a typo in the calling code (it should never unregister an
unknown name).naming
module.PYRO_NS_GROUPSEP
, which is the character that separates groups in names.
By default it is a dot ('.'), but if this proves incompatible with existing naming schemes (that use dots in the
names), just change it to something else. Notice that this item is only examined once: at startup time.
You have to restart the Naming Service if you change this item.PYRO_NS_DEFAULTGROUP
, which is the default group name in which names are
located. This simplifies partitioning your name space greatly. Notice that the Name Server proxy code is hand
crafted to enforce this name.PYRO_NS_ROOTCHAR
, which is the default escape character at the beginning
of names, which indicates the name is absolute from the root.PYRO_NS_NAME
...createGroup
, deleteGroup
and list
. Furthermore, status
has been
renamed to flatlist
. This suggests better what it does: return a flat dump of the namespace.nsc
and xnsc
with the new naming service features: list,
creategroup and deletegroup commands. (The old list command was renamed to
listall).protocol.py
where a KeyError
in a method invocation was always treated
as if a method call on an unknown object was tried, even when the object was correct (but raised a
KeyError
)KeyboardInterrupt
) will still abort it, however, as
will sending a shutdown command.core.py
. If a ProtocolError is detected (for instance, an invalid
header), the connection is dropped and operations are resumed. Pyro used to block or crash when a non-Pyro client
connected, such as a plain telnet or a web browser pointed at Pyro's socket.:test
" group (namespace) too, to help avoid conflicts with your
own names. Servers will no longer crash if their name is already registered (they just register
again).-k
' switch to ns
that will start the NS in a mode in which it ignores shutdown
requests sent by nsc
. This is a crude form of security, to protect the NS for malicious
shutdowns.PYRO_CONFIG_FILE
. Only useful as environment variable, it points to the
configuration file Pyro has to use.Daemon.connect
now returns the URI it connects the object as. This is convenient for servers that
want to use the URI immediately without having to consult the nameserver.ObjBase
and new DynamicProxyWithAttr
) You have to update all Pyro installations if
you want to use this new feature. If you don't use it, Pyro 1.2 should work together with version 1.1. Notice
that there is a performance hit of a few percent when you use DynamicProxyWithAttr
instead of the
regular DynamicProxy
to support the attribute feature.DynamicProxy
: it delays binding until actually needed, and they can now be safely pickled).obj.setCount(obj.getCount()+1)
now works correctly.PYRO_PORT_RANGE
.pyroc
to 1.2; static proxies now also support direct attribute access.DaemonSlave
class. Moved it into the Daemon
class. Also required small
change to TCPServer
.protocol.PYROAdapter
were also
necessary.TCPServer
and Pyro.core.Daemon
.
The
ns
command has a new option "-n" to specify a specific hostname to bind the server
on.optimizeLocalhost
method from PyroURI
PyroURI
consequently has an address
member instead of the
previous host
member. There are some small changes throughout the code to support this.os.path.abspath
.handleRequest
loop: in contrast to what the documentation says, it didn't
return after a request had been processed. It now does.PYRO_USE_WIN32GUID
).TCPServer
allows it to run in multithreaded
mode on systems that have Python threads. Please read the Rules and Limitations chapter for more
information. Note that the multithreaded capabilities have not yet been tested very much and probably aren't
very stable.PYRO_MULTITHREADED
config item to specify whether a Pyro server should be multithreaded or
single threaded. It defaults to 1 (multithreaded) on systems that support this.''
- fixed).NameServerLocator
.xnsc
: the port number of the NS is now correctly displayed.remote.py
module.
It makes using Pyro extremely easy: look at the client and server code!__copy__
method to the proxy code, which creates a local copy of a proxy object (with a new
connection to the remote object).protocol.py
to be compatible with Python 1.6.int2binstr
could return a string with less bytes than expected
which creates an invalid binary GUID. The code now passes a required string size and the GUID is now always 4*4
bytes.PYRO_COMPRESSION
. However, the current
solution is temporary and is likely to be replaced with a more general implementation. The zlib
module
is used for compression. If you don't have zlib
, Pyro still works, but without compression.Pyro_dyncall
method of ObjBase
that makes it a little more generic
when checking for 'local' attributes.__call__
method from DynamicProxy
. I assume nobody ever used the only benefit
it provided (that proxy('method')
was slightly faster than proxy.method()
).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 config. Furthermore, Pyro now
raises a PyroError if somehow a config file can't be read.remote.py
and a
very convenient
pyrorun
script. (John Wiegley)rns
script; this script restarts the NS automatically after a shutdown or crash.core.py
.PersistentNameServer
class in naming.py
, which implements a NameServer that
uses the hierarchical file system on your disk to store its database in a persistent way.PyroURI
instances from resolve
. In the past it could
also return URIs as strings.socket.error
but Pyro.errors.SocketClosedError
exceptions.PYROAdapter
has new rebindURI
method to support the new auto reconnect/rebind feature
when a network problem occurs.PYROAdapter
now raises a ProtocolError
when it receives a request for an unknown
object ID.ServerFullError
exception has been removed!!! You must change your code that used it. It has been
replaced by a more general ConnectionDeniedError
exception.PYRO_MOBILE_CODE
config item to enable mobile code.PYRO_DNS_URI
config item to use symbolic DNS hostnames in URIs instead of fixed IP
addressesPyro.core.ObjBase
.core.py
; PROTOCOL IS INCOMPATIBLE with previous version.Pyro.core.Daemon
: extra publishhost
argument to use in case of
firewall situation. See Features chapter.PYRO_NS_NAME
; it is now hardcoded and available (if needed) as
Pyro.naming.NS_NAME
.PYRO_NS_GROUPSEP
; it's now hardcoded as '.'.PYRO_NS_ROOTCHAR
; it's now hardcoded as ':'.Pyro.EventService.*
). See the chapter on Pyro Services.
There is an example too: "stockquotes".DynamicProxy
; it can now be used as a key in a dictionary. This was needed for the Event
Servive.SocketClosedException
to ConnectionClosedException
. The new name is more
independent of the actual implementation.socket
module and socket.error
. This makes the rest of
the code more independent of the actual protocol implementation. A future Pyro release may contain even more
changes to fully support multiple protocol implementations (not only sockets).pickle
trojan vulnerability in the 'Features and Guidelines' chapter.PYRO_NS_HOSTNAME
config item, for direct hostname lookup when the NS can't be found using
broadcast.select
,
errno
and getpid
. Only PYRO clients in Jython currently, because servers need
select
!remote.py
and it should work again on Windows
(signals).PYRO_LOGFILE
or PYRO_USER_LOGFILE
, all following log messages are written to the new location. If the filename
isn't an absolute path, it will be made relative to the current PYRO_STORAGE
location.PYRO_STORAGE
config item.__nonzero__
has been defined in the proxy.PYRO_CHECKSUM
to enable a quick Adler32 checksum on the messages. (Why Adler32
and not CRC32? Adler is faster, and we need speed).PYRONAME://nshostname:port/objectname
for very easy automatic object lookup.
nshostname and port are optional.PYROLOC://hostname:port/objectname
for very easy object binding without using a
Name Server. port is optional.Pyro.core.INTERNAL_DAEMON_GUID
. I'm not sure if this
feature stays, because it might also be a huge security/stability hole. (please advise?)Pyro.constants
to avoid import conflicts.Pyro.constants.NAMESERVER_NAME
and
Pyro.constants.EVENTSERVER_NAME
. (previously NS_NAME
and
EVENTSERVICE_NAME
)StringTypes
that was a Python 2.2 dependency.Pyro.util.getPyroTraceback
.NameServerLocator
no longer performs a broadcast lookup if the hostname is specified in the
getNS
call, or if the PYRO_NS_HOSTNAME
config item is specified.requestLoop
, instead of
handleRequests
(Don't worry, the latter still exists!). Updated most examples.core.py
. It only works with the more abstract
connection objects.getProxy
and
getAttrProxy
.proxy._release()
daemon.setTransientsCleanupAge(timeout)
Also added this to the Bank2 example, so have a look
there.CallbackObjBase
. It will raise local exceptions also, not
only silently passing them back to the server.PyroURI
instances from PYROLOC: and PYRONAME: uris. The instance will be the actual
URI, after performing the lookup. Not very useful actually, because we used to do the equivalent
proxy=Pyro.core.getProxyForURI('PYRONAME://.......')
Now you can also do
proxy=Pyro.core.PyroURI('PYRONAME://.......').getProxy()
self.getLocalStorage()
. This is an
empty class to store your stuff into. It has one attribute, caller
, that is the TCPConnection of the
calling object.remote.py
and remote_nons.py
(from the "quickstart[-noNS]"
examples) into the Pyro library: there is a new package Pyro.ext
that contains them.
They still have to be documented in this manual though.PyroURI
objects are now correctly hashable, so you can use them as keys in dictionaries. The
uniqueness is determined by the string representation of the oject.MyObject.attribute.subattribute
won't work in some cases, and extended
the attributes example a bit.PYRO_ES_QUEUESIZE
and PYRO_ES_BLOCKQUEUE
.PYRO_XML_PICKLE
changed: now chooses what
implementation is wanted._release()
first.SynchronizedObjBase
base class which provides threadsafe method calls (every method call is
synchronized on the object).CallbackObjBase
error reporting, added another callback example for this.CallbackObjBase
is better documented.DefaultConnValidator
). It is now much more
flexible. By providing a specialized ConnValidator object you can now completely control the logic that Pyro uses
on both client-side and server-side to authenticate new client connections. The change is backwards-compatible and
the default implementation still uses the md5 hashing of identification phrases for authentication.validateHostnameAndIP
method of the Daemon.apply
with func(*args)
syntax. Small speed increase
too.nssvc.bat
and
essvc.bat
to install/remove them.nsd
and
esd
to run them from init scripts.Pyro.core.initClient()
or
initServer()
. Failure to do so will result in a PyroError
very soon.PYRO_STORAGE
is no longer created and checked for
access when initializing clients. When you're initializing a server, it is still checked, unless
you explicitly disable the check by setting the new optional argument storageCheck
to zero:
Pyro.core.initServer(storageCheck=0)
.proxy.method(proxy)
i.e. pass the active proxy to a methodcall on itself. That didn't
work before.PYRO_DETAILED_TRACEBACK
to 1. Contributed by Richard Emslie.__str__
method has been customized in the PyroExceptionCapsule
. No longer necessary to use exception handlers
and call getPyroTraceback
. Contributed by jf Gosset. This is turned off by default, turn it on by
setting PYRO_PRINT_REMOTE_TRACEBACK
to 1.logging
module can be enabled with the use of two new config items
PYRO_STDLOGGING
and PYRO_STDLOGGING_CFGFILE
. The logging API is unchanged so old code
still runs. With an appropriate logging configuration file, it should be possible to log to any destination
(handler) that logging
supports. This further enables clients (with logging!) on a read-only system:
just log to syslog or a socket, no need to write logfiles to the local disk.nsc
(and the others) can show the meta information.__copy__
method to DynamicProxyWithAttrs
. (This bug caused the Bank2
example to crash, for instance).Pyro.core.initClient()
. Pyro will do
this automatically if it is not yet initialised.Pyro.core.initServer()
if you are
creating a Pyro Daemon as a first action. If you have not yet initialised Pyro explicitly, Pyro will do this for
you when you create a Pyro Daemon. If you do other Pyro operations before that, this will obviously not work.HKLM\System\CurrentControlSet\Services\PyroES
and ...\PyroNS
. The value under
that key is: PyroServiceArguments
(REG_SZ, it will be asked and created for you when doing a fresh
nssvc remove
... nssvc install
... essvc remove
... essvc
install
.Pyro.naming.NameServerStarter
now raises exceptions when it cannot start the name server.Pyro.naming.NameServerStarter
and Pyro.EventService.Server.EventServiceStarter
now
have a threadsafe way to wait until the server has been started: call the waitUntilStarted(timeout)
method. This is used in the updated "AllInOne" example.meth =
proxy.someMethod
), this did not work before.prox in aList
even when
the list contains other objects.NamingError
exception args.createAuthToken
now uses hmac-md5 instead of 'plain'
md5. Updated the "user_passwd_auth" example accordingly.Pyro versions before 3.8 can be found in the archive.
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.
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!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.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.
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!
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.
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).
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/
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.PYTHONPATH
).bin/
docs/
and 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:
Pyro.config.PYRO_STORAGE
! See below!Pyro.config.PYRO_PORT = 7000
Pyro.config.PYRO_TRACELEVEL = 3
$ export PYRO_LOGFILE=/var/log/PYRO/logfile
$ export PYRO_TRACELEVEL=3
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.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.
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-nsc
tool to unregister them, or restart the NS.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)pyro-ns
(Name Server)pyro-es
(Event Server)pyro-nsc
(Name Server Control tool)pyro-xnsc
(Graphical NS control tool)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)xnsc
tool, but based on the WxPython GUI toolkit.
pyro-nssvc, pyro-essvc
(Windows-only Name Server and Event Server 'NT-service' control
scripts)python -m
to start various toolspython -m Pyro.naming
- start the name serverpython -m Pyro.EventService.Server
- start the event serverpython -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.__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.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.
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.
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.
Pyro.core.ObjBase
. There are three ways to achieve this:
Pyro.core.ObjBase
and your original class. The class body can be a
simple 'pass
'. If you want to add a custom __init__
method, make sure you call the
__init__
method of Pyro.core.ObjBase
and of your own class, if it has one.
class ObjectImpl(Pyro.core.ObjBase, test.MyClass): def __init__(self): Pyro.core.ObjBase.__init__(self) test.MyClass.__init__(self) ... obj = ObjectImpl() ... use obj ...
Pyro.core.ObjBase
you just create that object and tell it to use your
own object as a delegate, by calling the delegateTo
method.
obj = Pyro.core.ObjBase() myobj = MyClass() obj.delegateTo(myobj) ... use obj ...
Pyro.core.ObjBase
. This is the least hassle
but you have to change existing code if you want to make classes suitable for Pyro.
class MyPyroObj(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) ...obj init here... ... obj = MyPyroObj() ... use obj ...
ObjBase
:
Pyro.core.SynchronizedObjBase
Pyro.core.CallbackObjBase
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.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.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.
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).
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.
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 loopHave 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.
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.
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.
ns
now contains the proxy for the NS.
uri = ns.resolve('my_object')
PyroURI
object before you use it. Just pass it to the constructur of
PyroURI
and you'll be fine.PYRONAME://
or PYROLOC://
URI strings. The first is a shortcut to the
Name Server, the second bypasses the Name Server completely. More info is in the Name Server chapter.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 supportIf you're using attribute proxies, be aware of their properties and limitations.
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+1or 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.
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.
pyro-nsc
command-line utility or the pyro-xnsc
graphical tool for this purpose, see Pyro script
tools.
Pyro.util.Log
object, which is an instance of
Pyro.util.SystemLogger
. System log tracelevel is configured using the PYRO_TRACELEVEL
config item, the logfile location is configured using the PYRO_LOGFILE
config item.Pyro.util.UserLogger
instance. User log
tracelevel is configured using the PYRO_USER_TRACELEVEL
config item, the user logfile location is
configured using the PYRO_USER_LOGFILE
config item.msg(source, *args)
- log a simple message (note). source
is a string that
identifies the source of the log entry, after that, any argument may follow to be printed in the logfile.error(source, *args)
- log an error. source
is a string that identifies the
source of the log entry, after that, any argument may follow to be printed in the logfile.warn(source, *args)
- log a warning. source
is a string that identifies the
source of the log entry, after that, any argument may follow to be printed in the logfile.raw(string)
- log a string (unformatted). string
is the string to write to the
logfile. This logging is done unconditionally, the tracelevel setting has no influence here.2002-01-16 16:45:02 [5884:MainThread] ** ERR! ** NameServerLocator ** Name Server not
responding
"raw
method can have any format,
including multiple lines.
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.
caller
property of the TLS object if required: that object
always equals the currently active client connection. Multithreaded or not.caller
property of the TLS in some way instead of just storing stuff on the TLS directly.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:
echo(args)
this will simply return the arguments you passed to it.error()
this will raise a remote exception.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 >>>
These chapters contain invaluable information about the more detailed aspects and possibilities of Pyro.
Pyro-3.14/docs/5-nameserver.html 0000644 0000765 0000024 00000062747 11526311451 016775 0 ustar irmen staff 0000000 0000000PYRONAME://
, PYROLOC://
and
PYROLOCSSL://
URIsbin
directory that will start a Pyro Name Server for you:
pyro-ns
(Name Server)pyro-ns.cmd
pyro-nssvc
(Windows-only Name Server 'NT-service' control script)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).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).python -m
to start the tools:python -m Pyro.naming
- start the name serverpython -m Pyro.nsc
- start the nsc tool. Also works with xnsc and wxnsc.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).
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. |
Pyro.naming.NameServer
)Pyro.naming.PersistentNameServer
)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.
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.
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.
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:
NameServerLocator
's broadcast mechanism. This only works if your network supports
broadcasting and the NS is reachable by a broadcast request.
locator = Pyro.naming.NameServerLocator() ns = locator.getNS()
If your network doesn't allow broadcasts, or the broadcast can't reach the NS, this mechanism doesn't work. There
is a simple workaround: just set the PYRO_NS_HOSTNAME
config option to the hostname on which your NS can
be found. This disables the broadcast lookup and uses the one below instead.
For simplicity, if Pyro cannot find the name server using a broadcast search, it tries to contact
it directly on the machine itself and localhost. This allows you to use it even on a system
where the name server is running on localhost (for some reason) without having to set
PYRO_NS_HOSTNAME
to localhost
.
You can override the default broadcast address (255.255.255.255) by setting the bcaddr
parameter:
ns=locator.getNS(bcaddr="192.168.1.255")
NameServerLocator
's direct host mechanism. This only works if you know the host on which
the NS is located. The port
argument is optional. If the NS has been started using a non-default port
number you can use it to specify the port number.
locator = Pyro.naming.NameServerLocator() ns = locator.getNS(host='hostname', port=7777)If you specify the hostname yourself, the locator doesn't attempt to find the NS with the broadcast mechanism, and therefore there is no lookup delay. Also you can specify a port number different from the default port. If you set the
PYRO_NS_HOSTNAME
config option, the locator automatically uses the specified host for a direct lookup.
You don't have to pass Pyro.config.PYRO_NS_HOSTNAME
to the getNS
method.
Pyro_NS_URI
':
uri = open('Pyro_NS_URI','r').read() uri = Pyro.core.PyroURI(uri) # convert string to real URI object ns = Pyro.naming.NameServerProxy(uri) # create a proxy for the NS ... you can now invoke methods, such as ns.ping() ...Note: The URI changes every time a Pyro server or object is created. You cannot use a previously written URI when you have restarted the server.
Pyro.naming.NameServerProxy
) contains all necessary logic
already. So just use that one. If you use the locator (see above) you will get a correct proxy automatically.NameServerProxy
has an optional second argument: the
connection authentication information.
PYRONAME://
, PYROLOC://
and
PYROLOCSSL://
URIsPYRO://
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)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.
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)
register(name, URI)
unregister(name)
ping()
list(groupname)
flatlist()
createGroup(name)
deleteGroup(name)
fullName(name)
setMeta(name, meta)
getMeta(name)
nsc.py / xnsc.py
) for more info.
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:
pyro-es
command
from the bin
directory (use pyro-es.cmd
on windows). You can specify the following
arguments:pyro-essvc
(Windows-only Event Server 'NT-service' control scripts)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.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).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.
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.
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)
__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).
__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.
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.
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.
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()
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).
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 |
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:
RemoteObj.person.address.street
) or 'nested' method calls (such as
RemoteObj.person.address.getStreet()
): the rule counts for each attribute.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.
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()
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).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.rebindURI
method of the object's protocol adapter itself if this is the case.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.
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.
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:
__main__
will not work because of a Python peculiarity. There are no plans for including
a workaround in Pyro.__init__
, you have
to import them from within the mobile object class methods.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.
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.
Pyro.core.SynchronizedObjBase
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!
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.
The "multithread" example shows what's going on, and how multithreading can help to improve performance and response times.
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.
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.
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.
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.
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.
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!
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.
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.
__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.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).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.Pyro.naming.NameServerProxy
.
When you use the Locator, you're safe..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..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.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.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.
daemon.shutdown()
when your program exits. This will cleanly stop all
threads that might still be running.__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) ...
Pyro.core.ObjBase
, or use the delegation approach, and call
Pyro.core.ObjBase.__init__(self)
from your own __init__
URI = self.getDaemon().connect(object) # no name, not in NS URI = self.getDaemon().connect(object,'name') # registered in NS with 'name'
return object
that
you would otherwise use, you do:
return object.getProxy() # regular dynamic proxy return object.getAttrProxy() # dynamic proxy with attribute support
self.getDaemon().disconnect(object) # can also pass object's UID del object
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.)Pyro.core.CallbackObjBase
instead of
the regular
ObjBase
. (see above at "callbacks")__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()
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.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.daemon.disconnect(object)
(you can disconnect the object directly or provide its UID)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.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.
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()
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")
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()
import Pyro.core # finds object automatically if you're running the Name Server. jokes = Pyro.core.getProxyForURI("PYRONAME://jokegen") print jokes.joke("Irmen")
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
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()
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.
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 ...)
(...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')
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.
irmen@atlantis:~/ex > python testserver.py Pyro Server Initialized. Using Pyro V3.5 searching for Name Server... Server object "test" ready.
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
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
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.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:
Look at the "denyhosts", "authenticate" and "user_passwd_auth" examples to see how you can use the connection validators.
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.
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.
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:
_setNewConnectionValidator(validator)
setNewConnectionValidator(validator)
_setIdentification(ident)
setAllowedIdentifications(idents)
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):
The three check methods
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 inconnection.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 codesdef 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 usescreateAuthToken
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 theconnection
argument to store the authentication token (which might be a username). A Pyro object may access this again by getting the connection objectself.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 useobj._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 fromPyro.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 theBasicSSLValidator
base class implementation of that method if you override it. See table below for return codes
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.
BCGuard()
function that returns a BC request validator object, or None
.NSGuard()
function that returns a NS new conn validator object, or None
.Pyro.naming.BCReqValidator
. You must override the two methods that check for each command if it is
allowed or if it is refused. These are acceptLocationCmd(self)
and
acceptShutdownCmd(self)
, and they return 0 or 1 (accept or deny). You can access
self.addr
to have the client's address (ip,port). You can call self.reply('message')
to
send a message back to the client. This may be polite, to let it know why you refused the command.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?
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.
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.html 0000644 0000765 0000024 00000001047 11526311451 015555 0 ustar irmen staff 0000000 0000000The following might be of some value:
Pyro-3.14/docs/LICENSE 0000644 0000765 0000024 00000002475 11517657576 014620 0 ustar irmen staff 0000000 0000000 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.png 0000644 0000765 0000024 00000076166 11517657576 016412 0 ustar irmen staff 0000000 0000000 ‰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¶ü÷ox ö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ñ