Author: Armin Rigo <[email protected]>
Branch: py3k
Changeset: r86363:aceb45156627
Date: 2016-08-20 22:57 +0200
http://bitbucket.org/pypy/pypy/changeset/aceb45156627/

Log:    hg merge py3k-kwonly-builtin

        Fix kwonly handling for built-in functions, and also probably fix
        the handling of kwonly arguments in general in the JIT

diff --git a/pypy/interpreter/eval.py b/pypy/interpreter/eval.py
--- a/pypy/interpreter/eval.py
+++ b/pypy/interpreter/eval.py
@@ -40,9 +40,6 @@
         and possibly more locals."""
         return self.signature().getallvarnames()
 
-    def getformalargcount(self):
-        return self.signature().scope_length()
-
     def getdocstring(self, space):
         return space.w_None
 
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -38,7 +38,9 @@
                           'name?',
                           'w_kw_defs?']
 
-    def __init__(self, space, code, w_globals=None, defs_w=[], w_kw_defs=None,
+    w_kw_defs = None
+
+    def __init__(self, space, code, w_globals=None, defs_w=[], kw_defs_w=None,
                  closure=None, w_ann=None, forcename=None, qualname=None):
         self.space = space
         self.name = forcename or code.co_name
@@ -48,10 +50,12 @@
         self.w_func_globals = w_globals  # the globals dictionary
         self.closure = closure    # normally, list of Cell instances or None
         self.defs_w = defs_w
-        self.w_kw_defs = w_kw_defs
         self.w_func_dict = None # filled out below if needed
         self.w_module = None
         self.w_ann = w_ann
+        #
+        if kw_defs_w is not None:
+            self.init_kwdefaults_dict(kw_defs_w)
 
     def __repr__(self):
         # return "function %s.%s" % (self.space, self.name)
@@ -379,14 +383,29 @@
 
     def fset_func_kwdefaults(self, space, w_new):
         if space.is_w(w_new, space.w_None):
-            w_new = None
-        elif not space.isinstance_w(w_new, space.w_dict):
-            raise oefmt(space.w_TypeError, "__kwdefaults__ must be a dict")
-        self.w_kw_defs = w_new
+            self.w_kw_defs = None
+        else:
+            if not space.isinstance_w(w_new, space.w_dict):
+                raise oefmt(space.w_TypeError, "__kwdefaults__ must be a dict")
+            w_instance = self.init_kwdefaults_dict()
+            w_instance.setdict(space, w_new)
+            self.w_kw_defs = w_instance.getdict(space)
 
     def fdel_func_kwdefaults(self, space):
         self.w_kw_defs = None
 
+    def init_kwdefaults_dict(self, kw_defs_w=[]):
+        # use the mapdict logic to get at least not-too-bad JIT code
+        # from function calls with default values of kwonly arguments
+        space = self.space
+        w_class = space.fromcache(KwDefsClassCache).w_class
+        w_instance = space.call_function(w_class)
+        for w_name, w_value in kw_defs_w:
+            attr = space.unicode_w(w_name).encode('utf-8')
+            w_instance.setdictvalue(space, attr, w_value)
+        self.w_kw_defs = w_instance.getdict(space)
+        return w_instance
+
     def fget_func_doc(self, space):
         if self.w_doc is None:
             self.w_doc = self.code.getdocstring(space)
@@ -663,10 +682,12 @@
     def __init__(self, func):
         assert isinstance(func, Function)
         Function.__init__(self, func.space, func.code, func.w_func_globals,
-                          func.defs_w, None, func.closure, None, func.name)
+                          func.defs_w, None, func.closure,
+                          None, func.name)
         self.w_doc = func.w_doc
         self.w_func_dict = func.w_func_dict
         self.w_module = func.w_module
+        self.w_kw_defs = func.w_kw_defs
 
     def descr_builtinfunction__new__(space, w_subtype):
         raise oefmt(space.w_TypeError,
@@ -684,3 +705,12 @@
     else:
         code = None
     return isinstance(code, BuiltinCode)
+
+
+class KwDefsClassCache:
+    def __init__(self, space):
+        self.w_class = space.appexec([], """():
+            class KwDefs:
+                pass
+            return KwDefs
+        """)
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -28,6 +28,8 @@
 from rpython.rlib.rarithmetic import r_longlong, r_int, r_ulonglong, r_uint
 from rpython.tool.sourcetools import func_with_new_name, compile2
 
+NO_DEFAULT = object()
+
 
 # internal non-translatable parts:
 class SignatureBuilder(object):
@@ -44,12 +46,21 @@
         self.argnames = argnames
         self.varargname = varargname
         self.kwargname = kwargname
+        self.kwonlyargnames = None
 
     def append(self, argname):
-        self.argnames.append(argname)
+        if self.kwonlyargnames is None:
+            self.argnames.append(argname)
+        else:
+            self.kwonlyargnames.append(argname)
+
+    def marker_kwonly(self):
+        assert self.kwonlyargnames is None
+        self.kwonlyargnames = []
 
     def signature(self):
-        return Signature(self.argnames, self.varargname, self.kwargname)
+        return Signature(self.argnames, self.varargname, self.kwargname,
+                         self.kwonlyargnames)
 
 #________________________________________________________________
 
@@ -66,13 +77,6 @@
         """NOT_RPYTHON"""
         raise NotImplementedError
 
-def kwonly(arg_unwrapper):
-    """Mark argument as keyword-only.
-
-    XXX: has no actual effect for now.
-    """
-    return arg_unwrapper
-
 
 class UnwrapSpecRecipe(object):
     "NOT_RPYTHON"
@@ -229,6 +233,11 @@
         name = int_unwrapping_space_method(typ)
         self.checked_space_method(name, app_sig)
 
+    def visit_kwonly(self, _, app_sig):
+        argname = self.orig_arg()
+        assert argname == '__kwonly__'
+        app_sig.marker_kwonly()
+
 
 class UnwrapSpec_EmitRun(UnwrapSpecEmit):
 
@@ -316,6 +325,9 @@
     def visit_truncatedint_w(self, typ):
         self.run_args.append("space.truncatedint_w(%s)" % (self.scopenext(),))
 
+    def visit_kwonly(self, typ):
+        self.run_args.append("None")
+
     def _make_unwrap_activation_class(self, unwrap_spec, cache={}):
         try:
             key = tuple(unwrap_spec)
@@ -468,6 +480,9 @@
     def visit_truncatedint_w(self, typ):
         self.unwrap.append("space.truncatedint_w(%s)" % (self.nextarg(),))
 
+    def visit_kwonly(self, typ):
+        raise FastFuncNotSupported
+
     def make_fastfunc(unwrap_spec, func):
         unwrap_info = UnwrapSpec_FastFunc_Unwrap()
         unwrap_info.apply_over(unwrap_spec)
@@ -563,6 +578,8 @@
                 unwrap_spec.append('args_w')
             elif argname.startswith('w_'):
                 unwrap_spec.append(W_Root)
+            elif argname == '__kwonly__':
+                unwrap_spec.append('kwonly')
             else:
                 unwrap_spec.append(None)
 
@@ -616,6 +633,8 @@
         argnames = sig.argnames
         varargname = sig.varargname
         kwargname = sig.kwargname
+        if sig.kwonlyargnames:
+            import pdb; pdb.set_trace()
         self._argnames = argnames
 
         if unwrap_spec is None:
@@ -950,64 +969,71 @@
         self.name = app_name
         self.as_classmethod = as_classmethod
 
-        if not f.func_defaults:
-            self._staticdefs = []
-        else:
-            argnames = self._code._argnames
-            defaults = f.func_defaults
-            self._staticdefs = zip(argnames[-len(defaults):], defaults)
+        argnames = self._code._argnames
+        defaults = f.func_defaults or ()
+        self._staticdefs = dict(zip(
+            argnames[len(argnames) - len(defaults):], defaults))
+
         return self
 
     def _getdefaults(self, space):
         "NOT_RPYTHON"
-        defs_w = []
-        unwrap_spec = self._code._unwrap_spec[-len(self._staticdefs):]
-        for i, (name, defaultval) in enumerate(self._staticdefs):
+        alldefs_w = {}
+        assert len(self._code._argnames) == len(self._code._unwrap_spec)
+        for name, spec in zip(self._code._argnames, self._code._unwrap_spec):
+            if name == '__kwonly__':
+                continue
+
+            defaultval = self._staticdefs.get(name, NO_DEFAULT)
+            w_def = Ellipsis
             if name.startswith('w_'):
-                assert defaultval is None, (
+                assert defaultval in (NO_DEFAULT, None), (
                     "%s: default value for '%s' can only be None, got %r; "
                     "use unwrap_spec(...=WrappedDefault(default))" % (
                     self._code.identifier, name, defaultval))
-                defs_w.append(None)
-            elif name != '__args__' and name != 'args_w':
-                spec = unwrap_spec[i]
-                if isinstance(defaultval, str) and spec not in [str]:
-                    defs_w.append(space.newbytes(defaultval))
-                else:
-                    defs_w.append(space.wrap(defaultval))
-        if self._code._unwrap_spec:
-            UNDEFINED = object()
-            alldefs_w = [UNDEFINED] * len(self._code.sig.argnames)
-            if defs_w:
-                alldefs_w[-len(defs_w):] = defs_w
-            code = self._code
-            assert isinstance(code._unwrap_spec, (list, tuple))
-            assert isinstance(code._argnames, list)
-            assert len(code._unwrap_spec) == len(code._argnames)
-            for i in range(len(code._unwrap_spec)-1, -1, -1):
-                spec = code._unwrap_spec[i]
-                argname = code._argnames[i]
-                if isinstance(spec, tuple) and spec[0] is W_Root:
-                    assert False, "use WrappedDefault"
-                if isinstance(spec, WrappedDefault):
-                    default_value = spec.default_value
-                    if isinstance(default_value, str):
-                        w_default = space.newbytes(default_value)
-                    else:
-                        w_default = space.wrap(default_value)
-                    assert isinstance(w_default, W_Root)
-                    assert argname.startswith('w_')
-                    argname = argname[2:]
-                    j = self._code.sig.argnames.index(argname)
-                    assert alldefs_w[j] in (UNDEFINED, None)
-                    alldefs_w[j] = w_default
-            first_defined = 0
-            while (first_defined < len(alldefs_w) and
-                   alldefs_w[first_defined] is UNDEFINED):
-                first_defined += 1
-            defs_w = alldefs_w[first_defined:]
-            assert UNDEFINED not in defs_w
-        return defs_w
+                if defaultval is None:
+                    w_def = None
+
+            if isinstance(spec, tuple) and spec[0] is W_Root:
+                assert False, "use WrappedDefault"
+            elif isinstance(spec, WrappedDefault):
+                assert name.startswith('w_')
+                defaultval = spec.default_value
+                w_def = Ellipsis
+
+            if defaultval is not NO_DEFAULT:
+                if name != '__args__' and name != 'args_w':
+                    if w_def is Ellipsis:
+                        if isinstance(defaultval, str) and spec not in [str]:
+                            w_def = space.newbytes(defaultval)
+                        else:
+                            w_def = space.wrap(defaultval)
+                    if name.startswith('w_'):
+                        name = name[2:]
+                    alldefs_w[name] = w_def
+        #
+        # Here, 'alldefs_w' maps some argnames to their wrapped default
+        # value.  We return two lists:
+        #  - a list of defaults for positional arguments, which covers
+        #    some suffix of the sig.argnames list
+        #  - a list of pairs (w_name, w_def) for kwonly arguments
+        #
+        sig = self._code.sig
+        first_defined = 0
+        while (first_defined < len(sig.argnames) and
+               sig.argnames[first_defined] not in alldefs_w):
+            first_defined += 1
+        defs_w = [alldefs_w.pop(name) for name in sig.argnames[first_defined:]]
+
+        kw_defs_w = None
+        if alldefs_w:
+            kw_defs_w = []
+            for name, w_def in sorted(alldefs_w.items()):
+                assert name in sig.kwonlyargnames
+                w_name = space.newunicode(name.decode('utf-8'))
+                kw_defs_w.append((w_name, w_def))
+
+        return defs_w, kw_defs_w
 
     # lazy binding to space
 
@@ -1027,9 +1053,10 @@
     def build(cache, gateway):
         "NOT_RPYTHON"
         space = cache.space
-        defs = gateway._getdefaults(space) # needs to be implemented by 
subclass
+        defs_w, kw_defs_w = gateway._getdefaults(space)
         code = gateway._code
-        fn = FunctionWithFixedCode(space, code, None, defs, 
forcename=gateway.name)
+        fn = FunctionWithFixedCode(space, code, None, defs_w, kw_defs_w,
+                                   forcename=gateway.name)
         if not space.config.translating:
             fn.add_to_table()
         if gateway.as_classmethod:
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -1235,15 +1235,15 @@
             for i in range(len(names_w) - 1, -1, -1):
                 space.setitem(w_ann, names_w[i], self.popvalue())
         defaultarguments = self.popvalues(posdefaults)
-        w_kw_defs = None
+        kw_defs_w = None
         if kwdefaults:
-            w_kw_defs = space.newdict(strdict=True)
-            for i in range(kwdefaults - 1, -1, -1):
-                w_name = self.popvalue()
-                w_def = self.popvalue()
-                space.setitem(w_kw_defs, w_def, w_name)
+            kw_defs_w = []
+            for i in range(kwdefaults):
+                w_defvalue = self.popvalue()
+                w_defname = self.popvalue()
+                kw_defs_w.append((w_defname, w_defvalue))
         fn = function.Function(space, codeobj, self.get_w_globals(), 
defaultarguments,
-                               w_kw_defs, freevars, w_ann, qualname=qualname)
+                               kw_defs_w, freevars, w_ann, qualname=qualname)
         self.pushvalue(space.wrap(fn))
 
     def MAKE_FUNCTION(self, oparg, next_instr):
diff --git a/pypy/interpreter/signature.py b/pypy/interpreter/signature.py
--- a/pypy/interpreter/signature.py
+++ b/pypy/interpreter/signature.py
@@ -39,6 +39,7 @@
 
     def scope_length(self):
         scopelen = len(self.argnames)
+        scopelen += len(self.kwonlyargnames)
         scopelen += self.has_vararg()
         scopelen += self.has_kwarg()
         return scopelen
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
@@ -30,7 +30,7 @@
         assert sig.num_argnames() == 3
         assert sig.has_vararg()
         assert sig.has_kwarg()
-        assert sig.scope_length() == 5
+        assert sig.scope_length() == 6
         assert sig.getallvarnames() == ["a", "b", "c", "d", "kwonly", "c"]
 
     def test_eq(self):
diff --git a/pypy/interpreter/test/test_gateway.py 
b/pypy/interpreter/test/test_gateway.py
--- a/pypy/interpreter/test/test_gateway.py
+++ b/pypy/interpreter/test/test_gateway.py
@@ -47,6 +47,12 @@
         code = gateway.BuiltinCode(f, unwrap_spec=[gateway.ObjSpace, "index"])
         assert code.signature() == Signature(["index"], None, None)
 
+        def f(space, __kwonly__, w_x):
+            pass
+        code = gateway.BuiltinCode(f, unwrap_spec=[gateway.ObjSpace,
+                                                   "kwonly", W_Root])
+        assert code.signature() == Signature([], kwonlyargnames=['x'])
+
 
     def test_call(self):
         def c(space, w_x, w_y, hello_w):
@@ -753,7 +759,7 @@
         @gateway.unwrap_spec(w_x = WrappedDefault(42), y=int)
         def g(space, w_x, y):
             never_called
-        py.test.raises(AssertionError, space.wrap, gateway.interp2app_temp(g))
+        py.test.raises(KeyError, space.wrap, gateway.interp2app_temp(g))
 
     def test_unwrap_spec_default_applevel_bug2(self):
         space = self.space
@@ -803,6 +809,80 @@
         w_res = space.call_args(w_g, args)
         assert space.eq_w(w_res, space.newbytes('foo'))
 
+    def test_unwrap_spec_kwonly(self):
+        space = self.space
+        def g(space, w_x, __kwonly__, w_y):
+            return space.sub(w_x, w_y)
+        w_g = space.wrap(gateway.interp2app_temp(g))
+        w = space.wrap
+        w1 = w(1)
+
+        for i in range(4):
+            a = argument.Arguments(space, [w1, w1, w1])
+            py.test.raises(gateway.OperationError, space.call_args, w_g, a)
+            py.test.raises(gateway.OperationError, space.call_function, w_g,
+                           *(i * (w1,)))
+
+        args = argument.Arguments(space, [w(1)],
+                                  w_starstararg = w({'y': 10}))
+        assert space.eq_w(space.call_args(w_g, args), w(-9))
+        args = argument.Arguments(space, [],
+                                  w_starstararg = w({'x': 2, 'y': 10}))
+        assert space.eq_w(space.call_args(w_g, args), w(-8))
+
+    def test_unwrap_spec_kwonly_default(self):
+        space = self.space
+        @gateway.unwrap_spec(w_x2=WrappedDefault(50), y2=int)
+        def g(space, w_x1, w_x2, __kwonly__, w_y1, y2=200):
+            return space.sub(space.sub(w_x1, w_x2),
+                             space.sub(w_y1, w(y2)))
+        w_g = space.wrap(gateway.interp2app_temp(g))
+        w = space.wrap
+        w1 = w(1)
+
+        for i in range(6):
+            py.test.raises(gateway.OperationError, space.call_function, w_g,
+                           *(i * (w1,)))
+
+        def expected(x1, x2=50, y1="missing", y2=200):
+            return (x1 - x2) - (y1 - y2)
+
+        def check(*args, **kwds):
+            a = argument.Arguments(space, [], w_stararg = w(args),
+                                          w_starstararg = w(kwds))
+            w_res = space.call_args(w_g, a)
+            assert space.eq_w(w_res, w(expected(*args, **kwds)))
+
+            del kwds['y1']
+            a = argument.Arguments(space, [], w_stararg = w(args),
+                                          w_starstararg = w(kwds))
+            py.test.raises(gateway.OperationError, space.call_args, w_g, a)
+
+            args += (1234,)
+            a = argument.Arguments(space, [], w_stararg = w(args),
+                                          w_starstararg = w(kwds))
+            py.test.raises(gateway.OperationError, space.call_args, w_g, a)
+
+        check(5,       y1=1234)
+        check(5, 1,    y1=1234)
+        check(5, x2=1, y1=1234)
+        check(5,       y1=1234, y2=343)
+        check(5, 1,    y1=1234, y2=343)
+        check(5, x2=1, y1=1234, y2=343)
+        check(x1=5,       y1=1234,       )
+        check(x1=5, x2=1, y1=1234,       )
+        check(x1=5,       y1=1234, y2=343)
+        check(x1=5, x2=1, y1=1234, y2=343)
+
+    def test_unwrap_spec_kwonly_default_2(self):
+        space = self.space
+        @gateway.unwrap_spec(w_x2=WrappedDefault(50))
+        def g(space, w_x2=None):
+            return w_x2
+        w_g = space.wrap(gateway.interp2app_temp(g))
+        w_res = space.call_function(w_g)
+        assert space.eq_w(w_res, space.wrap(50))
+
 
 class AppTestPyTestMark:
     @py.test.mark.unlikely_to_exist
diff --git a/pypy/interpreter/test/test_interpreter.py 
b/pypy/interpreter/test/test_interpreter.py
--- a/pypy/interpreter/test/test_interpreter.py
+++ b/pypy/interpreter/test/test_interpreter.py
@@ -379,6 +379,20 @@
         assert X().f() == 42
         """
 
