improve run-deprecations script

Rewrote in Twisted, which lets us read/scan/print all log lines in
realtime. The output is now correctly interleaved (as well as
maintaining the stdout-vs-stderr of each message). The renamed
--warnings= logfile records all relevant lines from *both* stdout and
stderr (i.e. any that includes "DeprecationWarning"), which handles a
change (perhaps in recent Twisteds?) that emits these warnings on stdout
instead of stderr.
This commit is contained in:
Brian Warner 2016-04-06 11:07:06 -10:00
parent d5e1b21a8a
commit 9d20de3db9
2 changed files with 65 additions and 45 deletions

View File

@ -1,4 +1,5 @@
import sys, os, subprocess import sys, os, io
from twisted.internet import reactor, protocol, task, defer
from twisted.python.procutils import which from twisted.python.procutils import which
from twisted.python import usage from twisted.python import usage
@ -8,7 +9,7 @@ from twisted.python import usage
class Options(usage.Options): class Options(usage.Options):
optParameters = [ optParameters = [
["stderr", None, None, "file to write stderr into at end of test run"], ["warnings", None, None, "file to write warnings into at end of test run"],
] ]
def parseArgs(self, command, *args): def parseArgs(self, command, *args):
@ -16,54 +17,73 @@ class Options(usage.Options):
self["args"] = list(args) self["args"] = list(args)
description = """Run as: description = """Run as:
PYTHONWARNINGS=default::DeprecationWarning python run-deprecations.py [--stderr=STDERRFILE] COMMAND ARGS.. PYTHONWARNINGS=default::DeprecationWarning python run-deprecations.py [--warnings=STDERRFILE] COMMAND ARGS..
""" """
config = Options() class RunPP(protocol.ProcessProtocol):
config.parseOptions() def outReceived(self, data):
self.stdout.write(data)
sys.stdout.write(data)
def errReceived(self, data):
self.stderr.write(data)
sys.stderr.write(data)
def processEnded(self, reason):
signal = reason.value.signal
rc = reason.value.exitCode
self.d.callback((signal, rc))
@defer.inlineCallbacks
def run_command(main):
config = Options()
config.parseOptions()
command = config["command"] command = config["command"]
if "/" in command: if "/" in command:
# don't search # don't search
exe = command exe = command
else: else:
executables = which(command) executables = which(command)
if not executables: if not executables:
raise ValueError("unable to find '%s' in PATH (%s)" % raise ValueError("unable to find '%s' in PATH (%s)" %
(command, os.environ.get("PATH"))) (command, os.environ.get("PATH")))
exe = executables[0] exe = executables[0]
pw = os.environ.get("PYTHONWARNINGS") pw = os.environ.get("PYTHONWARNINGS")
DDW = "default::DeprecationWarning" DDW = "default::DeprecationWarning"
if pw != DDW: if pw != DDW:
print "note: $PYTHONWARNINGS is '%s', not the expected %s" % (pw, DDW) print "note: $PYTHONWARNINGS is '%s', not the expected %s" % (pw, DDW)
sys.stdout.flush()
print "note: stderr is being captured, and will be emitted at the end" pp = RunPP()
sys.stdout.flush() pp.d = defer.Deferred()
pp.stdout = io.BytesIO()
pp.stderr = io.BytesIO()
reactor.spawnProcess(pp, exe, [exe] + config["args"], env=None)
(signal, rc) = yield pp.d
# stdout goes directly to the parent, so test progress can be watched in real warnings = []
# time. But subprocess.Popen() doesn't give us any good way of seeing it
p = subprocess.Popen([exe] + config["args"], stderr=subprocess.PIPE)
stderr = p.communicate()[1]
rc = p.returncode
count = 0
if config["stderr"]: pp.stdout.seek(0)
with open(config["stderr"], "wb") as f: for line in pp.stdout.readlines():
print >>f, stderr,
if stderr:
print >>sys.stderr, "--"
print >>sys.stderr, "Captured stderr follows:"
for line in stderr.splitlines():
if "DeprecationWarning" in line: if "DeprecationWarning" in line:
count += 1 warnings.append(line) # includes newline
print >>sys.stderr, line
print >>sys.stderr, "--"
if count: pp.stderr.seek(0)
print "ERROR: %d deprecation warnings found" % count for line in pp.stderr.readlines():
sys.exit(1) if "DeprecationWarning" in line:
print "no deprecation warnings" warnings.append(line)
sys.exit(rc)
if warnings:
if config["warnings"]:
with open(config["warnings"], "wb") as f:
print >>f, "".join(warnings)
print "ERROR: %d deprecation warnings found" % len(warnings)
sys.exit(1)
print "no deprecation warnings"
if signal:
sys.exit(signal)
sys.exit(rc)
task.react(run_command)

View File

@ -18,7 +18,7 @@ passenv = USERPROFILE HOMEDRIVE HOMEPATH
setenv = setenv =
PYTHONWARNINGS=default::DeprecationWarning PYTHONWARNINGS=default::DeprecationWarning
commands = commands =
python misc/build_helpers/run-deprecations.py --stderr=_trial_temp/stderr.log trial --rterrors {posargs:allmydata} python misc/build_helpers/run-deprecations.py --warnings=_trial_temp/deprecation-warnings.log trial --rterrors {posargs:allmydata}
[testenv:upcoming-deprecations] [testenv:upcoming-deprecations]
basepython=python2.7 basepython=python2.7
@ -29,7 +29,7 @@ deps =
git+https://github.com/twisted/twisted git+https://github.com/twisted/twisted
git+https://github.com/warner/foolscap git+https://github.com/warner/foolscap
commands = commands =
python misc/build_helpers/run-deprecations.py --stderr=_trial_temp/stderr.log trial --rterrors {posargs:allmydata} python misc/build_helpers/run-deprecations.py --warnings=_trial_temp/deprecation-warnings.log trial --rterrors {posargs:allmydata}
[testenv:checkmemory] [testenv:checkmemory]
commands = commands =