/Lib/multiprocessing/managers.py

http://unladen-swallow.googlecode.com/ · Python · 1083 lines · 719 code · 159 blank · 205 comment · 130 complexity · 2431fe24a34b11ad242cb9fcd9adfbdd MD5 · raw file

  1. #
  2. # Module providing the `SyncManager` class for dealing
  3. # with shared objects
  4. #
  5. # multiprocessing/managers.py
  6. #
  7. # Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
  8. #
  9. __all__ = [ 'BaseManager', 'SyncManager', 'BaseProxy', 'Token' ]
  10. #
  11. # Imports
  12. #
  13. import os
  14. import sys
  15. import weakref
  16. import threading
  17. import array
  18. import Queue
  19. from traceback import format_exc
  20. from multiprocessing import Process, current_process, active_children, Pool, util, connection
  21. from multiprocessing.process import AuthenticationString
  22. from multiprocessing.forking import exit, Popen, assert_spawning, ForkingPickler
  23. from multiprocessing.util import Finalize, info
  24. try:
  25. from cPickle import PicklingError
  26. except ImportError:
  27. from pickle import PicklingError
  28. #
  29. # Register some things for pickling
  30. #
  31. def reduce_array(a):
  32. return array.array, (a.typecode, a.tostring())
  33. ForkingPickler.register(array.array, reduce_array)
  34. view_types = [type(getattr({}, name)()) for name in ('items','keys','values')]
  35. #
  36. # Type for identifying shared objects
  37. #
  38. class Token(object):
  39. '''
  40. Type to uniquely indentify a shared object
  41. '''
  42. __slots__ = ('typeid', 'address', 'id')
  43. def __init__(self, typeid, address, id):
  44. (self.typeid, self.address, self.id) = (typeid, address, id)
  45. def __getstate__(self):
  46. return (self.typeid, self.address, self.id)
  47. def __setstate__(self, state):
  48. (self.typeid, self.address, self.id) = state
  49. def __repr__(self):
  50. return 'Token(typeid=%r, address=%r, id=%r)' % \
  51. (self.typeid, self.address, self.id)
  52. #
  53. # Function for communication with a manager's server process
  54. #
  55. def dispatch(c, id, methodname, args=(), kwds={}):
  56. '''
  57. Send a message to manager using connection `c` and return response
  58. '''
  59. c.send((id, methodname, args, kwds))
  60. kind, result = c.recv()
  61. if kind == '#RETURN':
  62. return result
  63. raise convert_to_error(kind, result)
  64. def convert_to_error(kind, result):
  65. if kind == '#ERROR':
  66. return result
  67. elif kind == '#TRACEBACK':
  68. assert type(result) is str
  69. return RemoteError(result)
  70. elif kind == '#UNSERIALIZABLE':
  71. assert type(result) is str
  72. return RemoteError('Unserializable message: %s\n' % result)
  73. else:
  74. return ValueError('Unrecognized message type')
  75. class RemoteError(Exception):
  76. def __str__(self):
  77. return ('\n' + '-'*75 + '\n' + str(self.args[0]) + '-'*75)
  78. #
  79. # Functions for finding the method names of an object
  80. #
  81. def all_methods(obj):
  82. '''
  83. Return a list of names of methods of `obj`
  84. '''
  85. temp = []
  86. for name in dir(obj):
  87. func = getattr(obj, name)
  88. if hasattr(func, '__call__'):
  89. temp.append(name)
  90. return temp
  91. def public_methods(obj):
  92. '''
  93. Return a list of names of methods of `obj` which do not start with '_'
  94. '''
  95. return [name for name in all_methods(obj) if name[0] != '_']
  96. #
  97. # Server which is run in a process controlled by a manager
  98. #
  99. class Server(object):
  100. '''
  101. Server class which runs in a process controlled by a manager object
  102. '''
  103. public = ['shutdown', 'create', 'accept_connection', 'get_methods',
  104. 'debug_info', 'number_of_objects', 'dummy', 'incref', 'decref']
  105. def __init__(self, registry, address, authkey, serializer):
  106. assert isinstance(authkey, bytes)
  107. self.registry = registry
  108. self.authkey = AuthenticationString(authkey)
  109. Listener, Client = listener_client[serializer]
  110. # do authentication later
  111. self.listener = Listener(address=address, backlog=5)
  112. self.address = self.listener.address
  113. self.id_to_obj = {0: (None, ())}
  114. self.id_to_refcount = {}
  115. self.mutex = threading.RLock()
  116. self.stop = 0
  117. def serve_forever(self):
  118. '''
  119. Run the server forever
  120. '''
  121. current_process()._manager_server = self
  122. try:
  123. try:
  124. while 1:
  125. try:
  126. c = self.listener.accept()
  127. except (OSError, IOError):
  128. continue
  129. t = threading.Thread(target=self.handle_request, args=(c,))
  130. t.daemon = True
  131. t.start()
  132. except (KeyboardInterrupt, SystemExit):
  133. pass
  134. finally:
  135. self.stop = 999
  136. self.listener.close()
  137. def handle_request(self, c):
  138. '''
  139. Handle a new connection
  140. '''
  141. funcname = result = request = None
  142. try:
  143. connection.deliver_challenge(c, self.authkey)
  144. connection.answer_challenge(c, self.authkey)
  145. request = c.recv()
  146. ignore, funcname, args, kwds = request
  147. assert funcname in self.public, '%r unrecognized' % funcname
  148. func = getattr(self, funcname)
  149. except Exception:
  150. msg = ('#TRACEBACK', format_exc())
  151. else:
  152. try:
  153. result = func(c, *args, **kwds)
  154. except Exception:
  155. msg = ('#TRACEBACK', format_exc())
  156. else:
  157. msg = ('#RETURN', result)
  158. try:
  159. c.send(msg)
  160. except Exception, e:
  161. try:
  162. c.send(('#TRACEBACK', format_exc()))
  163. except Exception:
  164. pass
  165. util.info('Failure to send message: %r', msg)
  166. util.info(' ... request was %r', request)
  167. util.info(' ... exception was %r', e)
  168. c.close()
  169. def serve_client(self, conn):
  170. '''
  171. Handle requests from the proxies in a particular process/thread
  172. '''
  173. util.debug('starting server thread to service %r',
  174. threading.current_thread().name)
  175. recv = conn.recv
  176. send = conn.send
  177. id_to_obj = self.id_to_obj
  178. while not self.stop:
  179. try:
  180. methodname = obj = None
  181. request = recv()
  182. ident, methodname, args, kwds = request
  183. obj, exposed, gettypeid = id_to_obj[ident]
  184. if methodname not in exposed:
  185. raise AttributeError(
  186. 'method %r of %r object is not in exposed=%r' %
  187. (methodname, type(obj), exposed)
  188. )
  189. function = getattr(obj, methodname)
  190. try:
  191. res = function(*args, **kwds)
  192. except Exception, e:
  193. msg = ('#ERROR', e)
  194. else:
  195. typeid = gettypeid and gettypeid.get(methodname, None)
  196. if typeid:
  197. rident, rexposed = self.create(conn, typeid, res)
  198. token = Token(typeid, self.address, rident)
  199. msg = ('#PROXY', (rexposed, token))
  200. else:
  201. msg = ('#RETURN', res)
  202. except AttributeError:
  203. if methodname is None:
  204. msg = ('#TRACEBACK', format_exc())
  205. else:
  206. try:
  207. fallback_func = self.fallback_mapping[methodname]
  208. result = fallback_func(
  209. self, conn, ident, obj, *args, **kwds
  210. )
  211. msg = ('#RETURN', result)
  212. except Exception:
  213. msg = ('#TRACEBACK', format_exc())
  214. except EOFError:
  215. util.debug('got EOF -- exiting thread serving %r',
  216. threading.current_thread().name)
  217. sys.exit(0)
  218. except Exception:
  219. msg = ('#TRACEBACK', format_exc())
  220. try:
  221. try:
  222. send(msg)
  223. except Exception, e:
  224. send(('#UNSERIALIZABLE', repr(msg)))
  225. except Exception, e:
  226. util.info('exception in thread serving %r',
  227. threading.current_thread().name)
  228. util.info(' ... message was %r', msg)
  229. util.info(' ... exception was %r', e)
  230. conn.close()
  231. sys.exit(1)
  232. def fallback_getvalue(self, conn, ident, obj):
  233. return obj
  234. def fallback_str(self, conn, ident, obj):
  235. return str(obj)
  236. def fallback_repr(self, conn, ident, obj):
  237. return repr(obj)
  238. fallback_mapping = {
  239. '__str__':fallback_str,
  240. '__repr__':fallback_repr,
  241. '#GETVALUE':fallback_getvalue
  242. }
  243. def dummy(self, c):
  244. pass
  245. def debug_info(self, c):
  246. '''
  247. Return some info --- useful to spot problems with refcounting
  248. '''
  249. self.mutex.acquire()
  250. try:
  251. result = []
  252. keys = self.id_to_obj.keys()
  253. keys.sort()
  254. for ident in keys:
  255. if ident != 0:
  256. result.append(' %s: refcount=%s\n %s' %
  257. (ident, self.id_to_refcount[ident],
  258. str(self.id_to_obj[ident][0])[:75]))
  259. return '\n'.join(result)
  260. finally:
  261. self.mutex.release()
  262. def number_of_objects(self, c):
  263. '''
  264. Number of shared objects
  265. '''
  266. return len(self.id_to_obj) - 1 # don't count ident=0
  267. def shutdown(self, c):
  268. '''
  269. Shutdown this process
  270. '''
  271. try:
  272. try:
  273. util.debug('manager received shutdown message')
  274. c.send(('#RETURN', None))
  275. if sys.stdout != sys.__stdout__:
  276. util.debug('resetting stdout, stderr')
  277. sys.stdout = sys.__stdout__
  278. sys.stderr = sys.__stderr__
  279. util._run_finalizers(0)
  280. for p in active_children():
  281. util.debug('terminating a child process of manager')
  282. p.terminate()
  283. for p in active_children():
  284. util.debug('terminating a child process of manager')
  285. p.join()
  286. util._run_finalizers()
  287. util.info('manager exiting with exitcode 0')
  288. except:
  289. import traceback
  290. traceback.print_exc()
  291. finally:
  292. exit(0)
  293. def create(self, c, typeid, *args, **kwds):
  294. '''
  295. Create a new shared object and return its id
  296. '''
  297. self.mutex.acquire()
  298. try:
  299. callable, exposed, method_to_typeid, proxytype = \
  300. self.registry[typeid]
  301. if callable is None:
  302. assert len(args) == 1 and not kwds
  303. obj = args[0]
  304. else:
  305. obj = callable(*args, **kwds)
  306. if exposed is None:
  307. exposed = public_methods(obj)
  308. if method_to_typeid is not None:
  309. assert type(method_to_typeid) is dict
  310. exposed = list(exposed) + list(method_to_typeid)
  311. ident = '%x' % id(obj) # convert to string because xmlrpclib
  312. # only has 32 bit signed integers
  313. util.debug('%r callable returned object with id %r', typeid, ident)
  314. self.id_to_obj[ident] = (obj, set(exposed), method_to_typeid)
  315. if ident not in self.id_to_refcount:
  316. self.id_to_refcount[ident] = 0
  317. # increment the reference count immediately, to avoid
  318. # this object being garbage collected before a Proxy
  319. # object for it can be created. The caller of create()
  320. # is responsible for doing a decref once the Proxy object
  321. # has been created.
  322. self.incref(c, ident)
  323. return ident, tuple(exposed)
  324. finally:
  325. self.mutex.release()
  326. def get_methods(self, c, token):
  327. '''
  328. Return the methods of the shared object indicated by token
  329. '''
  330. return tuple(self.id_to_obj[token.id][1])
  331. def accept_connection(self, c, name):
  332. '''
  333. Spawn a new thread to serve this connection
  334. '''
  335. threading.current_thread().name = name
  336. c.send(('#RETURN', None))
  337. self.serve_client(c)
  338. def incref(self, c, ident):
  339. self.mutex.acquire()
  340. try:
  341. self.id_to_refcount[ident] += 1
  342. finally:
  343. self.mutex.release()
  344. def decref(self, c, ident):
  345. self.mutex.acquire()
  346. try:
  347. assert self.id_to_refcount[ident] >= 1
  348. self.id_to_refcount[ident] -= 1
  349. if self.id_to_refcount[ident] == 0:
  350. del self.id_to_obj[ident], self.id_to_refcount[ident]
  351. util.debug('disposing of obj with id %r', ident)
  352. finally:
  353. self.mutex.release()
  354. #
  355. # Class to represent state of a manager
  356. #
  357. class State(object):
  358. __slots__ = ['value']
  359. INITIAL = 0
  360. STARTED = 1
  361. SHUTDOWN = 2
  362. #
  363. # Mapping from serializer name to Listener and Client types
  364. #
  365. listener_client = {
  366. 'pickle' : (connection.Listener, connection.Client),
  367. 'xmlrpclib' : (connection.XmlListener, connection.XmlClient)
  368. }
  369. #
  370. # Definition of BaseManager
  371. #
  372. class BaseManager(object):
  373. '''
  374. Base class for managers
  375. '''
  376. _registry = {}
  377. _Server = Server
  378. def __init__(self, address=None, authkey=None, serializer='pickle'):
  379. if authkey is None:
  380. authkey = current_process().authkey
  381. self._address = address # XXX not final address if eg ('', 0)
  382. self._authkey = AuthenticationString(authkey)
  383. self._state = State()
  384. self._state.value = State.INITIAL
  385. self._serializer = serializer
  386. self._Listener, self._Client = listener_client[serializer]
  387. def __reduce__(self):
  388. return type(self).from_address, \
  389. (self._address, self._authkey, self._serializer)
  390. def get_server(self):
  391. '''
  392. Return server object with serve_forever() method and address attribute
  393. '''
  394. assert self._state.value == State.INITIAL
  395. return Server(self._registry, self._address,
  396. self._authkey, self._serializer)
  397. def connect(self):
  398. '''
  399. Connect manager object to the server process
  400. '''
  401. Listener, Client = listener_client[self._serializer]
  402. conn = Client(self._address, authkey=self._authkey)
  403. dispatch(conn, None, 'dummy')
  404. self._state.value = State.STARTED
  405. def start(self):
  406. '''
  407. Spawn a server process for this manager object
  408. '''
  409. assert self._state.value == State.INITIAL
  410. # pipe over which we will retrieve address of server
  411. reader, writer = connection.Pipe(duplex=False)
  412. # spawn process which runs a server
  413. self._process = Process(
  414. target=type(self)._run_server,
  415. args=(self._registry, self._address, self._authkey,
  416. self._serializer, writer),
  417. )
  418. ident = ':'.join(str(i) for i in self._process._identity)
  419. self._process.name = type(self).__name__ + '-' + ident
  420. self._process.start()
  421. # get address of server
  422. writer.close()
  423. self._address = reader.recv()
  424. reader.close()
  425. # register a finalizer
  426. self._state.value = State.STARTED
  427. self.shutdown = util.Finalize(
  428. self, type(self)._finalize_manager,
  429. args=(self._process, self._address, self._authkey,
  430. self._state, self._Client),
  431. exitpriority=0
  432. )
  433. @classmethod
  434. def _run_server(cls, registry, address, authkey, serializer, writer):
  435. '''
  436. Create a server, report its address and run it
  437. '''
  438. # create server
  439. server = cls._Server(registry, address, authkey, serializer)
  440. # inform parent process of the server's address
  441. writer.send(server.address)
  442. writer.close()
  443. # run the manager
  444. util.info('manager serving at %r', server.address)
  445. server.serve_forever()
  446. def _create(self, typeid, *args, **kwds):
  447. '''
  448. Create a new shared object; return the token and exposed tuple
  449. '''
  450. assert self._state.value == State.STARTED, 'server not yet started'
  451. conn = self._Client(self._address, authkey=self._authkey)
  452. try:
  453. id, exposed = dispatch(conn, None, 'create', (typeid,)+args, kwds)
  454. finally:
  455. conn.close()
  456. return Token(typeid, self._address, id), exposed
  457. def join(self, timeout=None):
  458. '''
  459. Join the manager process (if it has been spawned)
  460. '''
  461. self._process.join(timeout)
  462. def _debug_info(self):
  463. '''
  464. Return some info about the servers shared objects and connections
  465. '''
  466. conn = self._Client(self._address, authkey=self._authkey)
  467. try:
  468. return dispatch(conn, None, 'debug_info')
  469. finally:
  470. conn.close()
  471. def _number_of_objects(self):
  472. '''
  473. Return the number of shared objects
  474. '''
  475. conn = self._Client(self._address, authkey=self._authkey)
  476. try:
  477. return dispatch(conn, None, 'number_of_objects')
  478. finally:
  479. conn.close()
  480. def __enter__(self):
  481. return self
  482. def __exit__(self, exc_type, exc_val, exc_tb):
  483. self.shutdown()
  484. @staticmethod
  485. def _finalize_manager(process, address, authkey, state, _Client):
  486. '''
  487. Shutdown the manager process; will be registered as a finalizer
  488. '''
  489. if process.is_alive():
  490. util.info('sending shutdown message to manager')
  491. try:
  492. conn = _Client(address, authkey=authkey)
  493. try:
  494. dispatch(conn, None, 'shutdown')
  495. finally:
  496. conn.close()
  497. except Exception:
  498. pass
  499. process.join(timeout=0.2)
  500. if process.is_alive():
  501. util.info('manager still alive')
  502. if hasattr(process, 'terminate'):
  503. util.info('trying to `terminate()` manager process')
  504. process.terminate()
  505. process.join(timeout=0.1)
  506. if process.is_alive():
  507. util.info('manager still alive after terminate')
  508. state.value = State.SHUTDOWN
  509. try:
  510. del BaseProxy._address_to_local[address]
  511. except KeyError:
  512. pass
  513. address = property(lambda self: self._address)
  514. @classmethod
  515. def register(cls, typeid, callable=None, proxytype=None, exposed=None,
  516. method_to_typeid=None, create_method=True):
  517. '''
  518. Register a typeid with the manager type
  519. '''
  520. if '_registry' not in cls.__dict__:
  521. cls._registry = cls._registry.copy()
  522. if proxytype is None:
  523. proxytype = AutoProxy
  524. exposed = exposed or getattr(proxytype, '_exposed_', None)
  525. method_to_typeid = method_to_typeid or \
  526. getattr(proxytype, '_method_to_typeid_', None)
  527. if method_to_typeid:
  528. for key, value in method_to_typeid.items():
  529. assert type(key) is str, '%r is not a string' % key
  530. assert type(value) is str, '%r is not a string' % value
  531. cls._registry[typeid] = (
  532. callable, exposed, method_to_typeid, proxytype
  533. )
  534. if create_method:
  535. def temp(self, *args, **kwds):
  536. util.debug('requesting creation of a shared %r object', typeid)
  537. token, exp = self._create(typeid, *args, **kwds)
  538. proxy = proxytype(
  539. token, self._serializer, manager=self,
  540. authkey=self._authkey, exposed=exp
  541. )
  542. conn = self._Client(token.address, authkey=self._authkey)
  543. dispatch(conn, None, 'decref', (token.id,))
  544. return proxy
  545. temp.__name__ = typeid
  546. setattr(cls, typeid, temp)
  547. #
  548. # Subclass of set which get cleared after a fork
  549. #
  550. class ProcessLocalSet(set):
  551. def __init__(self):
  552. util.register_after_fork(self, lambda obj: obj.clear())
  553. def __reduce__(self):
  554. return type(self), ()
  555. #
  556. # Definition of BaseProxy
  557. #
  558. class BaseProxy(object):
  559. '''
  560. A base for proxies of shared objects
  561. '''
  562. _address_to_local = {}
  563. _mutex = util.ForkAwareThreadLock()
  564. def __init__(self, token, serializer, manager=None,
  565. authkey=None, exposed=None, incref=True):
  566. BaseProxy._mutex.acquire()
  567. try:
  568. tls_idset = BaseProxy._address_to_local.get(token.address, None)
  569. if tls_idset is None:
  570. tls_idset = util.ForkAwareLocal(), ProcessLocalSet()
  571. BaseProxy._address_to_local[token.address] = tls_idset
  572. finally:
  573. BaseProxy._mutex.release()
  574. # self._tls is used to record the connection used by this
  575. # thread to communicate with the manager at token.address
  576. self._tls = tls_idset[0]
  577. # self._idset is used to record the identities of all shared
  578. # objects for which the current process owns references and
  579. # which are in the manager at token.address
  580. self._idset = tls_idset[1]
  581. self._token = token
  582. self._id = self._token.id
  583. self._manager = manager
  584. self._serializer = serializer
  585. self._Client = listener_client[serializer][1]
  586. if authkey is not None:
  587. self._authkey = AuthenticationString(authkey)
  588. elif self._manager is not None:
  589. self._authkey = self._manager._authkey
  590. else:
  591. self._authkey = current_process().authkey
  592. if incref:
  593. self._incref()
  594. util.register_after_fork(self, BaseProxy._after_fork)
  595. def _connect(self):
  596. util.debug('making connection to manager')
  597. name = current_process().name
  598. if threading.current_thread().name != 'MainThread':
  599. name += '|' + threading.current_thread().name
  600. conn = self._Client(self._token.address, authkey=self._authkey)
  601. dispatch(conn, None, 'accept_connection', (name,))
  602. self._tls.connection = conn
  603. def _callmethod(self, methodname, args=(), kwds={}):
  604. '''
  605. Try to call a method of the referrent and return a copy of the result
  606. '''
  607. try:
  608. conn = self._tls.connection
  609. except AttributeError:
  610. util.debug('thread %r does not own a connection',
  611. threading.current_thread().name)
  612. self._connect()
  613. conn = self._tls.connection
  614. conn.send((self._id, methodname, args, kwds))
  615. kind, result = conn.recv()
  616. if kind == '#RETURN':
  617. return result
  618. elif kind == '#PROXY':
  619. exposed, token = result
  620. proxytype = self._manager._registry[token.typeid][-1]
  621. proxy = proxytype(
  622. token, self._serializer, manager=self._manager,
  623. authkey=self._authkey, exposed=exposed
  624. )
  625. conn = self._Client(token.address, authkey=self._authkey)
  626. dispatch(conn, None, 'decref', (token.id,))
  627. return proxy
  628. raise convert_to_error(kind, result)
  629. def _getvalue(self):
  630. '''
  631. Get a copy of the value of the referent
  632. '''
  633. return self._callmethod('#GETVALUE')
  634. def _incref(self):
  635. conn = self._Client(self._token.address, authkey=self._authkey)
  636. dispatch(conn, None, 'incref', (self._id,))
  637. util.debug('INCREF %r', self._token.id)
  638. self._idset.add(self._id)
  639. state = self._manager and self._manager._state
  640. self._close = util.Finalize(
  641. self, BaseProxy._decref,
  642. args=(self._token, self._authkey, state,
  643. self._tls, self._idset, self._Client),
  644. exitpriority=10
  645. )
  646. @staticmethod
  647. def _decref(token, authkey, state, tls, idset, _Client):
  648. idset.discard(token.id)
  649. # check whether manager is still alive
  650. if state is None or state.value == State.STARTED:
  651. # tell manager this process no longer cares about referent
  652. try:
  653. util.debug('DECREF %r', token.id)
  654. conn = _Client(token.address, authkey=authkey)
  655. dispatch(conn, None, 'decref', (token.id,))
  656. except Exception, e:
  657. util.debug('... decref failed %s', e)
  658. else:
  659. util.debug('DECREF %r -- manager already shutdown', token.id)
  660. # check whether we can close this thread's connection because
  661. # the process owns no more references to objects for this manager
  662. if not idset and hasattr(tls, 'connection'):
  663. util.debug('thread %r has no more proxies so closing conn',
  664. threading.current_thread().name)
  665. tls.connection.close()
  666. del tls.connection
  667. def _after_fork(self):
  668. self._manager = None
  669. try:
  670. self._incref()
  671. except Exception, e:
  672. # the proxy may just be for a manager which has shutdown
  673. util.info('incref failed: %s' % e)
  674. def __reduce__(self):
  675. kwds = {}
  676. if Popen.thread_is_spawning():
  677. kwds['authkey'] = self._authkey
  678. if getattr(self, '_isauto', False):
  679. kwds['exposed'] = self._exposed_
  680. return (RebuildProxy,
  681. (AutoProxy, self._token, self._serializer, kwds))
  682. else:
  683. return (RebuildProxy,
  684. (type(self), self._token, self._serializer, kwds))
  685. def __deepcopy__(self, memo):
  686. return self._getvalue()
  687. def __repr__(self):
  688. return '<%s object, typeid %r at %s>' % \
  689. (type(self).__name__, self._token.typeid, '0x%x' % id(self))
  690. def __str__(self):
  691. '''
  692. Return representation of the referent (or a fall-back if that fails)
  693. '''
  694. try:
  695. return self._callmethod('__repr__')
  696. except Exception:
  697. return repr(self)[:-1] + "; '__str__()' failed>"
  698. #
  699. # Function used for unpickling
  700. #
  701. def RebuildProxy(func, token, serializer, kwds):
  702. '''
  703. Function used for unpickling proxy objects.
  704. If possible the shared object is returned, or otherwise a proxy for it.
  705. '''
  706. server = getattr(current_process(), '_manager_server', None)
  707. if server and server.address == token.address:
  708. return server.id_to_obj[token.id][0]
  709. else:
  710. incref = (
  711. kwds.pop('incref', True) and
  712. not getattr(current_process(), '_inheriting', False)
  713. )
  714. return func(token, serializer, incref=incref, **kwds)
  715. #
  716. # Functions to create proxies and proxy types
  717. #
  718. def MakeProxyType(name, exposed, _cache={}):
  719. '''
  720. Return an proxy type whose methods are given by `exposed`
  721. '''
  722. exposed = tuple(exposed)
  723. try:
  724. return _cache[(name, exposed)]
  725. except KeyError:
  726. pass
  727. dic = {}
  728. for meth in exposed:
  729. exec '''def %s(self, *args, **kwds):
  730. return self._callmethod(%r, args, kwds)''' % (meth, meth) in dic
  731. ProxyType = type(name, (BaseProxy,), dic)
  732. ProxyType._exposed_ = exposed
  733. _cache[(name, exposed)] = ProxyType
  734. return ProxyType
  735. def AutoProxy(token, serializer, manager=None, authkey=None,
  736. exposed=None, incref=True):
  737. '''
  738. Return an auto-proxy for `token`
  739. '''
  740. _Client = listener_client[serializer][1]
  741. if exposed is None:
  742. conn = _Client(token.address, authkey=authkey)
  743. try:
  744. exposed = dispatch(conn, None, 'get_methods', (token,))
  745. finally:
  746. conn.close()
  747. if authkey is None and manager is not None:
  748. authkey = manager._authkey
  749. if authkey is None:
  750. authkey = current_process().authkey
  751. ProxyType = MakeProxyType('AutoProxy[%s]' % token.typeid, exposed)
  752. proxy = ProxyType(token, serializer, manager=manager, authkey=authkey,
  753. incref=incref)
  754. proxy._isauto = True
  755. return proxy
  756. #
  757. # Types/callables which we will register with SyncManager
  758. #
  759. class Namespace(object):
  760. def __init__(self, **kwds):
  761. self.__dict__.update(kwds)
  762. def __repr__(self):
  763. items = self.__dict__.items()
  764. temp = []
  765. for name, value in items:
  766. if not name.startswith('_'):
  767. temp.append('%s=%r' % (name, value))
  768. temp.sort()
  769. return 'Namespace(%s)' % str.join(', ', temp)
  770. class Value(object):
  771. def __init__(self, typecode, value, lock=True):
  772. self._typecode = typecode
  773. self._value = value
  774. def get(self):
  775. return self._value
  776. def set(self, value):
  777. self._value = value
  778. def __repr__(self):
  779. return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value)
  780. value = property(get, set)
  781. def Array(typecode, sequence, lock=True):
  782. return array.array(typecode, sequence)
  783. #
  784. # Proxy types used by SyncManager
  785. #
  786. class IteratorProxy(BaseProxy):
  787. # XXX remove methods for Py3.0 and Py2.6
  788. _exposed_ = ('__next__', 'next', 'send', 'throw', 'close')
  789. def __iter__(self):
  790. return self
  791. def __next__(self, *args):
  792. return self._callmethod('__next__', args)
  793. def next(self, *args):
  794. return self._callmethod('next', args)
  795. def send(self, *args):
  796. return self._callmethod('send', args)
  797. def throw(self, *args):
  798. return self._callmethod('throw', args)
  799. def close(self, *args):
  800. return self._callmethod('close', args)
  801. class AcquirerProxy(BaseProxy):
  802. _exposed_ = ('acquire', 'release')
  803. def acquire(self, blocking=True):
  804. return self._callmethod('acquire', (blocking,))
  805. def release(self):
  806. return self._callmethod('release')
  807. def __enter__(self):
  808. return self._callmethod('acquire')
  809. def __exit__(self, exc_type, exc_val, exc_tb):
  810. return self._callmethod('release')
  811. class ConditionProxy(AcquirerProxy):
  812. # XXX will Condition.notfyAll() name be available in Py3.0?
  813. _exposed_ = ('acquire', 'release', 'wait', 'notify', 'notify_all')
  814. def wait(self, timeout=None):
  815. return self._callmethod('wait', (timeout,))
  816. def notify(self):
  817. return self._callmethod('notify')
  818. def notify_all(self):
  819. return self._callmethod('notify_all')
  820. class EventProxy(BaseProxy):
  821. _exposed_ = ('is_set', 'set', 'clear', 'wait')
  822. def is_set(self):
  823. return self._callmethod('is_set')
  824. def set(self):
  825. return self._callmethod('set')
  826. def clear(self):
  827. return self._callmethod('clear')
  828. def wait(self, timeout=None):
  829. return self._callmethod('wait', (timeout,))
  830. class NamespaceProxy(BaseProxy):
  831. _exposed_ = ('__getattribute__', '__setattr__', '__delattr__')
  832. def __getattr__(self, key):
  833. if key[0] == '_':
  834. return object.__getattribute__(self, key)
  835. callmethod = object.__getattribute__(self, '_callmethod')
  836. return callmethod('__getattribute__', (key,))
  837. def __setattr__(self, key, value):
  838. if key[0] == '_':
  839. return object.__setattr__(self, key, value)
  840. callmethod = object.__getattribute__(self, '_callmethod')
  841. return callmethod('__setattr__', (key, value))
  842. def __delattr__(self, key):
  843. if key[0] == '_':
  844. return object.__delattr__(self, key)
  845. callmethod = object.__getattribute__(self, '_callmethod')
  846. return callmethod('__delattr__', (key,))
  847. class ValueProxy(BaseProxy):
  848. _exposed_ = ('get', 'set')
  849. def get(self):
  850. return self._callmethod('get')
  851. def set(self, value):
  852. return self._callmethod('set', (value,))
  853. value = property(get, set)
  854. BaseListProxy = MakeProxyType('BaseListProxy', (
  855. '__add__', '__contains__', '__delitem__', '__delslice__',
  856. '__getitem__', '__getslice__', '__len__', '__mul__',
  857. '__reversed__', '__rmul__', '__setitem__', '__setslice__',
  858. 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove',
  859. 'reverse', 'sort', '__imul__'
  860. )) # XXX __getslice__ and __setslice__ unneeded in Py3.0
  861. class ListProxy(BaseListProxy):
  862. def __iadd__(self, value):
  863. self._callmethod('extend', (value,))
  864. return self
  865. def __imul__(self, value):
  866. self._callmethod('__imul__', (value,))
  867. return self
  868. DictProxy = MakeProxyType('DictProxy', (
  869. '__contains__', '__delitem__', '__getitem__', '__len__',
  870. '__setitem__', 'clear', 'copy', 'get', 'has_key', 'items',
  871. 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'
  872. ))
  873. ArrayProxy = MakeProxyType('ArrayProxy', (
  874. '__len__', '__getitem__', '__setitem__', '__getslice__', '__setslice__'
  875. )) # XXX __getslice__ and __setslice__ unneeded in Py3.0
  876. PoolProxy = MakeProxyType('PoolProxy', (
  877. 'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join',
  878. 'map', 'map_async', 'terminate'
  879. ))
  880. PoolProxy._method_to_typeid_ = {
  881. 'apply_async': 'AsyncResult',
  882. 'map_async': 'AsyncResult',
  883. 'imap': 'Iterator',
  884. 'imap_unordered': 'Iterator'
  885. }
  886. #
  887. # Definition of SyncManager
  888. #
  889. class SyncManager(BaseManager):
  890. '''
  891. Subclass of `BaseManager` which supports a number of shared object types.
  892. The types registered are those intended for the synchronization
  893. of threads, plus `dict`, `list` and `Namespace`.
  894. The `multiprocessing.Manager()` function creates started instances of
  895. this class.
  896. '''
  897. SyncManager.register('Queue', Queue.Queue)
  898. SyncManager.register('JoinableQueue', Queue.Queue)
  899. SyncManager.register('Event', threading.Event, EventProxy)
  900. SyncManager.register('Lock', threading.Lock, AcquirerProxy)
  901. SyncManager.register('RLock', threading.RLock, AcquirerProxy)
  902. SyncManager.register('Semaphore', threading.Semaphore, AcquirerProxy)
  903. SyncManager.register('BoundedSemaphore', threading.BoundedSemaphore,
  904. AcquirerProxy)
  905. SyncManager.register('Condition', threading.Condition, ConditionProxy)
  906. SyncManager.register('Pool', Pool, PoolProxy)
  907. SyncManager.register('list', list, ListProxy)
  908. SyncManager.register('dict', dict, DictProxy)
  909. SyncManager.register('Value', Value, ValueProxy)
  910. SyncManager.register('Array', Array, ArrayProxy)
  911. SyncManager.register('Namespace', Namespace, NamespaceProxy)
  912. # types returned by methods of PoolProxy
  913. SyncManager.register('Iterator', proxytype=IteratorProxy, create_method=False)
  914. SyncManager.register('AsyncResult', create_method=False)