+    def test_kwonlyarg_required(self):
+        """
+        def f(*, a=5, b):
+            return (a, b)
+        assert f(b=10) == (5, 10)
+        assert f(a=7, b=12) == (7, 12)
+        raises(TypeError, f)
+        raises(TypeError, f, 1)
+        raises(TypeError, f, 1, 1)
+        raises(TypeError, f, a=1)
+        raises(TypeError, f, 1, a=1)
+        raises(TypeError, f, 1, b=1)
+        """
+
     def test_extended_unpacking_short(self):
         """
         class Seq:
diff --git a/pypy/module/posix/interp_posix.py 
b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -15,8 +15,7 @@
 from rpython.rlib.unroll import unrolling_iterable
 from rpython.tool.sourcetools import func_with_new_name
 
-from pypy.interpreter.gateway import (
-    unwrap_spec, WrappedDefault, Unwrapper, kwonly)
+from pypy.interpreter.gateway import unwrap_spec, WrappedDefault, Unwrapper
 from pypy.interpreter.error import (
     OperationError, oefmt, wrap_oserror, wrap_oserror2, strerror as _strerror)
 from pypy.interpreter.executioncontext import ExecutionContext
@@ -211,7 +210,8 @@
             "%s: %s unavailable on this platform", funcname, arg)
 
 @unwrap_spec(flags=c_int, mode=c_int, dir_fd=DirFD(rposix.HAVE_OPENAT))
-def open(space, w_path, flags, mode=0777, dir_fd=DEFAULT_DIR_FD):
+def open(space, w_path, flags, mode=0777,
+         __kwonly__=None, dir_fd=DEFAULT_DIR_FD):
     """open(path, flags, mode=0o777, *, dir_fd=None)
 
 Open a file for low level IO.  Returns a file handle (integer).
