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.