[pypy-svn] r63754 - in pypy/branch/classdeco/pypy/interpreter: . astcompiler pyparser pyparser/data test

benjamin at codespeak.net benjamin at codespeak.net
Mon Apr 6 23:53:09 CEST 2009


Author: benjamin
Date: Mon Apr  6 23:53:08 2009
New Revision: 63754

Modified:
   pypy/branch/classdeco/pypy/interpreter/astcompiler/ast.py
   pypy/branch/classdeco/pypy/interpreter/astcompiler/ast.txt
   pypy/branch/classdeco/pypy/interpreter/astcompiler/pycodegen.py
   pypy/branch/classdeco/pypy/interpreter/astcompiler/symbols.py
   pypy/branch/classdeco/pypy/interpreter/pycompiler.py
   pypy/branch/classdeco/pypy/interpreter/pyparser/astbuilder.py
   pypy/branch/classdeco/pypy/interpreter/pyparser/data/Grammar2.6
   pypy/branch/classdeco/pypy/interpreter/pyparser/pythonutil.py
   pypy/branch/classdeco/pypy/interpreter/test/test_syntax.py
Log:
add support for class decorators

Modified: pypy/branch/classdeco/pypy/interpreter/astcompiler/ast.py
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/astcompiler/ast.py	(original)
+++ pypy/branch/classdeco/pypy/interpreter/astcompiler/ast.py	Mon Apr  6 23:53:08 2009
@@ -1498,8 +1498,9 @@
 CallFunc.typedef.acceptable_as_base_class = False
 
 class Class(Node):
-    def __init__(self, name, bases, w_doc, code, lineno=-1):
+    def __init__(self, decorators, name, bases, w_doc, code, lineno=-1):
         Node.__init__(self, lineno)
+        self.decorators = decorators
         self.name = name
         self.bases = bases
         self.w_doc = w_doc
@@ -1508,6 +1509,7 @@
     def getChildren(self):
         "NOT_RPYTHON"
         children = []
+        children.append(self.decorators)
         children.append(self.name)
         children.extend(flatten(self.bases))
         children.append(self.w_doc)
@@ -1516,21 +1518,32 @@
 
     def getChildNodes(self):
         nodelist = []
+        if self.decorators is not None:
+            nodelist.append(self.decorators)
         nodelist.extend(self.bases)
         nodelist.append(self.code)
         return nodelist
 
     def __repr__(self):
-        return "Class(%s, %s, %s, %s)" % (self.name.__repr__(), self.bases.__repr__(), self.w_doc.__repr__(), self.code.__repr__())
+        return "Class(%s, %s, %s, %s, %s)" % (self.decorators.__repr__(), self.name.__repr__(), self.bases.__repr__(), self.w_doc.__repr__(), self.code.__repr__())
 
     def accept(self, visitor):
         return visitor.visitClass(self)
 
     def mutate(self, visitor):
+        if self.decorators is not None:
+            self.decorators = self.decorators.mutate(visitor)
         visitor._mutate_list(self.bases)
         self.code = self.code.mutate(visitor)
         return visitor.visitClass(self)
 
+    def fget_decorators( space, self):
+        if self.decorators is None:
+            return space.w_None
+        else:
+            return space.wrap(self.decorators)
+    def fset_decorators( space, self, w_arg):
+        self.decorators = space.interp_w(Node, w_arg, can_be_None=True)
     def fget_name( space, self):
         return space.wrap(self.name)
     def fset_name( space, self, w_arg):
@@ -1550,8 +1563,10 @@
     def fset_code( space, self, w_arg):
         self.code = space.interp_w(Node, w_arg, can_be_None=False)
 
-def descr_Class_new(space, w_subtype, w_name, w_bases, w_w_doc, w_code, lineno=-1):
+def descr_Class_new(space, w_subtype, w_decorators, w_name, w_bases, w_w_doc, w_code, lineno=-1):
     self = space.allocate_instance(Class, w_subtype)
+    decorators = space.interp_w(Node, w_decorators, can_be_None=True)
+    self.decorators = decorators
     name = space.str_w(w_name)
     self.name = name
     bases = [space.interp_w(Node, w_node) for w_node in space.unpackiterable(w_bases)]
@@ -1568,6 +1583,12 @@
     return space.call_method(w_visitor, 'visitClass', w_self)
 
 def descr_Class_mutate(space, w_self, w_visitor): 
+    w_decorators = space.getattr(w_self, space.wrap("decorators"))
+    if not space.is_w(w_decorators, space.w_None):
+        space.setattr(w_decorators, space.wrap("parent"), w_self)
+        w_new_decorators = space.call_method(w_decorators, "mutate", w_visitor)
+        space.setattr(w_self, space.wrap("decorators"), w_new_decorators)
+
     w_list = space.getattr(w_self, space.wrap("bases"))
     list_w = space.unpackiterable(w_list)
     newlist_w = []