@@ -428,8 +428,8 @@
 @unwrap_spec(
     path=path_or_fd(allow_fd=True),
     dir_fd=DirFD(rposix.HAVE_FSTATAT),
-    follow_symlinks=kwonly(bool))
-def stat(space, path, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True):
+    follow_symlinks=bool)
+def stat(space, path, __kwonly__, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True):
     """stat(path, *, dir_fd=None, follow_symlinks=True) -> stat result
 
 Perform a stat system call on the given path.
@@ -476,7 +476,7 @@
 @unwrap_spec(
     path=path_or_fd(allow_fd=False),
     dir_fd=DirFD(rposix.HAVE_FSTATAT))
-def lstat(space, path, dir_fd=DEFAULT_DIR_FD):
+def lstat(space, path, __kwonly__, dir_fd=DEFAULT_DIR_FD):
     """lstat(path, *, dir_fd=None) -> stat result
 
 Like stat(), but do not follow symbolic links.
@@ -551,9 +551,9 @@
         raise wrap_oserror(space, e)
 
 @unwrap_spec(mode=c_int,
-    dir_fd=DirFD(rposix.HAVE_FACCESSAT), effective_ids=kwonly(bool),
-    follow_symlinks=kwonly(bool))
-def access(space, w_path, mode,
+    dir_fd=DirFD(rposix.HAVE_FACCESSAT), effective_ids=bool,
+    follow_symlinks=bool)
+def access(space, w_path, mode, __kwonly__,
         dir_fd=DEFAULT_DIR_FD, effective_ids=False, follow_symlinks=True):
     """\
 access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True)
@@ -626,7 +626,7 @@
         return space.wrap(rc)
 
 @unwrap_spec(dir_fd=DirFD(rposix.HAVE_UNLINKAT))
-def unlink(space, w_path, dir_fd=DEFAULT_DIR_FD):
+def unlink(space, w_path, __kwonly__, dir_fd=DEFAULT_DIR_FD):
     """unlink(path, *, dir_fd=None)
 
 Remove a file (same as remove()).
