cdef public extension type attributes
authorRobert Bradshaw <robertwb@math.washington.edu>
Sat, 2 Aug 2008 23:33:39 +0000 (16:33 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Sat, 2 Aug 2008 23:33:39 +0000 (16:33 -0700)
Cython/Compiler/Nodes.py
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/Parsing.py
Cython/Compiler/Symtab.py
Cython/Compiler/TreeFragment.py

index 1f857e845125cde409ab506cfbefcc3dafd69511..8046b4ff21a24213515b10015922d0aa802960b6 100644 (file)
@@ -624,13 +624,27 @@ class CVarDefNode(StatNode):
     #  declarators   [CDeclaratorNode]
     #  in_pxd        boolean
     #  api           boolean
+    #  need_properties [entry]
 
     child_attrs = ["base_type", "declarators"]
+    need_properties = []
     
     def analyse_declarations(self, env, dest_scope = None):
         if not dest_scope:
             dest_scope = env
+        self.dest_scope = dest_scope
         base_type = self.base_type.analyse(env)
+        
+        if (dest_scope.is_c_class_scope
+                and self.visibility == 'public' 
+                and base_type.is_pyobject 
+                and (base_type.is_builtin_type or base_type.is_extension_type)):
+            need_property = True
+            visibility = 'private'
+        else:
+            need_property = False
+            visibility = self.visibility
+            
         for declarator in self.declarators:
             name_declarator, type = declarator.analyse(base_type, env)
             if not type.is_complete():
@@ -653,8 +667,10 @@ class CVarDefNode(StatNode):
                 if self.in_pxd and self.visibility != 'extern':
                     error(self.pos, 
                         "Only 'extern' C variable declaration allowed in .pxd file")
-                dest_scope.declare_var(name, type, declarator.pos,
-                    cname = cname, visibility = self.visibility, is_cdef = 1)
+                entry = dest_scope.declare_var(name, type, declarator.pos,
+                            cname = cname, visibility = visibility, is_cdef = 1)
+                if need_property:
+                    self.need_properties.append(entry)
     
 
 class CStructOrUnionDefNode(StatNode):
index 1fc7778e88dd7b0433752f411c53c9f405b9d00b..c37d2167380ad13f0d95e79c8ba7dcc04683b654 100644 (file)
@@ -301,6 +301,14 @@ class DecoratorTransform(CythonTransform):
 
 class AnalyseDeclarationsTransform(CythonTransform):
 
+    basic_property = TreeFragment(u"""
+property NAME:
+    def __get__(self):
+        return ATTR
+    def __set__(self, value):
+        ATTR = value
+    """, level='c_class')
+
     def __call__(self, root):
         self.env_stack = [root.scope]
         return super(AnalyseDeclarationsTransform, self).__call__(root)        
@@ -325,7 +333,22 @@ class AnalyseDeclarationsTransform(CythonTransform):
     # on these nodes in a seperate recursive process from the
     # enclosing function or module, so we can simply drop them.
     def visit_CVarDefNode(self, node):
-        return None
+        if node.need_properties:
+            # cdef public attributes may need type testing on 
+            # assignment, so we create a property accesss
+            # mechanism for them. 
+            stats = []
+            for entry in node.need_properties:
+                property = self.basic_property.substitute({
+                    u"ATTR": AttributeNode(pos=entry.pos, obj=NameNode(pos=entry.pos, name="self"), attribute=entry.name),
+                }, pos=entry.pos)
+                property.stats[0].name = entry.name
+                property.analyse_declarations(node.dest_scope)
+                self.visit(property)
+                stats.append(property)
+            return StatListNode(pos=node.pos, stats=stats)
+        else:
+            return None
 
 class AnalyseExpressionsTransform(CythonTransform):
     def visit_ModuleNode(self, node):
index 3ff972d0d74dfd926cf73fc3db63d9d2944836c8..da3c44c0aa1060d9e8298552075a7f78c3caff24 100644 (file)
@@ -2299,6 +2299,15 @@ def p_doc_string(s):
         return result
     else:
         return None
+        
+def p_code(s, level=None):
+    s.add_type_name("object")
+    s.add_type_name("Py_buffer")
+    body = p_statement_list(s, Ctx(level = level), first_statement = 1)
+    if s.sy != 'EOF':
+        s.error("Syntax error in statement [%s,%s]" % (
+            repr(s.sy), repr(s.systring)))
+    return body
 
 def p_module(s, pxd, full_module_name):
     s.add_type_name("object")
index b29789ae9369b82684c440a756245e0461cb7368..fa450d7b5fcfe43e09eb2e1e7c85504e57d210ec 100644 (file)
@@ -1457,7 +1457,9 @@ class CClassScope(ClassScope):
         return entry
     
     def declare_property(self, name, doc, pos):
-        entry = self.declare(name, name, py_object_type, pos)
+        entry = self.lookup_here(name)
+        if entry is None:
+            entry = self.declare(name, name, py_object_type, pos)
         entry.is_property = 1
         entry.doc = doc
         entry.scope = PropertyScope(name, 
index 3e5da2c50f6dbe510abe1e43ef74cd17eb547799..9241983796995c353cb95ff40082095e4d972bad 100644 (file)
@@ -28,7 +28,7 @@ class StringParseContext(Main.Context):
             raise AssertionError("Not yet supporting any cimports/includes from string code snippets")
         return ModuleScope(module_name, parent_module = None, context = self)
         
-def parse_from_strings(name, code, pxds={}):
+def parse_from_strings(name, code, pxds={}, level=None):
     """
     Utility method to parse a (unicode) string of code. This is mostly
     used for internal Cython compiler purposes (creating code snippets
@@ -56,7 +56,10 @@ def parse_from_strings(name, code, pxds={}):
 
     scanner = PyrexScanner(buf, code_source, source_encoding = encoding,
                      scope = scope, context = context)
-    tree = Parsing.p_module(scanner, 0, module_name)
+    if level is None:
+        tree = Parsing.p_module(scanner, 0, module_name)
+    else:
+        tree = Parsing.p_code(scanner, level=level)
     return tree
 
 class TreeCopier(VisitorTransform):
@@ -171,7 +174,7 @@ def strip_common_indent(lines):
     return lines
     
 class TreeFragment(object):
-    def __init__(self, code, name="(tree fragment)", pxds={}, temps=[], pipeline=[]):
+    def __init__(self, code, name="(tree fragment)", pxds={}, temps=[], pipeline=[], level=None):
         if isinstance(code, unicode):
             def fmt(x): return u"\n".join(strip_common_indent(x.split(u"\n"))) 
             
@@ -180,9 +183,9 @@ class TreeFragment(object):
             for key, value in pxds.iteritems():
                 fmt_pxds[key] = fmt(value)
                 
-            t = parse_from_strings(name, fmt_code, fmt_pxds)
-            mod = t
-            t = t.body # Make sure a StatListNode is at the top
+            mod = t = parse_from_strings(name, fmt_code, fmt_pxds, level=level)
+            if level is None:
+                t = t.body # Make sure a StatListNode is at the top
             if not isinstance(t, StatListNode):
                 t = StatListNode(pos=mod.pos, stats=[t])
             for transform in pipeline: