Highlight the path of file location in error report

So that it's more obvious when we need to copy the file path.
This commit is contained in:
satoru 2016-07-30 17:12:35 +08:00 committed by Bruno Oliveira
parent aa145fa83e
commit 3c8222f1db
4 changed files with 76 additions and 40 deletions

View File

@ -122,4 +122,4 @@ Tom Viner
Trevor Bekolay Trevor Bekolay
Vasily Kuznetsov Vasily Kuznetsov
Wouter van Ackooy Wouter van Ackooy
Xuecong Liao

View File

@ -166,6 +166,9 @@ time or change existing behaviors in order to make them less surprising/more use
* Plugins now benefit from assertion rewriting. Thanks * Plugins now benefit from assertion rewriting. Thanks
`@sober7`_, `@nicoddemus`_ and `@flub`_ for the PR. `@sober7`_, `@nicoddemus`_ and `@flub`_ for the PR.
* Highlight path of the file location in the error report to make it easier to copy/paste.
Thanks `@suzaku`_ for the PR (`#1778`_).
* Fixtures marked with ``@pytest.fixture`` can now use ``yield`` statements exactly like * Fixtures marked with ``@pytest.fixture`` can now use ``yield`` statements exactly like
those marked with the ``@pytest.yield_fixture`` decorator. This change renders those marked with the ``@pytest.yield_fixture`` decorator. This change renders
``@pytest.yield_fixture`` deprecated and makes ``@pytest.fixture`` with ``yield`` statements ``@pytest.yield_fixture`` deprecated and makes ``@pytest.fixture`` with ``yield`` statements
@ -347,6 +350,7 @@ time or change existing behaviors in order to make them less surprising/more use
.. _#1723: https://github.com/pytest-dev/pytest/pull/1723 .. _#1723: https://github.com/pytest-dev/pytest/pull/1723
.. _#1740: https://github.com/pytest-dev/pytest/issues/1740 .. _#1740: https://github.com/pytest-dev/pytest/issues/1740
.. _#1749: https://github.com/pytest-dev/pytest/issues/1749 .. _#1749: https://github.com/pytest-dev/pytest/issues/1749
.. _#1778: https://github.com/pytest-dev/pytest/pull/1778
.. _#372: https://github.com/pytest-dev/pytest/issues/372 .. _#372: https://github.com/pytest-dev/pytest/issues/372
.. _#457: https://github.com/pytest-dev/pytest/issues/457 .. _#457: https://github.com/pytest-dev/pytest/issues/457
.. _#460: https://github.com/pytest-dev/pytest/pull/460 .. _#460: https://github.com/pytest-dev/pytest/pull/460
@ -385,6 +389,7 @@ time or change existing behaviors in order to make them less surprising/more use
.. _@RedBeardCode: https://github.com/RedBeardCode .. _@RedBeardCode: https://github.com/RedBeardCode
.. _@sallner: https://github.com/sallner .. _@sallner: https://github.com/sallner
.. _@sober7: https://github.com/sober7 .. _@sober7: https://github.com/sober7
.. _@suzaku: https://github.com/suzaku
.. _@Stranger6667: https://github.com/Stranger6667 .. _@Stranger6667: https://github.com/Stranger6667
.. _@tareqalayan: https://github.com/tareqalayan .. _@tareqalayan: https://github.com/tareqalayan
.. _@taschini: https://github.com/taschini .. _@taschini: https://github.com/taschini

View File

@ -785,7 +785,8 @@ class ReprFileLocation(TerminalRepr):
i = msg.find("\n") i = msg.find("\n")
if i != -1: if i != -1:
msg = msg[:i] msg = msg[:i]
tw.line("%s:%s: %s" %(self.path, self.lineno, msg)) tw.write(self.path, bold=True, red=True)
tw.line(":%s: %s" % (self.lineno, msg))
class ReprLocals(TerminalRepr): class ReprLocals(TerminalRepr):
def __init__(self, lines): def __init__(self, lines):

View File