@@ -1586,9 +1607,10 @@
     return space.call_method(w_visitor, "visitClass", w_self)
 
 Class.typedef = TypeDef('Class', Node.typedef, 
-                     __new__ = interp2app(descr_Class_new, unwrap_spec=[ObjSpace, W_Root, W_Root, W_Root, W_Root, W_Root, int]),
+                     __new__ = interp2app(descr_Class_new, unwrap_spec=[ObjSpace, W_Root, W_Root, W_Root, W_Root, W_Root, W_Root, int]),
                      accept=interp2app(descr_Class_accept, unwrap_spec=[ObjSpace, W_Root, W_Root] ),
                      mutate=interp2app(descr_Class_mutate, unwrap_spec=[ObjSpace, W_Root, W_Root] ),
+                    decorators=GetSetProperty(Class.fget_decorators, Class.fset_decorators ),
                     name=GetSetProperty(Class.fget_name, Class.fset_name ),
                     bases=GetSetProperty(Class.fget_bases, Class.fset_bases ),
                     w_doc=GetSetProperty(Class.fget_w_doc, Class.fset_w_doc ),

Modified: pypy/branch/classdeco/pypy/interpreter/astcompiler/ast.txt
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/astcompiler/ast.txt	(original)
+++ pypy/branch/classdeco/pypy/interpreter/astcompiler/ast.txt	Mon Apr  6 23:53:08 2009
@@ -22,7 +22,7 @@
 AbstractFunction:
 Function(AbstractFunction): decorators&, name*str, argnames!, defaults!, flags*int, w_doc%, code
 Lambda(AbstractFunction): argnames!, defaults!, flags*int, code
-Class: name*str, bases!, w_doc%, code
+Class: decorators&, name*str, bases!, w_doc%, code
 Pass:
 Break:
 Continue:

Modified: pypy/branch/classdeco/pypy/interpreter/astcompiler/pycodegen.py
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/astcompiler/pycodegen.py	(original)
+++ pypy/branch/classdeco/pypy/interpreter/astcompiler/pycodegen.py	Mon Apr  6 23:53:08 2009
@@ -327,6 +327,12 @@
             self.emitop_int('MAKE_FUNCTION', args)
 
     def visitClass(self, node):
+        if node.decorators:
+            for dec in node.decorators.nodes:
+                dec.accept(self)
+            ndecorators = len(node.decorators.nodes)
+        else:
+            ndecorators = 0
         gen = ClassCodeGenerator(self.space, node,
                                  self.get_module())
         node.code.accept( gen )
@@ -339,6 +345,8 @@
         self._makeClosure(gen, 0)
         self.emitop_int('CALL_FUNCTION', 0)
         self.emit('BUILD_CLASS')
+        for i in range(ndecorators):
+            self.emitop_int('CALL_FUNCTION', 1)
         self.storeName(node.name, node.lineno)
 
     # The rest are standard visitor methods

Modified: pypy/branch/classdeco/pypy/interpreter/astcompiler/symbols.py
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/astcompiler/symbols.py	(original)
+++ pypy/branch/classdeco/pypy/interpreter/astcompiler/symbols.py	Mon Apr  6 23:53:08 2009
@@ -440,6 +440,8 @@
     def visitClass(self, node):
         parent = self.cur_scope()
         parent.add_def(node.name)
+        if node.decorators:
+            node.decorators.accept(self)
         for n in node.bases:
             n.accept(self)
         scope = ClassScope(node.name, parent)

Modified: pypy/branch/classdeco/pypy/interpreter/pycompiler.py
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/pycompiler.py	(original)
+++ pypy/branch/classdeco/pypy/interpreter/pycompiler.py	Mon Apr  6 23:53:08 2009
@@ -217,7 +217,7 @@
 
         from pyparser.pythonparse import make_pyparser
         PyCodeCompiler.__init__(self, space)
-        self.grammar_version = override_version or "2.5"
+        self.grammar_version = override_version or "2.6"
         self.parser = make_pyparser(self.grammar_version)
         self.additional_rules = {}
         if self.grammar_version >= '2.5':

Modified: pypy/branch/classdeco/pypy/interpreter/pyparser/astbuilder.py
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/pyparser/astbuilder.py	(original)
+++ pypy/branch/classdeco/pypy/interpreter/pyparser/astbuilder.py	Mon Apr  6 23:53:08 2009
@@ -588,26 +588,9 @@
     builder.push(obj)
 
 def build_funcdef(builder, nb):
-    """funcdef: [decorators] 'def' NAME parameters ':' suite
+    """funcdef: 'def' NAME parameters ':' suite
     """
     atoms = get_atoms(builder, nb)
-    index = 0
-    decorators = []
-    decorator_node = None
-    lineno = atoms[0].lineno
-    # the original loop was:
-    # while not (isinstance(atoms[index], TokenObject) and atoms[index].get_value() == 'def'):
-    #     decorators.append(atoms[index])
-    #     index += 1
-    while index < len(atoms):
-        atom = atoms[index]
-        if isinstance(atom, TokenObject) and atom.get_value() == 'def':
-            break
-        decorators.append(atoms[index])
-        index += 1
-    if decorators:
-        decorator_node = ast.Decorators(decorators, lineno)
-    atoms = atoms[index:]
     funcname = atoms[1]
     lineno = funcname.lineno
     arglist = []
@@ -621,8 +604,17 @@
     arglist = atoms[2]
     code = atoms[-1]
     doc = get_docstring(builder, code)
-    builder.push(ast.Function(decorator_node, funcname, names, default, flags, doc, code, lineno))
+    builder.push(ast.Function(None, funcname, names, default, flags, doc, code, lineno))
+
+def build_decorators(builder, nb):
+    builder.push(ast.Decorators(get_atoms(builder, nb)))
 
+def build_decoratored(builder, nb):
+    """ decoratored: decorators (classdef | funcdef)
+    """
+    decorators, decorated = get_atoms(builder, nb)
+    decorated.decorators = decorators
+    builder.push(decorated)
 
 def build_classdef(builder, nb):
     """classdef: 'class' NAME ['(' [testlist] ')'] ':' suite"""
@@ -649,7 +641,7 @@
         else:
             basenames.append(base)
     doc = get_docstring(builder,body)
-    builder.push(ast.Class(classname, basenames, doc, body, lineno))
+    builder.push(ast.Class(None, classname, basenames, doc, body, lineno))
 
 def build_suite(builder, nb):
     """suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT"""
@@ -1061,6 +1053,8 @@
     'arglist' : build_arglist,
     'subscript' : build_subscript,
     'listmaker' : build_listmaker,
+    'decorated' : build_decoratored,
+    'decorators' : build_decorators,
     'funcdef' : build_funcdef,
     'classdef' : build_classdef,
     'return_stmt' : build_return_stmt,

Modified: pypy/branch/classdeco/pypy/interpreter/pyparser/data/Grammar2.6
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/pyparser/data/Grammar2.6	(original)
+++ pypy/branch/classdeco/pypy/interpreter/pyparser/data/Grammar2.6	Mon Apr  6 23:53:08 2009
@@ -33,7 +33,8 @@
 
 decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
 decorators: decorator+
-funcdef: [decorators] 'def' NAME parameters ':' suite
+decorated: decorators (classdef | funcdef)
+funcdef: 'def' NAME parameters ':' suite
 parameters: '(' [varargslist] ')'
 varargslist: ((fpdef ['=' test] ',')*
               ('*' NAME [',' '**' NAME] | '**' NAME) |
@@ -73,7 +74,7 @@
 exec_stmt: 'exec' expr ['in' test [',' test]]
 assert_stmt: 'assert' test [',' test]
 
-compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef
+compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
 if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
 while_stmt: 'while' test ':' suite ['else' ':' suite]
 for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]

Modified: pypy/branch/classdeco/pypy/interpreter/pyparser/pythonutil.py
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/pyparser/pythonutil.py	(original)
+++ pypy/branch/classdeco/pypy/interpreter/pyparser/pythonutil.py	Mon Apr  6 23:53:08 2009
@@ -37,7 +37,7 @@
         _ver = PYTHON_VERSION
     elif version == "stable":
         _ver = "_stablecompiler"
-    elif version in ("2.3","2.4","2.5a","2.5"):
+    elif version in ("2.3","2.4","2.5a","2.5", "2.6"):
         _ver = version
     else:
         raise ValueError('no such grammar version: %s' % version)

Modified: pypy/branch/classdeco/pypy/interpreter/test/test_syntax.py
==============================================================================
--- pypy/branch/classdeco/pypy/interpreter/test/test_syntax.py	(original)
+++ pypy/branch/classdeco/pypy/interpreter/test/test_syntax.py	Mon Apr  6 23:53:08 2009
@@ -578,6 +578,51 @@
         """
         exec s
 
+
+class AppTestClassDecorators:
+
+    def test_applied(self):
+        prog = """def my_dec(cls):
+    cls.testing = True
+    return cls
+ at my_dec
+class my_class: pass
+"""
+        ns = {}
+        exec prog in ns
+        assert ns["my_class"].testing
+
+    def test_stacked(self):
+        prog = """def one(cls):
+    record.append("one")
+    return cls
+def two(cls):
+    record.append("two")
+    return cls
+record = []
+ at two
+ at one
+class my_class: pass
+"""
+        ns = {}
+        exec prog in ns
+        assert ns["record"] == ["one", "two"]
+
+    def test_attribute(self):
+        prog = """def my_deco(cls):
+    cls.testing = True
+    return cls
+class fake_mod: pass
+x = fake_mod()
+x.dec = my_deco
+ at x.dec
+class myclass: pass
+"""
+        ns = {}
+        exec prog in ns
+        assert ns["myclass"].testing
+
+
 class AppTestSyntaxError:
 
     def test_tokenizer_error_location(self):



More information about the Pypy-commit mailing list