@@ -645,7 +645,7 @@
         raise wrap_oserror2(space, e, w_path)
 
 @unwrap_spec(dir_fd=DirFD(rposix.HAVE_UNLINKAT))
-def remove(space, w_path, dir_fd=DEFAULT_DIR_FD):
+def remove(space, w_path, __kwonly__, dir_fd=DEFAULT_DIR_FD):
     """remove(path, *, dir_fd=None)
 
 Remove a file (same as unlink()).
@@ -710,7 +710,7 @@
         raise wrap_oserror2(space, e, w_path)
 
 @unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_MKDIRAT))
-def mkdir(space, w_path, mode=0o777, dir_fd=DEFAULT_DIR_FD):
+def mkdir(space, w_path, mode=0o777, __kwonly__=None, dir_fd=DEFAULT_DIR_FD):
     """mkdir(path, mode=0o777, *, dir_fd=None)
 
 Create a directory.
@@ -731,7 +731,7 @@
         raise wrap_oserror2(space, e, w_path)
 
 @unwrap_spec(dir_fd=DirFD(rposix.HAVE_UNLINKAT))
-def rmdir(space, w_path, dir_fd=DEFAULT_DIR_FD):
+def rmdir(space, w_path, __kwonly__, dir_fd=DEFAULT_DIR_FD):
     """rmdir(path, *, dir_fd=None)
 
 Remove a directory.
@@ -898,8 +898,9 @@
     return space.newtuple([space.wrap(fd1), space.wrap(fd2)])
 
 @unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_FCHMODAT),
-             follow_symlinks=kwonly(bool))
-def chmod(space, w_path, mode, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True):
+             follow_symlinks=bool)
+def chmod(space, w_path, mode, __kwonly__,
+          dir_fd=DEFAULT_DIR_FD, follow_symlinks=True):
     """chmod(path, mode, *, dir_fd=None, follow_symlinks=True)
 
 Change the access permissions of a file.
