[pypy-commit] pypy py3k: (antocuni, romain) implement keyword only args in the astbuilder

rguillebert noreply at buildbot.pypy.org
Thu Jan 19 12:05:19 CET 2012


Author: Romain Guillebert <romain.py at gmail.com>
Branch: py3k
Changeset: r51475:27f5a4ae0d7d
Date: 2012-01-19 12:04 +0100
http://bitbucket.org/pypy/pypy/changeset/27f5a4ae0d7d/

Log:	(antocuni, romain) implement keyword only args in the astbuilder

diff --git a/pypy/interpreter/astcompiler/ast.py b/pypy/interpreter/astcompiler/ast.py
--- a/pypy/interpreter/astcompiler/ast.py
+++ b/pypy/interpreter/astcompiler/ast.py
@@ -2280,18 +2280,22 @@
 
 class arguments(AST):
 
-    def __init__(self, args, vararg, kwarg, defaults):
+    def __init__(self, args, vararg, kwonlyargs, kwarg, defaults):
         self.args = args
         self.w_args = None
         self.vararg = vararg
+        self.kwonlyargs = kwonlyargs
+        self.w_kwonlyargs = None
         self.kwarg = kwarg
         self.defaults = defaults
         self.w_defaults = None
-        self.initialization_state = 15
+        self.initialization_state = 31
 
     def mutate_over(self, visitor):
         if self.args:
             visitor._mutate_sequence(self.args)
+        if self.kwonlyargs:
+            visitor._mutate_sequence(self.kwonlyargs)
         if self.defaults:
             visitor._mutate_sequence(self.defaults)
         return visitor.visit_arguments(self)
@@ -2300,12 +2304,12 @@
         visitor.visit_arguments(self)
 
     def sync_app_attrs(self, space):
-        if (self.initialization_state & ~6) ^ 9:
-            self.missing_field(space, ['args', None, None, 'defaults'], 'arguments')
+        if (self.initialization_state & ~10) ^ 21:
+            self.missing_field(space, ['args', None, 'kwonlyargs', None, 'defaults'], 'arguments')
         else:
             if not self.initialization_state & 2:
                 self.vararg = None
-            if not self.initialization_state & 4:
+            if not self.initialization_state & 8:
                 self.kwarg = None
         w_list = self.w_args
         if w_list is not None:
@@ -2317,6 +2321,16 @@
         if self.args is not None:
             for node in self.args:
                 node.sync_app_attrs(space)
+        w_list = self.w_kwonlyargs
+        if w_list is not None:
+            list_w = space.listview(w_list)
+            if list_w:
+                self.kwonlyargs = [space.interp_w(expr, w_obj) for w_obj in list_w]
+            else:
+                self.kwonlyargs = None
+        if self.kwonlyargs is not None:
+            for node in self.kwonlyargs:
+                node.sync_app_attrs(space)
         w_list = self.w_defaults
         if w_list is not None:
             list_w = space.listview(w_list)
@@ -2727,6 +2741,7 @@
 
     def visit_arguments(self, node):
         self.visit_sequence(node.args)
+        self.visit_sequence(node.kwonlyargs)
         self.visit_sequence(node.defaults)
 
     def visit_keyword(self, node):
@@ -6908,12 +6923,29 @@
     w_self.deldictvalue(space, 'vararg')
     w_self.initialization_state |= 2
 
+def arguments_get_kwonlyargs(space, w_self):
+    if not w_self.initialization_state & 4:
+        typename = space.type(w_self).getname(space)
+        raise operationerrfmt(space.w_AttributeError, "'%s' object has no attribute '%s'", typename, 'kwonlyargs')
+    if w_self.w_kwonlyargs is None:
+        if w_self.kwonlyargs is None:
+            list_w = []
+        else:
+            list_w = [space.wrap(node) for node in w_self.kwonlyargs]
+        w_list = space.newlist(list_w)
+        w_self.w_kwonlyargs = w_list
+    return w_self.w_kwonlyargs
+
+def arguments_set_kwonlyargs(space, w_self, w_new_value):
+    w_self.w_kwonlyargs = w_new_value
+    w_self.initialization_state |= 4
+
 def arguments_get_kwarg(space, w_self):
     if w_self.w_dict is not None:
         w_obj = w_self.getdictvalue(space, 'kwarg')
         if w_obj is not None:
             return w_obj
