summaryrefslogtreecommitdiff
path: root/lib/spack/external/_pytest/terminal.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/external/_pytest/terminal.py')
-rw-r--r--lib/spack/external/_pytest/terminal.py193
1 files changed, 125 insertions, 68 deletions
diff --git a/lib/spack/external/_pytest/terminal.py b/lib/spack/external/_pytest/terminal.py
index 16bf757338..9da94d0c91 100644
--- a/lib/spack/external/_pytest/terminal.py
+++ b/lib/spack/external/_pytest/terminal.py
@@ -2,6 +2,9 @@
This is a good source for looking at the various reporting hooks.
"""
+from __future__ import absolute_import, division, print_function
+
+import itertools
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED
import pytest
@@ -10,39 +13,41 @@ import sys
import time
import platform
+from _pytest import nodes
import _pytest._pluggy as pluggy
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "reporting", after="general")
group._addoption('-v', '--verbose', action="count",
- dest="verbose", default=0, help="increase verbosity."),
+ dest="verbose", default=0, help="increase verbosity."),
group._addoption('-q', '--quiet', action="count",
- dest="quiet", default=0, help="decrease verbosity."),
+ dest="quiet", default=0, help="decrease verbosity."),
group._addoption('-r',
- action="store", dest="reportchars", default='', metavar="chars",
- help="show extra test summary info as specified by chars (f)ailed, "
- "(E)error, (s)skipped, (x)failed, (X)passed, "
- "(p)passed, (P)passed with output, (a)all except pP. "
- "The pytest warnings are displayed at all times except when "
- "--disable-pytest-warnings is set")
- group._addoption('--disable-pytest-warnings', default=False,
- dest='disablepytestwarnings', action='store_true',
- help='disable warnings summary, overrides -r w flag')
+ action="store", dest="reportchars", default='', metavar="chars",
+ help="show extra test summary info as specified by chars (f)ailed, "
+ "(E)error, (s)skipped, (x)failed, (X)passed, "
+ "(p)passed, (P)passed with output, (a)all except pP. "
+ "Warnings are displayed at all times except when "
+ "--disable-warnings is set")
+ group._addoption('--disable-warnings', '--disable-pytest-warnings', default=False,
+ dest='disable_warnings', action='store_true',
+ help='disable warnings summary')
group._addoption('-l', '--showlocals',
- action="store_true", dest="showlocals", default=False,
- help="show locals in tracebacks (disabled by default).")
+ action="store_true", dest="showlocals", default=False,
+ help="show locals in tracebacks (disabled by default).")
group._addoption('--tb', metavar="style",
- action="store", dest="tbstyle", default='auto',
- choices=['auto', 'long', 'short', 'no', 'line', 'native'],
- help="traceback print mode (auto/long/short/line/native/no).")
+ action="store", dest="tbstyle", default='auto',
+ choices=['auto', 'long', 'short', 'no', 'line', 'native'],
+ help="traceback print mode (auto/long/short/line/native/no).")
group._addoption('--fulltrace', '--full-trace',
- action="store_true", default=False,
- help="don't cut any tracebacks (default is to cut).")
+ action="store_true", default=False,
+ help="don't cut any tracebacks (default is to cut).")
group._addoption('--color', metavar="color",
- action="store", dest="color", default='auto',
- choices=['yes', 'no', 'auto'],
- help="color terminal output (yes/no/auto).")
+ action="store", dest="color", default='auto',
+ choices=['yes', 'no', 'auto'],
+ help="color terminal output (yes/no/auto).")
+
def pytest_configure(config):
config.option.verbose -= config.option.quiet
@@ -54,12 +59,13 @@ def pytest_configure(config):
reporter.write_line("[traceconfig] " + msg)
config.trace.root.setprocessor("pytest:config", mywriter)
+
def getreportopt(config):
reportopts = ""
reportchars = config.option.reportchars
- if not config.option.disablepytestwarnings and 'w' not in reportchars:
+ if not config.option.disable_warnings and 'w' not in reportchars:
reportchars += 'w'
- elif config.option.disablepytestwarnings and 'w' in reportchars:
+ elif config.option.disable_warnings and 'w' in reportchars:
reportchars = reportchars.replace('w', '')
if reportchars:
for char in reportchars:
@@ -69,6 +75,7 @@ def getreportopt(config):
reportopts = 'fEsxXw'
return reportopts
+
def pytest_report_teststatus(report):
if report.passed:
letter = "."
@@ -80,13 +87,41 @@ def pytest_report_teststatus(report):
letter = "f"
return report.outcome, letter, report.outcome.upper()
+
class WarningReport:
+ """
+ Simple structure to hold warnings information captured by ``pytest_logwarning``.
+ """
+
def __init__(self, code, message, nodeid=None, fslocation=None):
+ """
+ :param code: unused
+ :param str message: user friendly message about the warning
+ :param str|None nodeid: node id that generated the warning (see ``get_location``).
+ :param tuple|py.path.local fslocation:
+ file system location of the source of the warning (see ``get_location``).
+ """
self.code = code
self.message = message
self.nodeid = nodeid
self.fslocation = fslocation
+ def get_location(self, config):
+ """
+ Returns the more user-friendly information about the location
+ of a warning, or None.
+ """
+ if self.nodeid:
+ return self.nodeid
+ if self.fslocation:
+ if isinstance(self.fslocation, tuple) and len(self.fslocation) >= 2:
+ filename, linenum = self.fslocation[:2]
+ relpath = py.path.local(filename).relto(config.invocation_dir)
+ return '%s:%s' % (relpath, linenum)
+ else:
+ return str(self.fslocation)
+ return None
+
class TerminalReporter:
def __init__(self, config, file=None):
@@ -146,8 +181,22 @@ class TerminalReporter:
self._tw.line(line, **markup)
def rewrite(self, line, **markup):
+ """
+ Rewinds the terminal cursor to the beginning and writes the given line.
+
+ :kwarg erase: if True, will also add spaces until the full terminal width to ensure
+ previous lines are properly erased.
+
+ The rest of the keyword arguments are markup instructions.
+ """
+ erase = markup.pop('erase', False)
+ if erase:
+ fill_count = self._tw.fullwidth - len(line)
+ fill = ' ' * fill_count
+ else:
+ fill = ''
line = str(line)
- self._tw.write("\r" + line, **markup)
+ self._tw.write("\r" + line + fill, **markup)
def write_sep(self, sep, title=None, **markup):
self.ensure_newline()
@@ -166,8 +215,6 @@ class TerminalReporter:
def pytest_logwarning(self, code, fslocation, message, nodeid):
warnings = self.stats.setdefault("warnings", [])
- if isinstance(fslocation, tuple):
- fslocation = "%s:%d" % fslocation
warning = WarningReport(code=code, fslocation=fslocation,
message=message, nodeid=nodeid)
warnings.append(warning)
@@ -212,15 +259,15 @@ class TerminalReporter:
word, markup = word
else:
if rep.passed:
- markup = {'green':True}
+ markup = {'green': True}
elif rep.failed:
- markup = {'red':True}
+ markup = {'red': True}
elif rep.skipped:
- markup = {'yellow':True}
+ markup = {'yellow': True}
line = self._locationline(rep.nodeid, *rep.location)
if not hasattr(rep, 'node'):
self.write_ensure_prefix(line, word, **markup)
- #self._tw.write(word, **markup)
+ # self._tw.write(word, **markup)
else:
self.ensure_newline()
if hasattr(rep, 'node'):
@@ -241,7 +288,7 @@ class TerminalReporter:
items = [x for x in report.result if isinstance(x, pytest.Item)]
self._numcollected += len(items)
if self.isatty:
- #self.write_fspath_result(report.nodeid, 'E')
+ # self.write_fspath_result(report.nodeid, 'E')
self.report_collect()
def report_collect(self, final=False):
@@ -254,15 +301,15 @@ class TerminalReporter:
line = "collected "
else:
line = "collecting "
- line += str(self._numcollected) + " items"
+ line += str(self._numcollected) + " item" + ('' if self._numcollected == 1 else 's')
if errors:
line += " / %d errors" % errors
if skipped:
line += " / %d skipped" % skipped
if self.isatty:
+ self.rewrite(line, bold=True, erase=True)
if final:
- line += " \n"
- self.rewrite(line, bold=True)
+ self.write('\n')
else:
self.write_line(line)
@@ -288,6 +335,9 @@ class TerminalReporter:
self.write_line(msg)
lines = self.config.hook.pytest_report_header(
config=self.config, startdir=self.startdir)
+ self._write_report_lines_from_hooks(lines)
+
+ def _write_report_lines_from_hooks(self, lines):
lines.reverse()
for line in flatten(lines):
self.write_line(line)
@@ -295,8 +345,8 @@ class TerminalReporter:
def pytest_report_header(self, config):
inifile = ""
if config.inifile:
- inifile = config.rootdir.bestrelpath(config.inifile)
- lines = ["rootdir: %s, inifile: %s" %(config.rootdir, inifile)]
+ inifile = " " + config.rootdir.bestrelpath(config.inifile)
+ lines = ["rootdir: %s, inifile:%s" % (config.rootdir, inifile)]
plugininfo = config.pluginmanager.list_plugin_distinfo()
if plugininfo:
@@ -314,10 +364,9 @@ class TerminalReporter:
rep.toterminal(self._tw)
return 1
return 0
- if not self.showheader:
- return
- #for i, testarg in enumerate(self.config.args):
- # self.write_line("test path %d: %s" %(i+1, testarg))
+ lines = self.config.hook.pytest_report_collectionfinish(
+ config=self.config, startdir=self.startdir, items=session.items)
+ self._write_report_lines_from_hooks(lines)
def _printcollecteditems(self, items):
# to print out items and their parent collectors
@@ -340,14 +389,14 @@ class TerminalReporter:
stack = []
indent = ""
for item in items:
- needed_collectors = item.listchain()[1:] # strip root node
+ needed_collectors = item.listchain()[1:] # strip root node
while stack:
if stack == needed_collectors[:len(stack)]:
break
stack.pop()
for col in needed_collectors[len(stack):]:
stack.append(col)
- #if col.name == "()":
+ # if col.name == "()":
# continue
indent = (len(stack) - 1) * " "
self._tw.line("%s%s" % (indent, col))
@@ -396,15 +445,15 @@ class TerminalReporter:
line = self.config.cwd_relative_nodeid(nodeid)
if domain and line.endswith(domain):
line = line[:-len(domain)]
- l = domain.split("[")
- l[0] = l[0].replace('.', '::') # don't replace '.' in params
- line += "[".join(l)
+ values = domain.split("[")
+ values[0] = values[0].replace('.', '::') # don't replace '.' in params
+ line += "[".join(values)
return line
# collect_fspath comes from testid which has a "/"-normalized path
if fspath:
res = mkrel(nodeid).replace("::()", "") # parens-normalization
- if nodeid.split("::")[0] != fspath.replace("\\", "/"):
+ if nodeid.split("::")[0] != fspath.replace("\\", nodes.SEP):
res += " <- " + self.startdir.bestrelpath(fspath)
else:
res = "[location]"
@@ -415,7 +464,7 @@ class TerminalReporter:
fspath, lineno, domain = rep.location
return domain
else:
- return "test session" # XXX?
+ return "test session" # XXX?
def _getcrashline(self, rep):
try:
@@ -430,21 +479,29 @@ class TerminalReporter:
# summaries for sessionfinish
#
def getreports(self, name):
- l = []
+ values = []
for x in self.stats.get(name, []):
if not hasattr(x, '_pdbshown'):
- l.append(x)
- return l
+ values.append(x)
+ return values
def summary_warnings(self):
if self.hasopt("w"):
- warnings = self.stats.get("warnings")
- if not warnings:
+ all_warnings = self.stats.get("warnings")
+ if not all_warnings:
return
- self.write_sep("=", "pytest-warning summary")
- for w in warnings:
- self._tw.line("W%s %s %s" % (w.code,
- w.fslocation, w.message))
+
+ grouped = itertools.groupby(all_warnings, key=lambda wr: wr.get_location(self.config))
+
+ self.write_sep("=", "warnings summary", yellow=True, bold=False)
+ for location, warnings in grouped:
+ self._tw.line(str(location) or '<undetermined location>')
+ for w in warnings:
+ lines = w.message.splitlines()
+ indented = '\n'.join(' ' + x for x in lines)
+ self._tw.line(indented)
+ self._tw.line()
+ self._tw.line('-- Docs: http://doc.pytest.org/en/latest/warnings.html')
def summary_passes(self):
if self.config.option.tbstyle != "no":
@@ -466,7 +523,6 @@ class TerminalReporter:
content = content[:-1]
self._tw.line(content)
-
def summary_failures(self):
if self.config.option.tbstyle != "no":
reports = self.getreports('failed')
@@ -528,6 +584,7 @@ class TerminalReporter:
self.write_sep("=", "%d tests deselected" % (
len(self.stats['deselected'])), bold=True)
+
def repr_pythonversion(v=None):
if v is None:
v = sys.version_info
@@ -536,30 +593,30 @@ def repr_pythonversion(v=None):
except (TypeError, ValueError):
return str(v)
-def flatten(l):
- for x in l:
+
+def flatten(values):
+ for x in values:
if isinstance(x, (list, tuple)):
for y in flatten(x):
yield y
else:
yield x
+
def build_summary_stats_line(stats):
keys = ("failed passed skipped deselected "
- "xfailed xpassed warnings error").split()
- key_translation = {'warnings': 'pytest-warnings'}
+ "xfailed xpassed warnings error").split()
unknown_key_seen = False
for key in stats.keys():
if key not in keys:
- if key: # setup/teardown reports have an empty key, ignore them
+ if key: # setup/teardown reports have an empty key, ignore them
keys.append(key)
unknown_key_seen = True
parts = []
for key in keys:
val = stats.get(key, None)
if val:
- key_name = key_translation.get(key, key)
- parts.append("%d %s" % (len(val), key_name))
+ parts.append("%d %s" % (len(val), key))
if parts:
line = ", ".join(parts)
@@ -579,7 +636,7 @@ def build_summary_stats_line(stats):
def _plugin_nameversions(plugininfo):
- l = []
+ values = []
for plugin, dist in plugininfo:
# gets us name and version!
name = '{dist.project_name}-{dist.version}'.format(dist=dist)
@@ -588,6 +645,6 @@ def _plugin_nameversions(plugininfo):
name = name[7:]
# we decided to print python package names
# they can have more than one plugin
- if name not in l:
- l.append(name)
- return l
+ if name not in values:
+ values.append(name)
+ return values