| Index: testing/legion/rpc_methods.py
|
| diff --git a/testing/legion/rpc_methods.py b/testing/legion/rpc_methods.py
|
| index 7f17e2387e38b2e3c1bdd95e86fd031c74486481..1a59f288afde5fb03c1f8fb3d0ebf4fa6de5bae4 100644
|
| --- a/testing/legion/rpc_methods.py
|
| +++ b/testing/legion/rpc_methods.py
|
| @@ -41,6 +41,10 @@ class RPCMethods(object):
|
| logging.info('Echoing %s', message)
|
| return 'echo %s' % str(message)
|
|
|
| + def AbsPath(self, path):
|
| + """Returns the absolute path."""
|
| + return os.path.abspath(path)
|
| +
|
| def Quit(self):
|
| """Call _server.shutdown in another thread.
|
|
|
| @@ -58,6 +62,10 @@ class Subprocess(object):
|
| This non-blocking subprocess allows the caller to continue operating while
|
| also able to interact with this subprocess based on a key returned to
|
| the caller at the time of creation.
|
| +
|
| + Creation args are set via Set* methods called after calling Process but
|
| + before calling Start. This is due to a limitation of the XML-RPC
|
| + implementation not supporting keyword arguments.
|
| """
|
|
|
| _processes = {}
|
| @@ -65,23 +73,38 @@ class Subprocess(object):
|
| _creation_lock = threading.Lock()
|
|
|
| def __init__(self, cmd):
|
| - self.proc = subprocess42.Popen(cmd, stdout=subprocess42.PIPE,
|
| - stderr=subprocess42.PIPE)
|
| self.stdout = ''
|
| self.stderr = ''
|
| + self.cmd = cmd
|
| + self.proc = None
|
| + self.cwd = None
|
| + self.verbose = False
|
| + self.detached = False
|
| self.data_lock = threading.Lock()
|
| - threading.Thread(target=self._run).start()
|
|
|
| - def _run(self):
|
| + def __str__(self):
|
| + return '%r, cwd=%r, verbose=%r, detached=%r' % (
|
| + self.cmd, self.cwd, self.verbose, self.detached)
|
| +
|
| + def _reader(self):
|
| for pipe, data in self.proc.yield_any():
|
| with self.data_lock:
|
| if pipe == 'stdout':
|
| self.stdout += data
|
| + if self.verbose:
|
| + sys.stdout.write(data)
|
| else:
|
| self.stderr += data
|
| + if self.verbose:
|
| + sys.stderr.write(data)
|
| +
|
| + @classmethod
|
| + def KillAll(cls):
|
| + for key in cls._processes:
|
| + cls.Kill(key)
|
|
|
| @classmethod
|
| - def Popen(cls, cmd):
|
| + def Process(cls, cmd):
|
| with cls._creation_lock:
|
| key = 'Process%d' % cls._process_next_id
|
| cls._process_next_id += 1
|
| @@ -90,18 +113,50 @@ class Subprocess(object):
|
| cls._processes[key] = process
|
| return key
|
|
|
| + def _Start(self):
|
| + logging.info('Starting process %s', self)
|
| + self.proc = subprocess42.Popen(self.cmd, stdout=subprocess42.PIPE,
|
| + stderr=subprocess42.PIPE,
|
| + detached=self.detached, cwd=self.cwd)
|
| + threading.Thread(target=self._reader).start()
|
| +
|
| + @classmethod
|
| + def Start(cls, key):
|
| + cls._processes[key]._Start()
|
| +
|
| + @classmethod
|
| + def SetCwd(cls, key, cwd):
|
| + """Sets the process's cwd."""
|
| + logging.debug('Setting %s cwd to %s', key, cwd)
|
| + cls._processes[key].cwd = cwd
|
| +
|
| + @classmethod
|
| + def SetDetached(cls, key):
|
| + """Creates a detached process."""
|
| + logging.debug('Setting %s to run detached', key)
|
| + cls._processes[key].detached = True
|
| +
|
| + @classmethod
|
| + def SetVerbose(cls, key):
|
| + """Sets the stdout and stderr to be emitted locally."""
|
| + logging.debug('Setting %s to be verbose', key)
|
| + cls._processes[key].verbose = True
|
| +
|
| @classmethod
|
| def Terminate(cls, key):
|
| - logging.debug('Terminating and deleting process %s', key)
|
| - return cls._processes.pop(key).proc.terminate()
|
| + logging.debug('Terminating process %s', key)
|
| + cls._processes[key].proc.terminate()
|
|
|
| @classmethod
|
| def Kill(cls, key):
|
| - logging.debug('Killing and deleting process %s', key)
|
| - return cls._processes.pop(key).proc.kill()
|
| + logging.debug('Killing process %s', key)
|
| + cls._processes[key].proc.kill()
|
|
|
| @classmethod
|
| def Delete(cls, key):
|
| + if cls.GetReturncode(key) is None:
|
| + logging.warning('Killing %s before deleting it', key)
|
| + cls.Kill(key)
|
| logging.debug('Deleting process %s', key)
|
| cls._processes.pop(key)
|
|
|
| @@ -139,6 +194,14 @@ class Subprocess(object):
|
| return stderr
|
|
|
| @classmethod
|
| + def ReadOutput(cls, key):
|
| + """Returns the (stdout, stderr) since the last Read* call.
|
| +
|
| + See ReadStdout for additional details.
|
| + """
|
| + return cls.ReadStdout(key), cls.ReadStderr(key)
|
| +
|
| + @classmethod
|
| def Wait(cls, key):
|
| return cls._processes[key].proc.wait()
|
|
|
|
|