diff --git a/CHANGELOG b/CHANGELOG index c05a5500835..0e1aa81ce84 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,14 @@ 2.8.2.dev --------- +- fix #1085: proper handling of encoding errors when passing encoded byte + strings to pytest.parametrize in Python 2. + Thanks Themanwithoutaplan for the report and Bruno Oliveira for the PR. + +- fix #1087: handling SystemError when passing empty byte strings to + pytest.parametrize in Python 3. + Thanks Paul Kehrer for the report and Bruno Oliveira for the PR. + - fix #995: fixed internal error when filtering tracebacks where one entry was generated by an exec() statement. Thanks Daniel Hahler, Ashley C Straw, Philippe Gauthier and Pavel Savchenko diff --git a/_pytest/python.py b/_pytest/python.py index 20e790530f0..28a81520b17 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1048,6 +1048,8 @@ def addcall(self, funcargs=None, id=_notexists, param=_notexists): if _PY3: + import codecs + def _escape_bytes(val): """ If val is pure ascii, returns it as a str(), otherwise escapes @@ -1060,18 +1062,21 @@ def _escape_bytes(val): want to return escaped bytes for any byte, even if they match a utf-8 string. """ - # source: http://goo.gl/bGsnwC - import codecs - encoded_bytes, _ = codecs.escape_encode(val) - return encoded_bytes.decode('ascii') + if val: + # source: http://goo.gl/bGsnwC + encoded_bytes, _ = codecs.escape_encode(val) + return encoded_bytes.decode('ascii') + else: + # empty bytes crashes codecs.escape_encode (#1087) + return '' else: def _escape_bytes(val): """ - In py2 bytes and str are the same, so return it unchanged if it + In py2 bytes and str are the same type, so return it unchanged if it is a full ascii string, otherwise escape it into its binary form. """ try: - return val.encode('ascii') + return val.decode('ascii') except UnicodeDecodeError: return val.encode('string-escape') @@ -1100,7 +1105,7 @@ def _idval(val, argname, idx, idfn): # convertible to ascii, return it as an str() object instead try: return str(val) - except UnicodeDecodeError: + except UnicodeError: # fallthrough pass return str(argname)+str(idx) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index d0df62f81ed..111ca615ae1 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import re import pytest, py @@ -118,6 +119,41 @@ class A: assert metafunc._calls[2].id == "x1-a" assert metafunc._calls[3].id == "x1-b" + @pytest.mark.skipif('sys.version_info[0] >= 3') + def test_unicode_idval_python2(self): + """unittest for the expected behavior to obtain ids for parametrized + unicode values in Python 2: if convertible to ascii, they should appear + as ascii values, otherwise fallback to hide the value behind the name + of the parametrized variable name. #1086 + """ + from _pytest.python import _idval + values = [ + (u'', ''), + (u'ascii', 'ascii'), + (u'ação', 'a6'), + (u'josé@blah.com', 'a6'), + (u'δοκ.ιμή@παράδειγμα.δοκιμή', 'a6'), + ] + for val, expected in values: + assert _idval(val, 'a', 6, None) == expected + + def test_bytes_idval(self): + """unittest for the expected behavior to obtain ids for parametrized + bytes values: + - python2: non-ascii strings are considered bytes and formatted using + "binary escape", where any byte < 127 is escaped into its hex form. + - python3: bytes objects are always escaped using "binary escape". + """ + from _pytest.python import _idval + values = [ + (b'', ''), + (b'\xc3\xb4\xff\xe4', '\\xc3\\xb4\\xff\\xe4'), + (b'ascii', 'ascii'), + (u'αρά'.encode('utf-8'), '\\xce\\xb1\\xcf\\x81\\xce\\xac'), + ] + for val, expected in values: + assert _idval(val, 'a', 6, None) == expected + @pytest.mark.issue250 def test_idmaker_autoname(self): from _pytest.python import idmaker