-    if not w_self.initialization_state & 4:
+    if not w_self.initialization_state & 8:
         typename = space.type(w_self).getname(space)
         raise operationerrfmt(space.w_AttributeError, "'%s' object has no attribute '%s'", typename, 'kwarg')
     return space.wrap(w_self.kwarg)
@@ -6930,10 +6962,10 @@
         w_self.setdictvalue(space, 'kwarg', w_new_value)
         return
     w_self.deldictvalue(space, 'kwarg')
-    w_self.initialization_state |= 4
+    w_self.initialization_state |= 8
 
 def arguments_get_defaults(space, w_self):
-    if not w_self.initialization_state & 8:
+    if not w_self.initialization_state & 16:
         typename = space.type(w_self).getname(space)
         raise operationerrfmt(space.w_AttributeError, "'%s' object has no attribute '%s'", typename, 'defaults')
     if w_self.w_defaults is None:
@@ -6947,17 +6979,18 @@
 
 def arguments_set_defaults(space, w_self, w_new_value):
     w_self.w_defaults = w_new_value
-    w_self.initialization_state |= 8
-
-_arguments_field_unroller = unrolling_iterable(['args', 'vararg', 'kwarg', 'defaults'])
+    w_self.initialization_state |= 16
+
+_arguments_field_unroller = unrolling_iterable(['args', 'vararg', 'kwonlyargs', 'kwarg', 'defaults'])
 def arguments_init(space, w_self, __args__):
     w_self = space.descr_self_interp_w(arguments, w_self)
     w_self.w_args = None
+    w_self.w_kwonlyargs = None
     w_self.w_defaults = None
     args_w, kwargs_w = __args__.unpack()
     if args_w:
-        if len(args_w) != 4:
-            w_err = space.wrap("arguments constructor takes either 0 or 4 positional arguments")
+        if len(args_w) != 5:
+            w_err = space.wrap("arguments constructor takes either 0 or 5 positional arguments")
             raise OperationError(space.w_TypeError, w_err)
         i = 0
         for field in _arguments_field_unroller:
