Forward declaration of extension class in another module
authorGregory Ewing <greg.ewing@canterbury.ac.nz>
Wed, 14 May 2008 12:34:23 +0000 (00:34 +1200)
committerGregory Ewing <greg.ewing@canterbury.ac.nz>
Wed, 14 May 2008 12:34:23 +0000 (00:34 +1200)
+
+    - There is now a way of forward-declaring an extension type into
+      another module. This allows two .pxd files to define extension types
+      that refer to each other without running into circular import problems.
+      For example:
+
+        cimport blarg
+        cdef class blarg.Blarg # Forward declaration
+
+        cdef class Foo:
+          cdef blarg.Blarg blg

Cython/Compiler/Nodes.py
Cython/Compiler/Symtab.py

index d668b38e692379c0f767f33c485d65dc0cf85783..a843f090aa88daaff0921e277231b5ff29a5434d 100644 (file)
@@ -571,16 +571,7 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
             else:
                 type = py_object_type
         else:
-            scope = env
-            for name in self.module_path:
-                entry = scope.find(name, self.pos)
-                if entry and entry.as_module:
-                    scope = entry.as_module
-                else:
-                    if entry:
-                        error(self.pos, "'%s' is not a cimported module" % name)
-                    scope = None
-                    break
+            scope = env.find_imported_module(self.module_path, self.pos)
             if scope:
                 if scope.is_c_class_scope:
                     scope = scope.global_scope()
@@ -1230,6 +1221,8 @@ class DefNode(FuncDefNode):
         self.num_required_kw_args = rk
         self.num_required_args = r
     
+    entry = None
+    
     def analyse_declarations(self, env):
         for arg in self.args:
             base_type = arg.base_type.analyse(env)
@@ -2010,7 +2003,14 @@ class CClassDefNode(StatNode, BlockNode):
                     else:
                         self.base_type = base_class_entry.type
         has_body = self.body is not None
-        self.entry = env.declare_c_class(
+        if self.module_name:
+            module_path = self.module_name.split(".")
+            home_scope = env.find_imported_module(module_path, self.pos)
+            if not home_scope:
+                return
+        else:
+            home_scope = env
+        self.entry = home_scope.declare_c_class(
             name = self.class_name, 
             pos = self.pos,
             defining = has_body and self.in_pxd,
@@ -2022,6 +2022,8 @@ class CClassDefNode(StatNode, BlockNode):
             visibility = self.visibility,
             typedef_flag = self.typedef_flag,
             api = self.api)
+        if home_scope is not env and self.visibility == 'extern':
+            env.add_imported_entry(self.class_name, self.entry, pos)
         scope = self.entry.type.scope
 
         if self.doc and Options.docstrings:
index 485215a7ab751de576dd59fb98ec6c41f5e4ce23..8127e534817c37250bbaba757f0ba9283b327815 100644 (file)
@@ -420,6 +420,21 @@ class Scope:
         else:
             error(pos, "'%s' is not declared" % name)
     
+    def find_imported_module(self, path, pos):
+        # Look up qualified name, must be a module, report error if not found.
+        # Path is a list of names.
+        scope = self
+        for name in path:
+            entry = scope.find(name, pos)
+            if not entry:
+                return None
+            if entry.as_module:
+                scope = entry.as_module
+            else:
+                error(pos, "'%s' is not a cimported module" % scope.qualified_name)
+                return None
+        return scope
+        
     def lookup(self, name):
         # Look up name in this scope or an enclosing one.
         # Return None if not found.