@@ -965,7 +966,7 @@
 
 @unwrap_spec(src_dir_fd=DirFD(rposix.HAVE_RENAMEAT),
         dst_dir_fd=DirFD(rposix.HAVE_RENAMEAT))
-def rename(space, w_src, w_dst,
+def rename(space, w_src, w_dst, __kwonly__,
         src_dir_fd=DEFAULT_DIR_FD, dst_dir_fd=DEFAULT_DIR_FD):
     """rename(src, dst, *, src_dir_fd=None, dst_dir_fd=None)
 
@@ -989,7 +990,7 @@
 
 @unwrap_spec(src_dir_fd=DirFD(rposix.HAVE_RENAMEAT),
         dst_dir_fd=DirFD(rposix.HAVE_RENAMEAT))
-def replace(space, w_src, w_dst,
+def replace(space, w_src, w_dst, __kwonly__,
         src_dir_fd=DEFAULT_DIR_FD, dst_dir_fd=DEFAULT_DIR_FD):
     """replace(src, dst, *, src_dir_fd=None, dst_dir_fd=None)
 
@@ -1012,7 +1013,7 @@
         raise wrap_oserror(space, e)
 
 @unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_MKFIFOAT))
-def mkfifo(space, w_path, mode=0666, dir_fd=DEFAULT_DIR_FD):
+def mkfifo(space, w_path, mode=0666, __kwonly__=None, dir_fd=DEFAULT_DIR_FD):
     """mkfifo(path, mode=0o666, *, dir_fd=None)
 
 Create a FIFO (a POSIX named pipe).
@@ -1031,7 +1032,8 @@
         raise wrap_oserror2(space, e, w_path)
 
 @unwrap_spec(mode=c_int, device=c_int, dir_fd=DirFD(rposix.HAVE_MKNODAT))
-def mknod(space, w_filename, mode=0600, device=0, dir_fd=DEFAULT_DIR_FD):
+def mknod(space, w_filename, mode=0600, device=0,
+          __kwonly__=None, dir_fd=DEFAULT_DIR_FD):
     """mknod(filename, mode=0o600, device=0, *, dir_fd=None)
 
 Create a filesystem node (file, device special file or named pipe)