@ -26,14 +26,23 @@ import pytest
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3])) pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
class TWMock: class TWMock:
WRITE = object()
def __init__(self): def __init__(self):
self.lines = [] self.lines = []
self.is_writing = False
def sep(self, sep, line=None): def sep(self, sep, line=None):
self.lines.append((sep, line)) self.lines.append((sep, line))
def write(self, msg, **kw):
self.lines.append((TWMock.WRITE, msg))
def line(self, line, **kw): def line(self, line, **kw):
self.lines.append(line) self.lines.append(line)
def markup(self, text, **kw): def markup(self, text, **kw):
return text return text
def get_write_msg(self, idx):
flag, msg = self.lines[idx]
assert flag == TWMock.WRITE
return msg
fullwidth = 80 fullwidth = 80
@ -803,14 +812,18 @@ raise ValueError()
assert tw.lines[0] == " def f():" assert tw.lines[0] == " def f():"
assert tw.lines[1] == "> g(3)" assert tw.lines[1] == "> g(3)"
assert tw.lines[2] == "" assert tw.lines[2] == ""
assert tw.lines[3].endswith("mod.py:5: ") line = tw.get_write_msg(3)
assert tw.lines[4] == ("_ ", None) assert line.endswith("mod.py")
assert tw.lines[5] == "" assert tw.lines[4] == (":5: ")
assert tw.lines[6] == " def g(x):" assert tw.lines[5] == ("_ ", None)
assert tw.lines[7] == "> raise ValueError(x)" assert tw.lines[6] == ""
assert tw.lines[8] == "E ValueError: 3" assert tw.lines[7] == " def g(x):"
assert tw.lines[9] == "" assert tw.lines[8] == "> raise ValueError(x)"
assert tw.lines[10].endswith("mod.py:3: ValueError") assert tw.lines[9] == "E ValueError: 3"
assert tw.lines[10] == ""
line = tw.get_write_msg(11)
assert line.endswith("mod.py")
assert tw.lines[12] == ":3: ValueError"
def test_toterminal_long_missing_source(self, importasmod, tmpdir): def test_toterminal_long_missing_source(self, importasmod, tmpdir):
mod = importasmod(""" mod = importasmod("""
@ -829,13 +842,17 @@ raise ValueError()
tw.lines.pop(0) tw.lines.pop(0)
assert tw.lines[0] == "> ???" assert tw.lines[0] == "> ???"
assert tw.lines[1] == "" assert tw.lines[1] == ""
assert tw.lines[2].endswith("mod.py:5: ") line = tw.get_write_msg(2)
assert tw.lines[3] == ("_ ", None) assert line.endswith("mod.py")
assert tw.lines[4] == "" assert tw.lines[3] == ":5: "
assert tw.lines[5] == "> ???" assert tw.lines[4] == ("_ ", None)
assert tw.lines[6] == "E ValueError: 3" assert tw.lines[5] == ""
assert tw.lines[7] == "" assert tw.lines[6] == "> ???"
assert tw.lines[8].endswith("mod.py:3: ValueError") assert tw.lines[7] == "E ValueError: 3"
assert tw.lines[8] == ""
line = tw.get_write_msg(9)
assert line.endswith("mod.py")
assert tw.lines[10] == ":3: ValueError"
def test_toterminal_long_incomplete_source(self, importasmod, tmpdir): def test_toterminal_long_incomplete_source(self, importasmod, tmpdir):
mod = importasmod(""" mod = importasmod("""
@ -854,13 +871,17 @@ raise ValueError()
tw.lines.pop(0) tw.lines.pop(0)
assert tw.lines[0] == "> ???" assert tw.lines[0] == "> ???"
assert tw.lines[1] == "" assert tw.lines[1] == ""
assert tw.lines[2].endswith("mod.py:5: ") line = tw.get_write_msg(2)
assert tw.lines[3] == ("_ ", None) assert line.endswith("mod.py")
assert tw.lines[4] == "" assert tw.lines[3] == ":5: "
assert tw.lines[5] == "> ???" assert tw.lines[4] == ("_ ", None)
assert tw.lines[6] == "E ValueError: 3" assert tw.lines[5] == ""
assert tw.lines[7] == "" assert tw.lines[6] == "> ???"
assert tw.lines[8].endswith("mod.py:3: ValueError") assert tw.lines[7] == "E ValueError: 3"
assert tw.lines[8] == ""
line = tw.get_write_msg(9)
assert line.endswith("mod.py")
assert tw.lines[10] == ":3: ValueError"
def test_toterminal_long_filenames(self, importasmod): def test_toterminal_long_filenames(self, importasmod):
mod = importasmod(""" mod = importasmod("""
@ -874,15 +895,18 @@ raise ValueError()
try: try:
repr = excinfo.getrepr(abspath=False) repr = excinfo.getrepr(abspath=False)
repr.toterminal(tw) repr.toterminal(tw)
line = tw.lines[-1]
x = py.path.local().bestrelpath(path) x = py.path.local().bestrelpath(path)
if len(x) < len(str(path)): if len(x) < len(str(path)):
assert line == "mod.py:3: ValueError" msg = tw.get_write_msg(-2)
assert msg == "mod.py"
assert tw.lines[-1] == ":3: ValueError"
repr = excinfo.getrepr(abspath=True) repr = excinfo.getrepr(abspath=True)
repr.toterminal(tw) repr.toterminal(tw)
msg = tw.get_write_msg(-2)
assert msg == path
line = tw.lines[-1] line = tw.lines[-1]
assert line == "%s:3: ValueError" %(path,) assert line == ":3: ValueError"
finally: finally:
old.chdir() old.chdir()
@ -929,19 +953,25 @@ raise ValueError()
assert tw.lines[1] == " def f():" assert tw.lines[1] == " def f():"
assert tw.lines[2] == "> g()" assert tw.lines[2] == "> g()"
assert tw.lines[3] == "" assert tw.lines[3] == ""
assert tw.lines[4].endswith("mod.py:3: ") msg = tw.get_write_msg(4)
assert tw.lines[5] == ("_ ", None) assert msg.endswith("mod.py")
assert tw.lines[6].endswith("in g") assert tw.lines[5] == ":3: "
assert tw.lines[7] == " h()" assert tw.lines[6] == ("_ ", None)
assert tw.lines[8].endswith("in h") tw.get_write_msg(7)
assert tw.lines[9] == " i()" assert tw.lines[8].endswith("in g")
assert tw.lines[10] == ("_ ", None) assert tw.lines[9] == " h()"
assert tw.lines[11] == "" tw.get_write_msg(10)
assert tw.lines[12] == " def i():" assert tw.lines[11].endswith("in h")
assert tw.lines[13] == "> raise ValueError()" assert tw.lines[12] == " i()"
assert tw.lines[14] == "E ValueError" assert tw.lines[13] == ("_ ", None)
assert tw.lines[15] == "" assert tw.lines[14] == ""
assert tw.lines[16].endswith("mod.py:9: ValueError") assert tw.lines[15] == " def i():"
assert tw.lines[16] == "> raise ValueError()"
assert tw.lines[17] == "E ValueError"
assert tw.lines[18] == ""
msg = tw.get_write_msg(19)
msg.endswith("mod.py")
assert tw.lines[20] == ":9: ValueError"
@pytest.mark.skipif("sys.version_info[0] < 3") @pytest.mark.skipif("sys.version_info[0] < 3")
def test_exc_chain_repr(self, importasmod): def test_exc_chain_repr(self, importasmod):