def processRequests(self): try: RemoteEventHandler.processRequests(self) except ClosedError: from pyqtgraph.Qt import QtGui, QtCore QtGui.QApplication.instance().quit() self.timer.stop()
def startEventLoop(name, port, authkey): conn = multiprocessing.connection.Client(('localhost', int(port)), authkey=authkey) global HANDLER HANDLER = RemoteEventHandler(conn, name, os.getppid()) while True: try: HANDLER.processRequests() # exception raised when the loop should exit time.sleep(0.01) except ClosedError: break
def __init__(self, name=None, target=None, executable=None, copySysPath=True): """ ============ ============================================================= Arguments: name Optional name for this process used when printing messages from the remote process. target Optional function to call after starting remote process. By default, this is startEventLoop(), which causes the remote process to process requests from the parent process until it is asked to quit. If you wish to specify a different target, it must be picklable (bound methods are not). copySysPath If true, copy the contents of sys.path to the remote process ============ ============================================================= """ if target is None: target = startEventLoop if name is None: name = str(self) if executable is None: executable = sys.executable ## random authentication key authkey = ''.join([chr(random.getrandbits(7)) for i in range(20)]) ## Listen for connection from remote process (and find free port number) port = 10000 while True: try: l = multiprocessing.connection.Listener(('localhost', int(port)), authkey=authkey) break except socket.error as ex: if ex.errno != 98: raise port += 1 ## start remote process, instruct it to run target function sysPath = sys.path if copySysPath else None bootstrap = os.path.abspath(os.path.join(os.path.dirname(__file__), 'bootstrap.py')) self.proc = subprocess.Popen((executable, bootstrap), stdin=subprocess.PIPE) targetStr = pickle.dumps(target) ## double-pickle target so that child has a chance to ## set its sys.path properly before unpickling the target pickle.dump((name+'_child', port, authkey, targetStr, sysPath), self.proc.stdin) self.proc.stdin.close() ## open connection for remote process conn = l.accept() RemoteEventHandler.__init__(self, conn, name+'_parent', pid=self.proc.pid) atexit.register(self.join)
def __init__(self, *args, **kwds): RemoteEventHandler.__init__(self, *args, **kwds)
def __init__(self, name=None, target=0, preProxy=None, randomReseed=True): """ When initializing, an optional target may be given. If no target is specified, self.eventLoop will be used. If None is given, no target will be called (and it will be up to the caller to properly shut down the forked process) preProxy may be a dict of values that will appear as ObjectProxy in the remote process (but do not need to be sent explicitly since they are available immediately before the call to fork(). Proxies will be availabe as self.proxies[name]. If randomReseed is True, the built-in random and numpy.random generators will be reseeded in the child process. """ self.hasJoined = False if target == 0: target = self.eventLoop if name is None: name = str(self) conn, remoteConn = multiprocessing.Pipe() proxyIDs = {} if preProxy is not None: for k, v in preProxy.iteritems(): proxyId = LocalObjectProxy.registerObject(v) proxyIDs[k] = proxyId pid = os.fork() if pid == 0: self.isParent = False ## We are now in the forked process; need to be extra careful what we touch while here. ## - no reading/writing file handles/sockets owned by parent process (stdout is ok) ## - don't touch QtGui or QApplication at all; these are landmines. ## - don't let the process call exit handlers os.setpgrp() ## prevents signals (notably keyboard interrupt) being forwarded from parent to this process ## close all file handles we do not want shared with parent conn.close() sys.stdin.close() ## otherwise we screw with interactive prompts. fid = remoteConn.fileno() os.closerange(3, fid) os.closerange(fid+1, 4096) ## just guessing on the maximum descriptor count.. ## Override any custom exception hooks def excepthook(*args): import traceback traceback.print_exception(*args) sys.excepthook = excepthook ## Make it harder to access QApplication instance if 'PyQt4.QtGui' in sys.modules: sys.modules['PyQt4.QtGui'].QApplication = None sys.modules.pop('PyQt4.QtGui', None) sys.modules.pop('PyQt4.QtCore', None) ## sabotage atexit callbacks atexit._exithandlers = [] atexit.register(lambda: os._exit(0)) if randomReseed: if 'numpy.random' in sys.modules: sys.modules['numpy.random'].seed(os.getpid() ^ int(time.time()*10000%10000)) if 'random' in sys.modules: sys.modules['random'].seed(os.getpid() ^ int(time.time()*10000%10000)) RemoteEventHandler.__init__(self, remoteConn, name+'_child', pid=os.getppid()) ppid = os.getppid() self.forkedProxies = {} for name, proxyId in proxyIDs.iteritems(): self.forkedProxies[name] = ObjectProxy(ppid, proxyId=proxyId, typeStr=repr(preProxy[name])) if target is not None: target() else: self.isParent = True self.childPid = pid remoteConn.close() RemoteEventHandler.handlers = {} ## don't want to inherit any of this from the parent. RemoteEventHandler.__init__(self, conn, name+'_parent', pid=pid) atexit.register(self.join)