Author: Benjamin Peterson <[email protected]>
Branch:
Changeset: r44922:2978e535782d
Date: 2011-06-13 20:18 -0500
http://bitbucket.org/pypy/pypy/changeset/2978e535782d/
Log: allow non-ascii keyword arguments to be passed through functions
(fixes #751)
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -95,7 +95,9 @@
assert isinstance(args_w, list)
self.arguments_w = args_w
self.keywords = keywords
+ self.lexical_keywords = len(keywords) if keywords is not None else 0
self.keywords_w = keywords_w
+ self.keyword_names_w = None
if keywords is not None:
assert keywords_w is not None
assert len(keywords_w) == len(keywords)
@@ -182,6 +184,7 @@
raise
keys_w = space.unpackiterable(w_keys)
if keys_w:
+ self.keyword_names_w = keys_w
self._do_combine_starstarargs_wrapped(keys_w, w_starstararg)
return True
else:
@@ -201,11 +204,11 @@
space.w_TypeError,
space.wrap("keywords must be strings"))
if e.match(space, space.w_UnicodeEncodeError):
- raise OperationError(
- space.w_TypeError,
- space.wrap("keyword cannot be encoded to ascii"))
- raise
- if self.keywords and key in self.keywords:
+ # Allow this to pass through
+ key = None
+ else:
+ raise
+ if key is not None and self.keywords and key in self.keywords:
raise operationerrfmt(self.space.w_TypeError,
"got multiple values "
"for keyword argument "
@@ -339,6 +342,10 @@
used_keywords = [False] * num_kwds
for i in range(num_kwds):
name = keywords[i]
+ # If name was not encoded as a string, it could be None. In
that
+ # case, it's definitely not going to be in the signature.
+ if name is None:
+ continue
j = signature.find_argname(name)
if j < 0:
continue
@@ -376,15 +383,21 @@
if num_remainingkwds:
for i in range(len(keywords)):
if not used_keywords[i]:
- key = keywords[i]
- self.space.setitem(w_kwds, self.space.wrap(key),
keywords_w[i])
+ if i < self.lexical_keywords:
+ w_key = self.space.wrap(keywords[i])
+ else:
+ j = i - self.lexical_keywords
+ w_key = self.keyword_names_w[j]
+ self.space.setitem(w_kwds, w_key, keywords_w[i])
scope_w[co_argcount + has_vararg] = w_kwds
elif num_remainingkwds:
if co_argcount == 0:
raise ArgErrCount(avail, num_kwds,
co_argcount, has_vararg, has_kwarg,
defaults_w, missing)
- raise ArgErrUnknownKwds(num_remainingkwds, keywords, used_keywords)
+ raise ArgErrUnknownKwds(self.space, num_remainingkwds, keywords,
+ self.keyword_names_w,
self.lexical_keywords,
+ used_keywords)
if missing:
raise ArgErrCount(avail, num_kwds,
@@ -666,13 +679,25 @@
class ArgErrUnknownKwds(ArgErr):
- def __init__(self, num_remainingkwds, keywords, used_keywords):
+ def __init__(self, space, num_remainingkwds, keywords, keyword_names_w,
+ lexical_keywords, used_keywords):
self.kwd_name = ''
self.num_kwds = num_remainingkwds
if num_remainingkwds == 1:
for i in range(len(keywords)):
if not used_keywords[i]:
- self.kwd_name = keywords[i]
+ if i < lexical_keywords:
+ name = keywords[i]
+ else:
+ w_name = keyword_names_w[i - lexical_keywords]
+ if not space.isinstance_w(w_name, space.w_str):
+ # We'll assume it's unicode. Encode it.
+ w_enc = space.wrap(space.sys.defaultencoding)
+ w_err = space.wrap("replace")
+ w_name = space.call_method(w_name, "encode", w_enc,
+ w_err)
+ name = space.str_w(w_name)
+ self.kwd_name = name
break
def getmsg(self, fnname):
diff --git a/pypy/interpreter/test/test_argument.py
b/pypy/interpreter/test/test_argument.py
--- a/pypy/interpreter/test/test_argument.py
+++ b/pypy/interpreter/test/test_argument.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
import py
from pypy.interpreter.argument import (Arguments, ArgumentsForTranslation,
ArgErr, ArgErrUnknownKwds, ArgErrMultipleValues, ArgErrCount, rawshape,
@@ -126,6 +127,7 @@
w_AttributeError = AttributeError
w_UnicodeEncodeError = UnicodeEncodeError
w_dict = dict
+ w_str = str
class TestArgumentsNormal(object):
@@ -485,26 +487,6 @@
args._match_signature(None, l, Signature(['abc']))
assert len(l) == 1
assert l[0] == space.wrap(5)
- #
- def str_w(w):
- try:
- return str(w)
- except UnicodeEncodeError:
- raise OperationError(space.w_UnicodeEncodeError,
- space.wrap("oups"))
- space.str_w = str_w
- w_starstar = space.wrap({u'\u1234': 5})
- err = py.test.raises(OperationError, Arguments,
- space, [], w_starstararg=w_starstar)
- # Check that we get a TypeError. On CPython it is because of
- # "no argument called '?'". On PyPy we get a TypeError too, but
- # earlier: "keyword cannot be encoded to ascii". The
- # difference, besides the error message, is only apparent if the
- # receiver also takes a **arg. Then CPython passes the
- # non-ascii unicode unmodified, whereas PyPy complains. We will
- # not care until someone has a use case for that.
- assert not err.value.match(space, space.w_UnicodeEncodeError)
- assert err.value.match(space, space.w_TypeError)
class TestErrorHandling(object):
def test_missing_args(self):
@@ -559,10 +541,12 @@
assert 0, "did not raise"
def test_unknown_keywords(self):
- err = ArgErrUnknownKwds(1, ['a', 'b'], [True, False])
+ space = DummySpace()
+ err = ArgErrUnknownKwds(space, 1, ['a', 'b'], None, 2, [True, False])
s = err.getmsg('foo')
assert s == "foo() got an unexpected keyword argument 'b'"
- err = ArgErrUnknownKwds(2, ['a', 'b', 'c'], [True, False, False])
+ err = ArgErrUnknownKwds(space, 2, ['a', 'b', 'c'], None, 3,
+ [True, False, False])
s = err.getmsg('foo')
assert s == "foo() got 2 unexpected keyword arguments"
@@ -592,6 +576,14 @@
exc = raises(TypeError, (lambda a, b, **kw: 0), a=1)
assert exc.value.message == "<lambda>() takes exactly 2 non-keyword
arguments (0 given)"
+ def test_unicode_keywords(self):
+ def f(**kwargs):
+ assert kwargs[u"美"] == 42
+ f(**{u"美" : 42})
+ def f(x): pass
+ e = raises(TypeError, "f(**{u'ü' : 19})")
+ assert "?" in str(e.value)
+
def make_arguments_for_translation(space, args_w, keywords_w={},
w_stararg=None, w_starstararg=None):
return ArgumentsForTranslation(space, args_w, keywords_w.keys(),
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit