From 578cba20d42d98a4d9348ebc06bd82a8b3e9364c Mon Sep 17 00:00:00 2001 From: holger krekel Date: Fri, 21 May 2010 16:42:46 +0200 Subject: [PATCH] fix issue94 make reporting more robust against bogus source code (and internally be more careful when presenting unexpected byte sequences) also make py.code.Source accept a list of lines directly. --HG-- branch : trunk --- CHANGELOG | 3 +++ py/_code/source.py | 6 +++++- py/_plugin/pytest_pytester.py | 15 ++++++++++++--- testing/code/test_source.py | 14 ++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e6d36aff5..c4e8cadab 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,9 @@ Changes between 1.3.0 and 1.3.1 declarative approach with the @py.test.mark.xfail cannot be used as it would mark all configurations as xfail. +- fix issue94: make reporting more robust against bogus source code + (and internally be more careful when presenting unexpected byte sequences) + - improve and refine letter reporting in the progress bar: . pass f failed test diff --git a/py/_code/source.py b/py/_code/source.py index dff0b1f70..1eb4dcc86 100644 --- a/py/_code/source.py +++ b/py/_code/source.py @@ -26,6 +26,8 @@ class Source(object): partlines = [] if isinstance(part, Source): partlines = part.lines + elif isinstance(part, (tuple, list)): + partlines = [x.rstrip("\n") for x in part] elif isinstance(part, py.builtin._basestring): partlines = part.split('\n') if rstrip: @@ -172,7 +174,9 @@ class Source(object): try: #compile(source+'\n', "x", "exec") syntax_checker(source+'\n') - except SyntaxError: + except KeyboardInterrupt: + raise + except Exception: return False else: return True diff --git a/py/_plugin/pytest_pytester.py b/py/_plugin/pytest_pytester.py index 20cac915c..0fb4f24de 100644 --- a/py/_plugin/pytest_pytester.py +++ b/py/_plugin/pytest_pytester.py @@ -110,7 +110,7 @@ class TmpTestdir: def _makefile(self, ext, args, kwargs): items = list(kwargs.items()) if args: - source = "\n".join(map(str, args)) + source = "\n".join(map(str, args)) + "\n" basename = self.request.function.__name__ items.insert(0, (basename, source)) ret = None @@ -294,8 +294,10 @@ class TmpTestdir: ret = popen.wait() f1.close() f2.close() - out = p1.read("rb").decode("utf-8").splitlines() - err = p2.read("rb").decode("utf-8").splitlines() + out = p1.read("rb") + out = getdecoded(out).splitlines() + err = p2.read("rb") + err = getdecoded(err).splitlines() def dump_lines(lines, fp): try: for line in lines: @@ -360,6 +362,13 @@ class TmpTestdir: child.timeout = expect_timeout return child +def getdecoded(out): + try: + return out.decode("utf-8") + except UnicodeDecodeError: + return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % ( + py.io.saferepr(out),) + class PseudoPlugin: def __init__(self, vars): self.__dict__.update(vars) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 35f2e45d1..978915550 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -36,6 +36,19 @@ def test_source_from_function(): source = py.code.Source(test_source_str_function) assert str(source).startswith('def test_source_str_function():') +def test_source_from_method(): + class TestClass: + def test_method(self): + pass + source = py.code.Source(TestClass().test_method) + assert source.lines == ["def test_method(self):", + " pass"] + +def test_source_from_lines(): + lines = ["a \n", "b\n", "c"] + source = py.code.Source(lines) + assert source.lines == ['a ', 'b', 'c'] + def test_source_from_inner_function(): def f(): pass @@ -92,6 +105,7 @@ def test_isparseable(): assert Source(" \nif 1:\n pass").isparseable() assert not Source("if 1:\n").isparseable() assert not Source(" \nif 1:\npass").isparseable() + assert not Source(chr(0)).isparseable() class TestAccesses: source = Source("""\