<previous | top | next> | Pyro Manual |
More specifically, when your client detects a problem with the network
(SocketClosedError
) 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).
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. A client might supply a hazardous or buggy program. Therefore you can set a codeValidator for each Pyro object that might load mobile code.
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.
LIMITATION: currently it is not possible to use mobile objects from a module in a package or subpackage. You can only use modules that can be imported directly ("import ModuleName
").
Have a look at the "agent2" example to see how this all works.
In the past, there was only one built-in check; the number of connections was limited to a certain amount.
Since version Pyro 2.0 this check is now done by the default connection validator Pyro.protocol.limitingConnValidator
.
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.
accept(self, connection)
method. The connection
is a Pyro.protocol.TCPConnection
object. The client's address is in connection.addr
.
When you want to derive from the default validator Pyro.protocol.limitingConnValidator
, keep in mind that the constructor needs the
Pyro Daemon object as argument.
setNewConnectionValidator
method. Supply an instance
of your validator class as an argument.
accept
method must return (1,0)
if the connection is accepted (or call another validator's accept
).
It must return (0,code)
when the connection is refused, where code
is one of the following:
Deny Reason Code | Description |
---|---|
0 | unspecified |
1 | server too busy (too many connections) |
2 | host blocked |
3 | security reasons (general) |
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.
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. The singlethreaded mode is quite a bit faster.
Still, 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 high performance, 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.
The "multithread" example shows what's going on, and how multithreading can help to improve performance and response times.
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.
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 URI's 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 URI's as though
it were actually 192.1.1.1. Were it not for 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.
__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.
Package.module
').
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. Since Pyro 2.0
this is no longer necessary if you enable the mobile code feature.
See the "agent2" example.
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. This approach is used in the example in the next chapter. See also the next item.
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.
__init__
from the base class in the __init__
method -if you have one- of your remote class: def __init__(self): Pyro.core.ObjBase.__init__(self) ...
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.ObjBase
, or use the delegation approach,
and call Pyro.core.ObjBase.__init__(self)
from your own __init__
URI = self.getDaemon().connect(object) # no name
proxy = object.getProxy() # regular dynamic proxy proxy = object.getProxy(1) # dynamic proxy with attribute support
self.getDaemon().disconnect(object) del object