@@ -6969,9 +7002,10 @@
 arguments.typedef = typedef.TypeDef("arguments",
     AST.typedef,
     __module__='_ast',
-    _fields=_FieldsWrapper(['args', 'vararg', 'kwarg', 'defaults']),
+    _fields=_FieldsWrapper(['args', 'vararg', 'kwonlyargs', 'kwarg', 'defaults']),
     args=typedef.GetSetProperty(arguments_get_args, arguments_set_args, cls=arguments),
     vararg=typedef.GetSetProperty(arguments_get_vararg, arguments_set_vararg, cls=arguments),
+    kwonlyargs=typedef.GetSetProperty(arguments_get_kwonlyargs, arguments_set_kwonlyargs, cls=arguments),
     kwarg=typedef.GetSetProperty(arguments_get_kwarg, arguments_set_kwarg, cls=arguments),
     defaults=typedef.GetSetProperty(arguments_get_defaults, arguments_set_defaults, cls=arguments),
     __new__=interp2app(get_AST_new(arguments)),
diff --git a/pypy/interpreter/astcompiler/astbuilder.py b/pypy/interpreter/astcompiler/astbuilder.py
--- a/pypy/interpreter/astcompiler/astbuilder.py
+++ b/pypy/interpreter/astcompiler/astbuilder.py
@@ -508,13 +508,14 @@
         # and varargslist (lambda definition).
         if arguments_node.type == syms.parameters:
             if len(arguments_node.children) == 2:
-                return ast.arguments(None, None, None, None)
+                return ast.arguments(None, None, None, None, None)
             arguments_node = arguments_node.children[1]
         i = 0
         child_count = len(arguments_node.children)
         defaults = []
         args = []
         variable_arg = None
+        keywordonly_args = None
         keywords_arg = None
         have_default = False
         while i < child_count:
@@ -552,13 +553,16 @@
                         self.check_forbidden_name(arg_name, name_node)
                         name = ast.Name(arg_name, ast.Param, name_node.lineno,
                                         name_node.column)
-                        args.append(name)
+                        if keywordonly_args is None:
+                            args.append(name)
+                        else:
+                            keywordonly_args.append(name)
                     i += 2
                     break
             elif arg_type == tokens.STAR:
                 name_node = arguments_node.children[i + 1]
+                keywordonly_args = []
                 if name_node.type == tokens.COMMA:
-                    # XXX for now
                     i += 2
                 else:
                     variable_arg = name_node.children[0].value
@@ -575,7 +579,7 @@
             defaults = None
         if not args:
             args = None
-        return ast.arguments(args, variable_arg, keywords_arg, defaults)
+        return ast.arguments(args, variable_arg, keywordonly_args, keywords_arg, defaults)
 
     def handle_arg_unpacking(self, fplist_node):
         args = []
@@ -775,7 +779,7 @@
     def handle_lambdef(self, lambdef_node):
         expr = self.handle_expr(lambdef_node.children[-1])
         if len(lambdef_node.children) == 3:
-            args = ast.arguments(None, None, None, None)
+            args = ast.arguments(None, None, None, None, None)
         else:
             args = self.handle_arguments(lambdef_node.children[1])
         return ast.Lambda(args, expr, lambdef_node.lineno, lambdef_node.column)
diff --git a/pypy/interpreter/astcompiler/test/test_astbuilder.py b/pypy/interpreter/astcompiler/test/test_astbuilder.py
--- a/pypy/interpreter/astcompiler/test/test_astbuilder.py
+++ b/pypy/interpreter/astcompiler/test/test_astbuilder.py
@@ -1185,3 +1185,17 @@
         if1, if2 = comps[0].ifs
         assert isinstance(if1, ast.Name)
         assert isinstance(if2, ast.Name)
+
+    def test_kwonly_arguments(self):
+        fn = self.get_first_stmt("def f(a, b, c, *, kwarg): pass")
+        assert isinstance(fn, ast.FunctionDef)
+        assert len(fn.args.kwonlyargs) == 1
+        assert isinstance(fn.args.kwonlyargs[0], ast.expr)
+        assert fn.args.kwonlyargs[0].id == "kwarg"
+
+    def test_kwonly_arguments_2(self):
+        fn = self.get_first_stmt("def f(a, b, c, *args, kwarg): pass")
+        assert isinstance(fn, ast.FunctionDef)
+        assert len(fn.args.kwonlyargs) == 1
+        assert isinstance(fn.args.kwonlyargs[0], ast.expr)
+        assert fn.args.kwonlyargs[0].id == "kwarg"
diff --git a/pypy/interpreter/astcompiler/tools/Python.asdl b/pypy/interpreter/astcompiler/tools/Python.asdl
--- a/pypy/interpreter/astcompiler/tools/Python.asdl
+++ b/pypy/interpreter/astcompiler/tools/Python.asdl
@@ -105,7 +105,7 @@
 	excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
 	                attributes(int lineno, int col_offset)
 
-	arguments = (expr* args, identifier? vararg, 
+	arguments = (expr* args, identifier? vararg, expr* kwonlyargs, 
 		     identifier? kwarg, expr* defaults)
 
         -- keyword arguments supplied to call


More information about the pypy-commit mailing list