Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ Russel Winder
Ben Webb
Alexei Kozlenok
Cal Leeming
Cara Vinson
4 changes: 3 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

*

*
* Fix (`#1351 <https://github.com/pytest-dev/pytest/issues/1351>`_):
explicitly passed parametrize ids do not get escaped to ascii.
Thanks `@ceridwen`_ for the PR.

* Improve error message when a plugin fails to load.
Thanks `@nicoddemus`_ for the PR.
Expand Down
79 changes: 45 additions & 34 deletions _pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -1014,9 +1014,12 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
if callable(ids):
idfn = ids
ids = None
if ids and len(ids) != len(argvalues):
raise ValueError('%d tests specified with %d ids' %(
len(argvalues), len(ids)))
if ids:
if len(ids) != len(argvalues):
raise ValueError('%d tests specified with %d ids' %(
len(argvalues), len(ids)))
else:
ids = [_escape_strings(i) for i in ids]
if not ids:
ids = idmaker(argnames, argvalues, idfn)
newcalls = []
Expand Down Expand Up @@ -1070,64 +1073,72 @@ 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
into a sequence of escaped bytes:
def _escape_strings(val):
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
bytes objects into a sequence of escaped bytes:

b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'

and escapes unicode objects into a sequence of escaped unicode
ids, e.g.:

'4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'

note:
the obvious "v.decode('unicode-escape')" will return
valid utf-8 unicode if it finds them in the string, but we
valid utf-8 unicode if it finds them in bytes, but we
want to return escaped bytes for any byte, even if they match
a utf-8 string.

"""
if val:
# source: http://goo.gl/bGsnwC
encoded_bytes, _ = codecs.escape_encode(val)
return encoded_bytes.decode('ascii')
if isinstance(val, bytes):
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:
# empty bytes crashes codecs.escape_encode (#1087)
return ''
return val.encode('unicode_escape').decode('ascii')
else:
def _escape_bytes(val):
"""
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.
def _escape_strings(val):
"""In py2 bytes and str are the same type, so return if it's a bytes
object, return it unchanged if it is a full ascii string,
otherwise escape it into its binary form.

If it's a unicode string, change the unicode characters into
unicode escapes.

"""
try:
return val.decode('ascii')
except UnicodeDecodeError:
return val.encode('string-escape')
if isinstance(val, bytes):
try:
return val.decode('ascii')
except UnicodeDecodeError:
return val.encode('string-escape')
else:
return val.encode('unicode-escape')


def _idval(val, argname, idx, idfn):
if idfn:
try:
s = idfn(val)
if s:
return s
return _escape_strings(s)
except Exception:
pass

if isinstance(val, bytes):
return _escape_bytes(val)
if isinstance(val, bytes) or (_PY2 and isinstance(val, unicode)):
return _escape_strings(val)
elif isinstance(val, (float, int, str, bool, NoneType)):
return str(val)
elif isinstance(val, REGEX_TYPE):
return _escape_bytes(val.pattern) if isinstance(val.pattern, bytes) else val.pattern
return _escape_strings(val.pattern) if isinstance(val.pattern, bytes) else val.pattern
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given the new idea for unicode, i feel we no longer need the conditional, please check if thats the case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran the tests again with the conditional removed, and everything passed, so I assume it's okay.

elif enum is not None and isinstance(val, enum.Enum):
return str(val)
elif isclass(val) and hasattr(val, '__name__'):
return val.__name__
elif _PY2 and isinstance(val, unicode):
# special case for python 2: if a unicode string is
# convertible to ascii, return it as an str() object instead
try:
return str(val)
except UnicodeError:
# fallthrough
pass
return str(argname)+str(idx)

def _idvalset(idx, valset, argnames, idfn):
Expand Down