1
#############################################################################
3
# $Id: core.py,v 2.104.2.25 2009/03/29 20:19:27 irmen Exp $
6
# This is part of "Pyro" - Python Remote Objects
7
# which is (c) Irmen de Jong - irmen@users.sourceforge.net
9
#############################################################################
11
import sys, time, re, os, weakref
12
import imp, marshal, new, socket
13
from pickle import PicklingError
14
import Pyro.constants, Pyro.util, Pyro.protocol, Pyro.errors
15
from Pyro.errors import *
16
from types import UnboundMethodType, MethodType, BuiltinMethodType, TupleType, StringType, UnicodeType
17
if Pyro.util.supports_multithreading():
23
def _checkInit(pyrotype="client"):
24
if not getattr(Pyro.config, Pyro.constants.CFGITEM_PYRO_INITIALIZED):
25
# If Pyro has not been initialized explicitly, do it automatically.
26
if pyrotype=="server":
32
#############################################################################
34
# ObjBase - Server-side object implementation base class
35
# or master class with the actual object as delegate
37
# SynchronizedObjBase - Just the same, but with synchronized method
38
# calls (thread-safe).
40
#############################################################################
44
self.objectGUID=Pyro.util.getGUID()
46
self.lastUsed=time.time() # for later reaping unused objects
47
if Pyro.config.PYRO_MOBILE_CODE:
48
self.codeValidator=lambda n,m,a: 1 # always accept
50
return self.objectGUID
51
def setGUID(self, guid): # used with persistent name server
52
self.objectGUID = guid
53
def delegateTo(self,delegate):
54
self.delegate=delegate
55
def setPyroDaemon(self, daemon):
56
# This will usually introduce a cyclic reference between the
57
# object and the daemon. Use a weak ref if available.
58
# NOTE: if you correctly clean up the object (that is, disconnect it from the daemon)
59
# the cyclic reference is cleared correctly, and no problem occurs.
60
# NOTE: you have to make sure your original daemon object doesn't get garbage collected
61
# if you still want to use the objects! You have to keep a ref. to the daemon somewhere.
63
self.daemon=weakref.proxy(daemon)
66
def setCodeValidator(self, v):
68
raise TypeError("codevalidator must be a callable object")
72
def getLocalStorage(self):
73
return self.daemon.getLocalStorage()
75
# Called when daemon reaps this object due to unaccessed time
76
# Override this method if needed; to act on this event
79
return self.daemon.getProxyForObj(self)
80
def getAttrProxy(self):
81
return self.daemon.getAttrProxyForObj(self)
82
def Pyro_dyncall(self, method, flags, args):
83
# update the timestamp
84
self.lastUsed=time.time()
85
# find the method in this object, and call it with the supplied args.
87
if flags & Pyro.constants.RIF_Keywords:
88
# reconstruct the varargs from a tuple like
89
# (a,b,(va1,va2,va3...),{kw1:?,...})
92
if flags & Pyro.constants.RIF_Varargs:
93
# reconstruct the varargs from a tuple like (a,b,(va1,va2,va3...))
94
args=args[:-1]+args[-1]
95
if keywords and type(keywords.iterkeys().next()) is unicode and sys.platform!="cli":
96
# IronPython sends all strings as unicode, but apply() doesn't grok unicode keywords.
97
# So we need to rebuild the keywords dict with str keys...
98
keywords = dict([(str(k),v) for k,v in keywords.iteritems()])
99
# If the method is part of ObjBase, never call the delegate object because
100
# that object doesn't implement that method. If you don't check this,
101
# remote attributes won't work with delegates for instance, because the
102
# delegate object doesn't implement _r_xa. (remote_xxxattr)
103
if method in dir(ObjBase):
104
return getattr(self,method) (*args,**keywords)
106
# try..except to deal with obsoleted string exceptions (raise "blahblah")
108
return getattr(self.delegate or self,method) (*args,**keywords)
110
exc_info = sys.exc_info()
112
if type(exc_info[0]) == StringType :
113
if exc_info[1] == None :
114
raise Exception, exc_info[0], exc_info[2]
116
raise Exception, "%s: %s" % (exc_info[0], exc_info[1]), exc_info[2]
120
del exc_info # delete frame to allow proper GC
122
# remote getattr/setattr support:
123
def _r_ha(self, attr):
125
attr = getattr(self.delegate or self,attr)
126
if type(attr) in (UnboundMethodType, MethodType, BuiltinMethodType):
131
def _r_ga(self, attr):
132
return getattr(self.delegate or self, attr)
133
def _r_sa(self, attr, value):
134
setattr(self.delegate or self, attr, value)
135
# remote code downloading support (server downloads from client):
136
def remote_supply_code(self, name, module, sourceaddr):
137
# XXX this is nasty code, and also duplicated in protocol.py _retrieveCode()
138
if Pyro.config.PYRO_MOBILE_CODE and self.codeValidator(name,module,sourceaddr):
140
imp.acquire_lock() # threadsafe imports
141
if name in sys.modules and getattr(sys.modules[name],'_PYRO_bytecode',None):
142
# already have this module, don't import again
143
# we checked for the _PYRO_bytecode attribute because that is only
144
# present when all loading code below completed successfully
146
Log.msg('ObjBase','loading supplied code: ',name,'from',str(sourceaddr))
147
if module[0:4]!=imp.get_magic():
148
# compile source code
149
code=compile(module,'<downloaded>','exec')
151
# read bytecode from the client
152
code=marshal.loads(module[8:])
154
# make the module hierarchy and add all names to sys.modules
157
mod=new.module("pyro-agent-context")
160
# use already loaded modules instead of overwriting them
162
if sys.modules.has_key(real_path):
163
mod = sys.modules[real_path]
165
setattr(mod,m,new.module(path[1:]))
167
sys.modules[path[1:]]=mod
168
# execute the module code in the right module.
169
exec code in mod.__dict__
170
# store the bytecode for possible later reference if we need to pass it on
171
mod.__dict__['_PYRO_bytecode'] = module
175
Log.warn('ObjBase','attempt to supply code denied: ',name,'from',str(sourceaddr))
176
raise PyroError('attempt to supply code denied')
178
# remote code retrieve support (client retrieves from server):
179
def remote_retrieve_code(self, name):
180
# XXX codeValidator: can we somehow get the client's address it is sent to?
181
# XXX this code is ugly. And duplicated in protocol.py remoteInvocation.
182
if Pyro.config.PYRO_MOBILE_CODE and self.codeValidator(name,None,None):
183
Log.msg("ObjBase","supplying code: ",name)
185
importmodule=new.module("pyro-server-import")
187
exec "import " + name in importmodule.__dict__
189
Log.error("ObjBase","Client wanted a non-existing module:", name)
190
raise PyroError("Client wanted a non-existing module", name)
191
m=eval("importmodule."+name)
192
# try to load the module's compiled source, or the real .py source if that fails.
193
# note that the source code (.py) is opened with universal newline mode
194
(filebase,ext)=os.path.splitext(m.__file__)
195
if ext.startswith(".PY"):
196
exts = ( (".PYO","rb"), (".PYC","rb"), (".PY","rU") ) # uppercase
198
exts = ( (".pyo","rb"), (".pyc","rb"), (".py","rU") ) # lowercase
199
for ext,mode in exts:
201
m=open(filebase+ext, mode).read()
202
return m # supply the module to the client!
205
Log.error("ObjBase","cannot read module source code for module:", name)
206
raise PyroError("cannot read module source code")
210
Log.error("ObjBase","attempt to retrieve code denied:", name)
211
raise PyroError("attempt to retrieve code denied")
214
class SynchronizedObjBase(ObjBase):
216
ObjBase.__init__(self)
217
self.synlock=Pyro.util.getLockObject()
218
def Pyro_dyncall(self, method, flags, args):
219
self.synlock.acquire() # synchronize method invocation
221
return ObjBase.Pyro_dyncall(self, method,flags,args)
223
self.synlock.release()
226
# Use this class instead if you're using callback objects and you
227
# want to see local exceptions. (otherwise they go back to the calling server...)
228
class CallbackObjBase(ObjBase):
230
ObjBase.__init__(self)
231
def Pyro_dyncall(self, method, flags, args):
233
return ObjBase.Pyro_dyncall(self,method,flags,args)
236
Log.warn('CallbackObjBase','Exception in callback object: ',x)
237
raise PyroExceptionCapsule(x,str(x))
240
#############################################################################
242
# PyroURI - Pyro Universal Resource Identifier
244
# This class represents a Pyro URI (which consists of four parts,
245
# a protocol identifier, an IP address, a portnumber, and an object ID.
247
# The URI can be converted to a string representation (str converter).
248
# The URI can also be read back from such a string (reinitFromString).
249
# The URI can be initialised from its parts (init).
250
# The URI can be initialised from a string directly, if the init
251
# code detects a ':' and '/' in the host argument (which is then
252
# assumed to be a string URI, not a host name/ IP address).
254
#############################################################################
257
def __init__(self,host,objectID=0,port=0,prtcol='PYRO'):
258
# if the 'host' arg is a PyroURI, copy contents
259
if isinstance(host, PyroURI):
260
self.init(host.address, host.objectID, host.port, host.protocol)
262
# If the 'host' arg contains '://', assume it's an URI string.
263
if host.find('://')>0:
264
self.reinitFromString(host)
267
raise URIError('invalid URI format')
268
self.init(host, objectID, port, prtcol)
270
return self.protocol+'://'+self.address+':'+str(self.port)+'/'+self.objectID
272
return '<PyroURI \''+str(self)+'\'>'
274
# XXX this is handy but not safe. If the URI changes, the object will be in the wrong hash bucket.
275
return hash(str(self))
276
def __cmp__(self, o):
277
return cmp(str(self), str(o))
280
def init(self,host,objectID,port=0,prtcol='PYRO'):
282
raise URIError('malformed hostname')
283
if Pyro.config.PYRO_DNS_URI:
286
self.address=Pyro.protocol.getIPAddress(host)
288
raise URIError('unknown host')
290
if type(port)==type(1):
293
raise TypeError("port must be integer")
295
self.port=Pyro.config.PYRO_PORT
297
self.objectID=objectID
298
def reinitFromString(self,arg):
299
if arg.startswith('PYROLOC') or arg.startswith('PYRONAME'):
300
uri=processStringURI(arg)
301
self.init(uri.address,uri.objectID,uri.port,uri.protocol)
303
x=re.match(r'(?P<protocol>[^\s:/]+)://(?P<hostname>[^\s:]+):?(?P<port>\d+)?/(?P<id>\S*)',arg)
307
port=int(x.group('port'))
308
self.init(x.group('hostname'), x.group('id'), port, x.group('protocol'))
310
Log.error('PyroURI','invalid URI format passed: '+arg)
311
raise URIError('invalid URI format')
313
return DynamicProxy(self)
314
def getAttrProxy(self):
315
return DynamicProxyWithAttrs(self)
319
# This method takes a string representation of a Pyro URI
320
# and parses it. If it's a meta-protocol URI such as
321
# PYRONAME://.... it will do what is needed to make
322
# a regular PYRO:// URI out of it (resolve names etc).
324
def processStringURI(URI):
325
# PYRONAME(SSL)://[hostname[:port]/]objectname
326
x=re.match(r'(?P<protocol>PYRONAME|PYRONAMESSL)://(((?P<hostname>[^\s:]+):(?P<port>\d+)/)|((?P<onlyhostname>[^\s:]+)/))?(?P<name>\S*)',URI)
328
protocol=x.group('protocol')
329
if protocol=="PYRONAMESSL":
330
raise ProtocolError("NOT SUPPORTED YET: "+protocol) # XXX obviously, this should be implemented
331
hostname=x.group('hostname') or x.group('onlyhostname')
335
loc=Pyro.naming.NameServerLocator()
338
NS=loc.getNS(host=hostname,port=port)
339
return NS.resolve(name)
340
# PYROLOC(SSL)://hostname[:port]/objectname
341
x=re.match(r'(?P<protocol>PYROLOC|PYROLOCSSL)://(?P<hostname>[^\s:]+):?(?P<port>\d+)?/(?P<name>\S*)',URI)
343
protocol=x.group('protocol')
344
hostname=x.group('hostname')
351
return PyroURI(hostname,name,port,protocol)
352
if URI.startswith('PYROLOC') or URI.startswith('PYRONAME'):
353
# hmm should have matched above. Likely invalid.
354
raise URIError('invalid URI format')
355
# It's not a meta-protocol such as PYROLOC or PYRONAME,
356
# let the normal Pyro URI deal with it.
357
# (it can deal with regular PYRO: and PYROSSL: protocols)
361
#############################################################################
363
# DynamicProxy - dynamic Pyro proxy
365
# Can be used by clients to invoke objects for which they have no
368
#############################################################################
370
def getProxyForURI(URI):
371
return DynamicProxy(URI)
372
def getAttrProxyForURI(URI):
373
return DynamicProxyWithAttrs(URI)
376
# method call abstraction, adapted from Python's xmlrpclib
377
# it would be rather easy to add nested method calls, but
378
# that is not compatible with the way that Pyro's method
379
# calls are defined to work ( no nested calls )
380
def __init__(self, send, name):
383
def __call__(self, *args, **kwargs):
384
return self.__send(self.__name, args, kwargs)
387
def __init__(self, URI):
388
_checkInit() # init required
389
if type(URI) in (StringType,UnicodeType):
390
URI=processStringURI(URI)
392
self.objectID = URI.objectID
393
# Delay adapter binding to enable transporting of proxies.
394
# We just create an adapter, and don't connect it...
395
self.adapter = Pyro.protocol.getProtocolAdapter(self.URI.protocol)
396
# ---- don't forget to register local vars with DynamicProxyWithAttrs, see below
399
self.adapter.release(nolog=1)
400
except (AttributeError, RuntimeError):
402
def _setIdentification(self, ident):
403
self.adapter.setIdentification(ident)
404
def _setNewConnectionValidator(self, validator):
405
self.adapter.setNewConnectionValidator(validator)
406
def _setOneway(self, methods):
407
if type(methods) not in (type([]), type((0,))):
409
self.adapter.setOneway(methods)
410
def _setTimeout(self,timeout):
411
self.adapter.setTimeout(timeout)
412
def _transferThread(self, newOwnerThread=None):
413
pass # dummy function to retain API compatibility with Pyro 3.7
416
self.adapter.release()
418
return self.URI._local()
420
return self.URI._islocal()
421
def __copy__(self): # create copy of current proxy object
422
proxyCopy = DynamicProxy(self.URI)
423
proxyCopy.adapter.setIdentification(self.adapter.getIdentification(), munge=False) # copy identification info
424
proxyCopy._setTimeout(self.adapter.timeout)
425
proxyCopy._setOneway(self.adapter.onewayMethods)
426
proxyCopy._setNewConnectionValidator(self.adapter.getNewConnectionValidator())
428
def __deepcopy__(self, arg):
429
raise PyroError("cannot deepcopy a proxy")
430
def __getattr__(self, name):
431
if name=="__getinitargs__": # allows it to be safely pickled
432
raise AttributeError()
433
return _RemoteMethod(self._invokePYRO, name)
435
return "<"+self.__class__.__name__+" for "+str(self.URI)+">"
439
# makes it possible to use this class as a key in a dict
440
return hash(self.objectID)
441
def __eq__(self,other):
442
# makes it possible to compare two proxies using objectID
443
return hasattr(other,"objectID") and self.objectID==other.objectID
444
def __ne__(self,other):
445
# makes it possible to compare two proxies using objectID
446
return not hasattr(other,"objectID") or self.objectID!=other.objectID
447
def __nonzero__(self):
449
def __coerce__(self,other):
450
# makes it possible to compare two proxies using objectID (cmp)
451
if hasattr(other,"objectID"):
452
return (self.objectID, other.objectID)
455
def _invokePYRO(self, name, vargs, kargs):
456
if not self.adapter.connected():
457
# rebind here, don't do it from inside the remoteInvocation because deadlock will occur
458
self.adapter.bindToURI(self.URI)
459
return self.adapter.remoteInvocation(name, Pyro.constants.RIF_VarargsAndKeywords, vargs, kargs)
461
# Pickling support, otherwise pickle uses __getattr__:
462
def __getstate__(self):
463
# for pickling, return a non-connected copy of ourselves:
464
copy = self.__copy__()
467
def __setstate__(self, args):
468
# this appears to be necessary otherwise pickle won't work
472
class DynamicProxyWithAttrs(DynamicProxy):
473
def __init__(self, URI):
474
# first set the list of 'local' attrs for __setattr__
475
self.__dict__["_local_attrs"] = ("_local_attrs","URI", "objectID", "adapter", "_name", "_attr_cache")
476
self._attr_cache = {}
477
DynamicProxy.__init__(self, URI)
478
def _r_ga(self, attr, value=0):
480
return _RemoteMethod(self._invokePYRO, "_r_ga") (attr) # getattr
482
return _RemoteMethod(self._invokePYRO, "_r_ha") (attr) # hasattr
483
def findattr(self, attr):
484
if attr in self._attr_cache.keys():
485
return self._attr_cache[attr]
486
# look it up and cache the value
487
self._attr_cache[attr] = self._r_ga(attr)
488
return self._attr_cache[attr]
489
def __copy__(self): # create copy of current proxy object
490
return DynamicProxyWithAttrs(self.URI)
491
def __setattr__(self, attr, value):
492
if attr in self.__dict__["_local_attrs"]:
493
self.__dict__[attr]=value
495
result = self.findattr(attr)
496
if result==2: # attribute
497
return _RemoteMethod(self._invokePYRO, "_r_sa") (attr,value)
499
raise AttributeError('not an attribute')
500
def __getattr__(self, attr):
501
# allows it to be safely pickled
502
if attr not in ("__getinitargs__", "__hash__","__eq__","__ne__") and attr not in self.__dict__["_local_attrs"]:
503
result=self.findattr(attr)
504
if result==1: # method
505
return _RemoteMethod(self._invokePYRO, attr)
507
return self._r_ga(attr, 1)
511
#############################################################################
513
# Daemon - server-side Pyro daemon
515
# Accepts and dispatches incoming Pyro method calls.
517
#############################################################################
519
# The pyro object that represents the daemon.
520
# The daemon is not directly remotely accessible, for security reasons.
521
class DaemonServant(ObjBase):
522
def __init__(self, daemon):
523
ObjBase.__init__(self)
524
self.daemon=weakref.proxy(daemon)
525
def getRegistered(self):
526
return self.daemon.getRegistered()
527
def ResolvePYROLOC(self, name):
528
return self.daemon.ResolvePYROLOC(name)
531
class Daemon(Pyro.protocol.TCPServer, ObjBase):
532
def __init__(self,prtcol='PYRO',host=None,port=0,norange=0,publishhost=None):
533
ObjBase.__init__(self)
534
self.NameServer = None
536
_checkInit("server") # init required
537
self.setGUID(Pyro.constants.INTERNAL_DAEMON_GUID)
538
self.implementations={Pyro.constants.INTERNAL_DAEMON_GUID:(DaemonServant(self),'__PYRO_Internal_Daemon')}
539
self.persistentConnectedObjs=[] # guids
540
self.transientsCleanupAge=0
541
self.transientsMutex=Pyro.util.getLockObject()
542
self.nscallMutex=Pyro.util.getLockObject()
544
host=Pyro.config.PYRO_HOST
545
if publishhost is None:
546
publishhost=Pyro.config.PYRO_PUBLISHHOST
548
# Determine range scanning or random port allocation
550
# Fixed or random port allocation
551
# If port is zero, OS will randomly assign, otherwise,
552
# attempt to use the provided port value
556
# Scanning port allocation
560
self.port = Pyro.config.PYRO_PORT
561
portrange=Pyro.config.PYRO_PORT_RANGE
566
for i in range(portrange):
568
Pyro.protocol.TCPServer.__init__(self, self.port, host, Pyro.config.PYRO_MULTITHREADED,prtcol)
570
# If we bound to an OS provided port, report it
571
self.port = self.sock.getsockname()[1]
572
self.hostname = publishhost or Pyro.protocol.getHostname()
573
self.protocol = prtcol
574
self.adapter = Pyro.protocol.getProtocolAdapter(prtcol)
575
self.validateHostnameAndIP() # ignore any result message... it's in the log already.
577
except ProtocolError,msg:
580
Log.error('Daemon','Couldn\'t start Pyro daemon: ' +str(errormsg))
581
raise DaemonError('Couldn\'t start Pyro daemon: ' +str(errormsg))
583
# to be called to stop all connections and shut down.
584
def shutdown(self, disconnect=False):
585
Pyro.protocol.TCPServer.shutdown(self)
587
self.__disconnectObjects()
588
def __disconnectObjects(self):
589
# server shutting down, unregister all known objects in the NS
590
if self.NameServer and Pyro and Pyro.constants:
591
self.nscallMutex.acquire()
593
if Pyro.constants.INTERNAL_DAEMON_GUID in self.implementations:
594
del self.implementations[Pyro.constants.INTERNAL_DAEMON_GUID]
595
if self.implementations:
596
Log.warn('Daemon','Shutting down but there are still',len(self.implementations),'objects connected - disconnecting them')
597
for guid in self.implementations.keys():
598
if guid not in self.persistentConnectedObjs:
599
(obj,name)=self.implementations[guid]
602
self.NameServer.unregister(name)
604
Log.warn('Daemon','Error while unregistering object during shutdown:',x)
605
self.implementations={}
607
self.nscallMutex.release()
610
self.__disconnectObjects() # unregister objects
613
Pyro.protocol.TCPServer.__del__(self)
614
except (AttributeError, RuntimeError):
618
return '<Pyro Daemon on '+self.hostname+':'+str(self.port)+'>'
619
def __getstate__(self):
620
raise PicklingError('no access to the daemon')
622
def validateHostnameAndIP(self):
623
# Checks if hostname is sensible. Returns None if it is, otherwise a message
624
# telling what's wrong if it isn't too serious. If things are really bad,
625
# expect an exception to be raised. Things are logged too.
626
if not self.hostname:
627
Log.error("Daemon","no hostname known")
628
raise socket.error("no hostname known for daemon")
629
if self.hostname!="localhost":
630
ip = Pyro.protocol.getIPAddress(self.hostname)
632
Log.error("Daemon","no IP address known")
633
raise socket.error("no IP address known for daemon")
634
if not ip.startswith("127.0."):
635
return None # this is good!
636
# 127.0.x.x or 'localhost' is a warning situation!
637
msg="daemon bound on hostname that resolves to loopback address 127.0.x.x"
638
Log.warn("Daemon",msg)
639
Log.warn("Daemon","hostname="+self.hostname)
642
def useNameServer(self,NS):
644
def getNameServer(self):
645
return self.NameServer
646
def setTimeout(self, timeout):
647
self.adapter.setTimeout(timeout)
648
def setAllowedIdentifications(self, ids):
649
self.getNewConnectionValidator().setAllowedIdentifications(ids)
650
def setTransientsCleanupAge(self, secs):
651
self.transientsCleanupAge=secs
653
Log.msg('Daemon','creating Grim Reaper thread for transients, timeout=',secs)
654
reaper=threading.Thread(target=self._grimReaper)
655
reaper.setDaemon(1) # thread must exit at program termination.
657
def _grimReaper(self):
658
# this runs in a thread.
659
while self.transientsCleanupAge>0:
660
time.sleep(self.transientsCleanupAge/5)
661
self.reapUnusedTransients()
663
def getProxyForObj(self, obj):
664
return DynamicProxy( PyroURI(self.hostname,
665
obj.GUID(), prtcol=self.protocol, port=self.port) )
666
def getAttrProxyForObj(self, obj):
667
return DynamicProxyWithAttrs( PyroURI(self.hostname,
668
obj.GUID(), prtcol=self.protocol, port=self.port) )
670
def connectPersistent(self, obj, name=None):
671
# when a persistent entry is found in the NS, that URI is
672
# used instead of the supplied one, if the address matches.
673
if name and self.NameServer:
674
self.nscallMutex.acquire()
677
newURI = PyroURI(self.hostname, obj.GUID(), prtcol=self.protocol, port=self.port)
678
URI=self.NameServer.resolve(name)
679
if (URI.protocol,URI.address,URI.port)==(newURI.protocol,newURI.address,newURI.port):
680
# reuse the previous object ID
681
obj.setGUID(URI.objectID)
682
# enter the (object,name) in the known impl. dictionary
683
self.implementations[obj.GUID()]=(obj,name)
684
self.persistentConnectedObjs.append(obj.GUID())
685
obj.setPyroDaemon(self)
688
# name exists, but address etc. is wrong. Remove it.
689
# then continue so it wil be re-registered.
690
try: self.NameServer.unregister(name)
691
except NamingError: pass
695
self.nscallMutex.release()
697
self.persistentConnectedObjs.append(obj.GUID())
698
return self.connect(obj, name)
700
def connect(self, obj, name=None):
701
URI = PyroURI(self.hostname, obj.GUID(), prtcol=self.protocol, port=self.port)
702
# if not transient, register the object with the NS
704
self.nscallMutex.acquire()
707
self.NameServer.register(name, URI)
709
Log.warn('Daemon','connecting object without name server specified:',name)
711
self.nscallMutex.release()
712
# enter the (object,name) in the known implementations dictionary
713
self.implementations[obj.GUID()]=(obj,name)
714
obj.setPyroDaemon(self)
717
def disconnect(self,obj):
719
if self.NameServer and self.implementations[obj.GUID()][1]:
720
self.nscallMutex.acquire()
722
# only unregister with NS if it had a name (was not transient)
723
self.NameServer.unregister(self.implementations[obj.GUID()][1])
725
self.nscallMutex.release()
726
del self.implementations[obj.GUID()]
727
if obj.GUID() in self.persistentConnectedObjs:
728
self.persistentConnectedObjs.remove(obj.GUID())
729
# XXX Clean up connections/threads to this object?
730
# Can't be done because thread/socket is not associated with single object
732
obj.setPyroDaemon(None)
734
def getRegistered(self):
736
for guid in self.implementations.keys():
737
r[guid]=self.implementations[guid][1] # keep only the names
740
def handleInvocation(self, conn): # overridden from TCPServer
741
# called in both single- and multithreaded mode
742
self.getLocalStorage().caller=conn
743
self.getAdapter().handleInvocation(self, conn)
744
self.reapUnusedTransients()
746
def reapUnusedTransients(self):
747
if not self.transientsCleanupAge: return
749
self.transientsMutex.acquire()
751
for (obj,name) in self.implementations.values()[:]: # use copy of list
753
# object is transient, reap it if timeout requires so.
754
if (now-obj.lastUsed)>self.transientsCleanupAge:
758
self.transientsMutex.release()
760
def handleError(self,conn,onewaycall=False): # overridden from TCPServer
762
(exc_type, exc_value, exc_trb) = sys.exc_info()
763
if exc_type==ProtocolError:
764
# Problem with the communication protocol, shut down the connection
765
# XXX is shutting down what we want???
766
Log.error('Daemon','protocol error occured:',exc_value)
767
Log.error('Daemon','Due to network error: shutting down connection with',conn)
768
self.removeConnection(conn)
770
exclist = Pyro.util.formatTraceback(exc_type, exc_value, exc_trb)
771
out =''.join(exclist)
772
Log.warn('Daemon', 'Exception during processing of request from',
773
conn,' type',exc_type,
774
'\n--- traceback of this exception follows:\n',
775
out,'\n--- end of traceback')
776
if exc_type==PyroExceptionCapsule:
778
# This is a capsuled exception, used with callback objects.
779
# That means we are actually the daemon on the client.
780
# Return the error to the other side and raise exception locally once more.
781
# (with a normal exception, it is not raised locally again!)
782
# only send the exception object if it's not a oneway call.
784
self.adapter.returnException(conn,exc_value.excObj,0,exclist) # don't shutdown
787
# normal exception, only return exception object if it's not a oneway call
789
self.adapter.returnException(conn,exc_value,0,exclist) # don't shutdown connection
792
# clean up circular references to traceback info to allow proper GC
793
del exc_type, exc_value, exc_trb
795
def getAdapter(self):
796
# overridden from TCPServer
799
def getLocalObject(self, guid):
800
# return a local object registered with the given guid
801
return self.implementations[guid][0]
802
def getLocalObjectForProxy(self, proxy):
803
# return a local object registered with the guid to which the given proxy points
804
return self.implementations[proxy.objectID][0]
806
def ResolvePYROLOC(self, name):
807
# this gets called from the protocol adapter when
808
# it wants the daemon to resolve a local object name (PYROLOC: protocol)
809
Log.msg('Daemon','resolving PYROLOC name: ',name)
810
for o in self.implementations.keys():
811
if self.implementations[o][1]==name:
813
raise NamingError('no object found by this name',name)
816
#############################################################################
818
# Client/Server Init code
820
#############################################################################
822
# Has init been performed already?
827
def _initGeneric_pre():
828
global _init_generic_done
829
if _init_generic_done:
831
if Pyro.config.PYRO_TRACELEVEL == 0: return
833
out='\n'+'-'*60+' NEW SESSION\n'+time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))+ \
834
' Pyro Initializing, version '+Pyro.constants.VERSION+'\n'
837
sys.stderr.write('PYRO: Can\'t write the tracefile '+Pyro.config.PYRO_LOGFILE+'\n'+str(e))
839
def _initGeneric_post():
840
global _init_generic_done
841
setattr(Pyro.config, Pyro.constants.CFGITEM_PYRO_INITIALIZED,1)
842
if Pyro.config.PYRO_TRACELEVEL == 0: return
844
if not _init_generic_done:
845
out='Configuration settings are as follows:\n'
846
for item in dir(Pyro.config):
847
if item[0:4] =='PYRO':
848
out+=item+' = '+str(Pyro.config.__dict__[item])+'\n'
850
Log.raw('Init done.\n'+'-'*70+'\n')
856
def initClient(banner=0):
857
global _init_client_done
858
if _init_client_done: return
860
if Pyro.config.PYRO_TRACELEVEL >0: Log.raw('This is initClient.\n')
861
Pyro.config.finalizeConfig_Client()
864
print 'Pyro Client Initialized. Using Pyro V'+Pyro.constants.VERSION
867
def initServer(banner=0, storageCheck=1):
868
global _init_server_done
869
if _init_server_done: return
871
if Pyro.config.PYRO_TRACELEVEL >0: Log.raw('This is initServer.\n')
872
Pyro.config.finalizeConfig_Server(storageCheck=storageCheck)
875
print 'Pyro Server Initialized. Using Pyro V'+Pyro.constants.VERSION
879
if __name__=="__main__":
880
print "Pyro version:",Pyro.constants.VERSION