@@ -1093,9 +1095,9 @@
 @unwrap_spec(
     src='fsencode', dst='fsencode',
     src_dir_fd=DirFD(rposix.HAVE_LINKAT), dst_dir_fd=DirFD(rposix.HAVE_LINKAT),
-    follow_symlinks=kwonly(bool))
+    follow_symlinks=bool)
 def link(
-        space, src, dst,
+        space, src, dst, __kwonly__,
         src_dir_fd=DEFAULT_DIR_FD, dst_dir_fd=DEFAULT_DIR_FD,
         follow_symlinks=True):
     """\
@@ -1125,7 +1127,7 @@
 
 @unwrap_spec(dir_fd=DirFD(rposix.HAVE_SYMLINKAT))
 def symlink(space, w_src, w_dst, w_target_is_directory=None,
-        dir_fd=DEFAULT_DIR_FD):
+            __kwonly__=None, dir_fd=DEFAULT_DIR_FD):
     """symlink(src, dst, target_is_directory=False, *, dir_fd=None)
 
 Create a symbolic link pointing to src named dst.
@@ -1153,7 +1155,7 @@
 @unwrap_spec(
     path=path_or_fd(allow_fd=False),
     dir_fd=DirFD(rposix.HAVE_READLINKAT))
-def readlink(space, path, dir_fd=DEFAULT_DIR_FD):
+def readlink(space, path, __kwonly__, dir_fd=DEFAULT_DIR_FD):
     """readlink(path, *, dir_fd=None) -> path
 
 Return a string representing the path to which the symbolic link points.
@@ -1353,9 +1355,9 @@
 
 @unwrap_spec(
     path=path_or_fd(allow_fd=rposix.HAVE_FUTIMENS or rposix.HAVE_FUTIMES),
-    w_times=WrappedDefault(None), w_ns=kwonly(WrappedDefault(None)),
-    dir_fd=DirFD(rposix.HAVE_UTIMENSAT), follow_symlinks=kwonly(bool))
-def utime(space, path, w_times, w_ns, dir_fd=DEFAULT_DIR_FD,
+    w_times=WrappedDefault(None), w_ns=WrappedDefault(None),
+    dir_fd=DirFD(rposix.HAVE_UTIMENSAT), follow_symlinks=bool)
+def utime(space, path, w_times, __kwonly__, w_ns, dir_fd=DEFAULT_DIR_FD,
           follow_symlinks=True):
     """utime(path, times=None, *, ns=None, dir_fd=None, follow_symlinks=True)
 
