From 3897047a2c0f8906c99418ddad6e2c68f0dec5c7 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 30 Jul 2010 12:05:32 -0700 Subject: Added exit code checking to process.py (twisted process utils). A bit of class refactoring to make it work & cleaner. Also added some more instructive messages to install_venv.py, because otherwise people that don't know what they're doing will install the wrong pip... i.e. I did :-) --- nova/process.py | 90 +++++++++++++++++++++------------------------------------ 1 file changed, 33 insertions(+), 57 deletions(-) (limited to 'nova/process.py') diff --git a/nova/process.py b/nova/process.py index 2dc56372f..24ea3eb7f 100644 --- a/nova/process.py +++ b/nova/process.py @@ -54,19 +54,20 @@ class UnexpectedErrorOutput(IOError): IOError.__init__(self, "got stdout: %r\nstderr: %r" % (stdout, stderr)) -# NOTE(termie): this too -class _BackRelay(protocol.ProcessProtocol): +# This is based on _BackRelay from twister.internal.utils, but modified to capture +# both stdout and stderr without odd stderr handling, and also to handle stdin +class BackRelayWithInput(protocol.ProcessProtocol): """ Trivial protocol for communicating with a process and turning its output into the result of a L{Deferred}. @ivar deferred: A L{Deferred} which will be called back with all of stdout - and, if C{errortoo} is true, all of stderr as well (mixed together in - one string). If C{errortoo} is false and any bytes are received over - stderr, this will fire with an L{_UnexpectedErrorOutput} instance and - the attribute will be set to C{None}. + and all of stderr as well (as a tuple). C{terminate_on_stderr} is true + and any bytes are received over stderr, this will fire with an + L{_UnexpectedErrorOutput} instance and the attribute will be set to + C{None}. - @ivar onProcessEnded: If C{errortoo} is false and bytes are received over + @ivar onProcessEnded: If C{terminate_on_stderr} is false and bytes are received over stderr, this attribute will refer to a L{Deferred} which will be called back when the process ends. This C{Deferred} is also associated with the L{_UnexpectedErrorOutput} which C{deferred} fires with earlier in @@ -74,52 +75,43 @@ class _BackRelay(protocol.ProcessProtocol): ended, in addition to knowing when bytes have been received via stderr. """ - def __init__(self, deferred, errortoo=0): + def __init__(self, deferred, startedDeferred=None, terminate_on_stderr=False, + check_exit_code=True, input=None): self.deferred = deferred - self.s = StringIO.StringIO() - if errortoo: - self.errReceived = self.errReceivedIsGood - else: - self.errReceived = self.errReceivedIsBad - - def errReceivedIsBad(self, text): - if self.deferred is not None: + self.stdout = StringIO.StringIO() + self.stderr = StringIO.StringIO() + self.startedDeferred = startedDeferred + self.terminate_on_stderr = terminate_on_stderr + self.check_exit_code = check_exit_code + self.input = input + + def errReceived(self, text): + self.sterr.write(text) + if self.terminate_on_stderr and (self.deferred is not None): self.onProcessEnded = defer.Deferred() - err = UnexpectedErrorOutput(text, self.onProcessEnded) - self.deferred.errback(failure.Failure(err)) + self.deferred.errback(UnexpectedErrorOutput(stdout=self.stdout.getvalue(), stderr=self.stderr.getvalue())) self.deferred = None self.transport.loseConnection() - def errReceivedIsGood(self, text): - self.s.write(text) + def errReceived(self, text): + self.stderr.write(text) def outReceived(self, text): - self.s.write(text) + self.stdout.write(text) def processEnded(self, reason): if self.deferred is not None: - self.deferred.callback(self.s.getvalue()) + stdout, stderr = self.stdout.getvalue(), self.stderr.getvalue() + try: + if self.check_exit_code: + reason.trap(error.ProcessDone) + self.deferred.callback((stdout, stderr)) + except: + self.deferred.errback(UnexpectedErrorOutput(stdout, stderr)) elif self.onProcessEnded is not None: self.onProcessEnded.errback(reason) -class BackRelayWithInput(_BackRelay): - def __init__(self, deferred, startedDeferred=None, error_ok=0, - input=None): - # Twisted doesn't use new-style classes in most places :( - _BackRelay.__init__(self, deferred, errortoo=error_ok) - self.error_ok = error_ok - self.input = input - self.stderr = StringIO.StringIO() - self.startedDeferred = startedDeferred - - def errReceivedIsBad(self, text): - self.stderr.write(text) - self.transport.loseConnection() - - def errReceivedIsGood(self, text): - self.stderr.write(text) - def connectionMade(self): if self.startedDeferred: self.startedDeferred.callback(self) @@ -127,31 +119,15 @@ class BackRelayWithInput(_BackRelay): self.transport.write(self.input) self.transport.closeStdin() - def processEnded(self, reason): - if self.deferred is not None: - stdout, stderr = self.s.getvalue(), self.stderr.getvalue() - try: - # NOTE(termie): current behavior means if error_ok is True - # we won't throw an error even if the process - # exited with a non-0 status, so you can't be - # okay with stderr output and not with bad exit - # codes. - if not self.error_ok: - reason.trap(error.ProcessDone) - self.deferred.callback((stdout, stderr)) - except: - self.deferred.errback(UnexpectedErrorOutput(stdout, stderr)) - - def getProcessOutput(executable, args=None, env=None, path=None, reactor=None, - error_ok=0, input=None, startedDeferred=None): + check_exit_code=True, input=None, startedDeferred=None): if reactor is None: from twisted.internet import reactor args = args and args or () env = env and env and {} d = defer.Deferred() p = BackRelayWithInput( - d, startedDeferred=startedDeferred, error_ok=error_ok, input=input) + d, startedDeferred=startedDeferred, check_exit_code=check_exit_code, input=input) # NOTE(vish): commands come in as unicode, but self.executes needs # strings or process.spawn raises a deprecation warning executable = str(executable) -- cgit From 0ee7d2f74a959bcf1cf611f63842302866774475 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Sun, 8 Aug 2010 12:57:33 -0700 Subject: Greater compliance with pep8/pylint style checks --- nova/process.py | 108 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 53 deletions(-) (limited to 'nova/process.py') diff --git a/nova/process.py b/nova/process.py index 9e9de2ee8..37ab538ee 100644 --- a/nova/process.py +++ b/nova/process.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2010 FathomDB Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -20,16 +21,11 @@ Process pool, still buggy right now. """ -import logging -import multiprocessing import StringIO from twisted.internet import defer from twisted.internet import error -from twisted.internet import process from twisted.internet import protocol from twisted.internet import reactor -from twisted.internet import threads -from twisted.python import failure from nova import flags @@ -54,8 +50,9 @@ class UnexpectedErrorOutput(IOError): IOError.__init__(self, "got stdout: %r\nstderr: %r" % (stdout, stderr)) -# This is based on _BackRelay from twister.internal.utils, but modified to capture -# both stdout and stderr without odd stderr handling, and also to handle stdin +# This is based on _BackRelay from twister.internal.utils, but modified to +# capture both stdout and stderr, without odd stderr handling, and also to +# handle stdin class BackRelayWithInput(protocol.ProcessProtocol): """ Trivial protocol for communicating with a process and turning its output @@ -67,35 +64,37 @@ class BackRelayWithInput(protocol.ProcessProtocol): L{_UnexpectedErrorOutput} instance and the attribute will be set to C{None}. - @ivar onProcessEnded: If C{terminate_on_stderr} is false and bytes are received over - stderr, this attribute will refer to a L{Deferred} which will be called - back when the process ends. This C{Deferred} is also associated with - the L{_UnexpectedErrorOutput} which C{deferred} fires with earlier in - this case so that users can determine when the process has actually - ended, in addition to knowing when bytes have been received via stderr. + @ivar onProcessEnded: If C{terminate_on_stderr} is false and bytes are + received over stderr, this attribute will refer to a L{Deferred} which + will be called back when the process ends. This C{Deferred} is also + associated with the L{_UnexpectedErrorOutput} which C{deferred} fires + with earlier in this case so that users can determine when the process + has actually ended, in addition to knowing when bytes have been received + via stderr. """ - def __init__(self, deferred, startedDeferred=None, terminate_on_stderr=False, - check_exit_code=True, input=None): + def __init__(self, deferred, started_deferred=None, + terminate_on_stderr=False, check_exit_code=True, + process_input=None): self.deferred = deferred self.stdout = StringIO.StringIO() self.stderr = StringIO.StringIO() - self.startedDeferred = startedDeferred + self.started_deferred = started_deferred self.terminate_on_stderr = terminate_on_stderr self.check_exit_code = check_exit_code - self.input = input + self.process_input = process_input + self.on_process_ended = None def errReceived(self, text): - self.sterr.write(text) + self.stderr.write(text) if self.terminate_on_stderr and (self.deferred is not None): - self.onProcessEnded = defer.Deferred() - self.deferred.errback(UnexpectedErrorOutput(stdout=self.stdout.getvalue(), stderr=self.stderr.getvalue())) + self.on_process_ended = defer.Deferred() + self.deferred.errback(UnexpectedErrorOutput( + stdout=self.stdout.getvalue(), + stderr=self.stderr.getvalue())) self.deferred = None self.transport.loseConnection() - def errReceived(self, text): - self.stderr.write(text) - def outReceived(self, text): self.stdout.write(text) @@ -107,37 +106,40 @@ class BackRelayWithInput(protocol.ProcessProtocol): reason.trap(error.ProcessDone) self.deferred.callback((stdout, stderr)) except: - # This logic is a little suspicious to me (justinsb)... - # If the callback throws an exception, then errback will be called also. - # However, this is what the unit tests test for... + # NOTE(justinsb): This logic is a little suspicious to me... + # If the callback throws an exception, then errback will be + # called also. However, this is what the unit tests test for... self.deferred.errback(UnexpectedErrorOutput(stdout, stderr)) - elif self.onProcessEnded is not None: - self.onProcessEnded.errback(reason) + elif self.on_process_ended is not None: + self.on_process_ended.errback(reason) def connectionMade(self): - if self.startedDeferred: - self.startedDeferred.callback(self) - if self.input: - self.transport.write(self.input) + if self.started_deferred: + self.started_deferred.callback(self) + if self.process_input: + self.transport.write(self.process_input) self.transport.closeStdin() -def getProcessOutput(executable, args=None, env=None, path=None, reactor=None, - check_exit_code=True, input=None, startedDeferred=None): - if reactor is None: - from twisted.internet import reactor +def get_process_output(executable, args=None, env=None, path=None, + process_reactor=None, check_exit_code=True, + process_input=None, started_deferred=None): + if process_reactor is None: + process_reactor = reactor args = args and args or () env = env and env and {} - d = defer.Deferred() - p = BackRelayWithInput( - d, startedDeferred=startedDeferred, check_exit_code=check_exit_code, input=input) + deferred = defer.Deferred() + process_handler = BackRelayWithInput( + deferred, started_deferred=started_deferred, + check_exit_code=check_exit_code, process_input=process_input) # NOTE(vish): commands come in as unicode, but self.executes needs # strings or process.spawn raises a deprecation warning executable = str(executable) if not args is None: args = [str(x) for x in args] - reactor.spawnProcess(p, executable, (executable,)+tuple(args), env, path) - return d + process_reactor.spawnProcess( process_handler, executable, + (executable,)+tuple(args), env, path) + return deferred class ProcessPool(object): @@ -163,26 +165,26 @@ class ProcessPool(object): return self.execute(executable, args, **kw) def execute(self, *args, **kw): - d = self._pool.acquire() + deferred = self._pool.acquire() - def _associateProcess(proto): - d.process = proto.transport + def _associate_process(proto): + deferred.process = proto.transport return proto.transport started = defer.Deferred() - started.addCallback(_associateProcess) - kw.setdefault('startedDeferred', started) + started.addCallback(_associate_process) + kw.setdefault('started_deferred', started) - d.process = None - d.started = started + deferred.process = None + deferred.started = started - d.addCallback(lambda _: getProcessOutput(*args, **kw)) - d.addBoth(self._release) - return d + deferred.addCallback(lambda _: get_process_output(*args, **kw)) + deferred.addBoth(self._release) + return deferred - def _release(self, rv=None): + def _release(self, retval=None): self._pool.release() - return rv + return retval class SharedPool(object): _instance = None -- cgit From 993563b6cc9db9f24480678cf8b2d0750aee7a92 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Sun, 8 Aug 2010 13:05:24 -0700 Subject: Used new (clearer) flag names when calling processes --- nova/process.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'nova/process.py') diff --git a/nova/process.py b/nova/process.py index 37ab538ee..d36de0410 100644 --- a/nova/process.py +++ b/nova/process.py @@ -123,15 +123,19 @@ class BackRelayWithInput(protocol.ProcessProtocol): def get_process_output(executable, args=None, env=None, path=None, process_reactor=None, check_exit_code=True, - process_input=None, started_deferred=None): + process_input=None, started_deferred=None, + terminate_on_stderr=False): if process_reactor is None: process_reactor = reactor args = args and args or () env = env and env and {} deferred = defer.Deferred() process_handler = BackRelayWithInput( - deferred, started_deferred=started_deferred, - check_exit_code=check_exit_code, process_input=process_input) + deferred, + started_deferred=started_deferred, + check_exit_code=check_exit_code, + process_input=process_input, + terminate_on_stderr=terminate_on_stderr) # NOTE(vish): commands come in as unicode, but self.executes needs # strings or process.spawn raises a deprecation warning executable = str(executable) -- cgit From d508418214016d5c00aa8d304f9498f5b99a960b Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 16 Aug 2010 14:16:21 +0200 Subject: rather comprehensive style fixes --- nova/process.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/process.py') diff --git a/nova/process.py b/nova/process.py index 2dc56372f..86f29e2c4 100644 --- a/nova/process.py +++ b/nova/process.py @@ -23,6 +23,7 @@ Process pool, still buggy right now. import logging import multiprocessing import StringIO + from twisted.internet import defer from twisted.internet import error from twisted.internet import process @@ -205,6 +206,7 @@ class ProcessPool(object): self._pool.release() return rv + class SharedPool(object): _instance = None def __init__(self): @@ -213,5 +215,6 @@ class SharedPool(object): def __getattr__(self, key): return getattr(self._instance, key) + def simple_execute(cmd, **kwargs): return SharedPool().simple_execute(cmd, **kwargs) -- cgit