Escape both bytes and unicode strings for "ids" in Metafunc.parametrize
This commit is contained in:
parent
ceacc12b52
commit
4405dd0ffe
|
@ -23,7 +23,9 @@
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
|
|
||||||
*
|
* 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.
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
|
|
|
@ -1025,9 +1025,12 @@ class Metafunc(FuncargnamesCompatAttr):
|
||||||
if callable(ids):
|
if callable(ids):
|
||||||
idfn = ids
|
idfn = ids
|
||||||
ids = None
|
ids = None
|
||||||
if ids and len(ids) != len(argvalues):
|
if ids:
|
||||||
raise ValueError('%d tests specified with %d ids' %(
|
if len(ids) != len(argvalues):
|
||||||
len(argvalues), len(ids)))
|
raise ValueError('%d tests specified with %d ids' %(
|
||||||
|
len(argvalues), len(ids)))
|
||||||
|
else:
|
||||||
|
ids = [_escape_strings(i) for i in ids]
|
||||||
if not ids:
|
if not ids:
|
||||||
ids = idmaker(argnames, argvalues, idfn)
|
ids = idmaker(argnames, argvalues, idfn)
|
||||||
newcalls = []
|
newcalls = []
|
||||||
|
@ -1078,38 +1081,55 @@ class Metafunc(FuncargnamesCompatAttr):
|
||||||
self._calls.append(cs)
|
self._calls.append(cs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if _PY3:
|
if _PY3:
|
||||||
import codecs
|
import codecs
|
||||||
|
|
||||||
def _escape_bytes(val):
|
def _escape_strings(val):
|
||||||
"""
|
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
|
||||||
If val is pure ascii, returns it as a str(), otherwise escapes
|
bytes objects into a sequence of escaped bytes:
|
||||||
into a sequence of escaped bytes:
|
|
||||||
b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
|
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:
|
note:
|
||||||
the obvious "v.decode('unicode-escape')" will return
|
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
|
want to return escaped bytes for any byte, even if they match
|
||||||
a utf-8 string.
|
a utf-8 string.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if val:
|
if isinstance(val, bytes):
|
||||||
# source: http://goo.gl/bGsnwC
|
if val:
|
||||||
encoded_bytes, _ = codecs.escape_encode(val)
|
# source: http://goo.gl/bGsnwC
|
||||||
return encoded_bytes.decode('ascii')
|
encoded_bytes, _ = codecs.escape_encode(val)
|
||||||
|
return encoded_bytes.decode('ascii')
|
||||||
|
else:
|
||||||
|
# empty bytes crashes codecs.escape_encode (#1087)
|
||||||
|
return ''
|
||||||
else:
|
else:
|
||||||
# empty bytes crashes codecs.escape_encode (#1087)
|
return val.encode('unicode_escape').decode('ascii')
|
||||||
return ''
|
|
||||||
else:
|
else:
|
||||||
def _escape_bytes(val):
|
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.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
In py2 bytes and str are the same type, so return it unchanged if it
|
if isinstance(val, bytes):
|
||||||
is a full ascii string, otherwise escape it into its binary form.
|
try:
|
||||||
"""
|
return val.decode('ascii')
|
||||||
try:
|
except UnicodeDecodeError:
|
||||||
return val.decode('ascii')
|
return val.encode('string-escape')
|
||||||
except UnicodeDecodeError:
|
else:
|
||||||
return val.encode('string-escape')
|
return val.encode('unicode-escape')
|
||||||
|
|
||||||
|
|
||||||
def _idval(val, argname, idx, idfn):
|
def _idval(val, argname, idx, idfn):
|
||||||
|
@ -1117,28 +1137,20 @@ def _idval(val, argname, idx, idfn):
|
||||||
try:
|
try:
|
||||||
s = idfn(val)
|
s = idfn(val)
|
||||||
if s:
|
if s:
|
||||||
return s
|
return _escape_strings(s)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if isinstance(val, bytes):
|
if isinstance(val, bytes) or (_PY2 and isinstance(val, unicode)):
|
||||||
return _escape_bytes(val)
|
return _escape_strings(val)
|
||||||
elif isinstance(val, (float, int, str, bool, NoneType)):
|
elif isinstance(val, (float, int, str, bool, NoneType)):
|
||||||
return str(val)
|
return str(val)
|
||||||
elif isinstance(val, REGEX_TYPE):
|
elif isinstance(val, REGEX_TYPE):
|
||||||
return val.pattern
|
return _escape_strings(val.pattern)
|
||||||
elif enum is not None and isinstance(val, enum.Enum):
|
elif enum is not None and isinstance(val, enum.Enum):
|
||||||
return str(val)
|
return str(val)
|
||||||
elif isclass(val) and hasattr(val, '__name__'):
|
elif isclass(val) and hasattr(val, '__name__'):
|
||||||
return 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)
|
return str(argname)+str(idx)
|
||||||
|
|
||||||
def _idvalset(idx, valset, argnames, idfn):
|
def _idvalset(idx, valset, argnames, idfn):
|
||||||
|
|
Loading…
Reference in New Issue