@@ -1889,8 +1891,9 @@
 
 @unwrap_spec(
     uid=c_uid_t, gid=c_gid_t,
-    dir_fd=DirFD(rposix.HAVE_FCHOWNAT), follow_symlinks=kwonly(bool))
-def chown(space, w_path, uid, gid, dir_fd=DEFAULT_DIR_FD, 
follow_symlinks=True):
+    dir_fd=DirFD(rposix.HAVE_FCHOWNAT), follow_symlinks=bool)
+def chown(space, w_path, uid, gid, __kwonly__,
+          dir_fd=DEFAULT_DIR_FD, follow_symlinks=True):
     """chown(path, uid, gid, *, dir_fd=None, follow_symlinks=True)
 
 Change the owner and group id of path to the numeric uid and gid.
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -337,7 +337,7 @@
             space = self.space
             w_dict = obj.getdict(space)
             try:
-                space.delitem(w_dict, space.wrap(name))
+                space.delitem(w_dict, space.wrap(name.decode('utf-8')))
             except OperationError as ex:
                 if not ex.match(space, space.w_KeyError):
                     raise
@@ -402,7 +402,7 @@
     def materialize_r_dict(self, space, obj, dict_w):
         new_obj = self.back.materialize_r_dict(space, obj, dict_w)
         if self.index == DICT:
-            w_attr = space.wrap(self.name)
+            w_attr = space.wrap(self.name.decode('utf-8'))
             dict_w[w_attr] = obj._mapdict_read_storage(self.storageindex)
         else:
             self._copy_attr(obj, new_obj)
@@ -810,7 +810,7 @@
             raise KeyError
         key = curr.name
         w_value = self.getitem_str(w_dict, key)
-        w_key = self.space.wrap(key)
+        w_key = self.space.wrap(key.decode('utf-8'))
         self.delitem(w_dict, w_key)
         return (w_key, w_value)
 
@@ -845,7 +845,7 @@
             if curr_map:
                 self.curr_map = curr_map.back
                 attr = curr_map.name
-                w_attr = self.space.wrap(attr)
+                w_attr = self.space.wrap(attr.decode('utf-8'))
                 return w_attr
         return None
 
@@ -886,7 +886,7 @@
             if curr_map:
                 self.curr_map = curr_map.back
                 attr = curr_map.name
-                w_attr = self.space.wrap(attr)
+                w_attr = self.space.wrap(attr.decode('utf-8'))
                 return w_attr, self.w_obj.getdictvalue(self.space, attr)
         return None, None
 
diff --git a/pypy/objspace/std/test/test_mapdict.py 
b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
 from pypy.objspace.std.test.test_dictmultiobject import FakeSpace, W_DictObject
 from pypy.objspace.std.mapdict import *
 
@@ -873,6 +875,15 @@
         d = x.__dict__
         assert list(__pypy__.reversed_dict(d)) == list(d.keys())[::-1]
 
+    def test_nonascii_argname(self):
+        """
+        class X:
+            pass
+        x = X()
+        x.&#26085;&#26412; = 3
+        assert x.&#26085;&#26412; == 3
+        assert x.__dict__ == {'&#26085;&#26412;': 3}
+        """
 
 class AppTestWithMapDictAndCounters(object):
     spaceconfig = {"objspace.std.withmethodcachecounter": True}
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to