diff --git a/Cython/Compiler/CypclassWrapper.py b/Cython/Compiler/CypclassWrapper.py
new file mode 100644
index 0000000000000000000000000000000000000000..8eff7d384ccf8c2d10bca8198ae9ba857e6000e4
--- /dev/null
+++ b/Cython/Compiler/CypclassWrapper.py
@@ -0,0 +1,40 @@
+#
+#   Code generation for wrapping cypclass as a Python Extension Type
+#
+#   Will be generated:
+#       - a PyTypeObject definition for each user defined cypclass
+#       - Python wrappers for cypclass methods
+#       - Python getters/setters for cypclass attributes
+#       - Specific 'tp slots' for handling cycplass objects from Python:
+#           . tp_new
+#           . tp_init
+#           . tp_dealloc
+#           ...
+#
+#   Functions defined here will be called from ModuleNode.py
+#
+#   Reasons for using a separate file:
+#       - avoid cluttering ModuleNode.py
+#       - regroup common logic
+#       - decouple the code generation process from that of 'cdef class'
+#
+#   Code generation for cypclass will be similar to code generation for 'cdef class' in ModuleNode.py,
+#   but differences are significant enough that it is better to introduce some redundancy than try to
+#   handle both 'cdef class' and 'cypclass' in ModuleNode.py.
+#
+
+
+def generate_cypclass_typeobj_declarations(env, code, definition):
+    """
+        Generate declarations of global pointers to the PyTypeObject for each cypclass
+    """
+
+    for entry in env.cypclass_entries:
+        if definition or entry.defined_in_pxd:
+            code.putln("static PyTypeObject *%s = 0;" % (
+                entry.type.typeptr_cname))
+        cyp_scope = entry.type.scope
+        if cyp_scope:
+            # generate declarations for nested cycplasses
+            generate_cypclass_typeobj_declarations(cyp_scope, code, definition)
+
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index c3fcc8b6d1d9e2966e8911de3c6714112629c68d..2244fc4e4fca843f170b51028150f8e01d989af9 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -20,6 +20,7 @@ from .PyrexTypes import CPtrType
 from . import Future
 from . import Annotate
 from . import Code
+from . import CypclassWrapper
 from . import Naming
 from . import Nodes
 from . import Options
@@ -680,7 +681,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
             modulecode.putln("")
             modulecode.putln("/* Module declarations from %s */" % module.qualified_name.as_c_string_literal())
             self.generate_c_class_declarations(module, modulecode, defined_here, globalstate)
-            self.generate_cypclass_typeobj_declarations(module, modulecode, defined_here)
+            CypclassWrapper.generate_cypclass_typeobj_declarations(module, modulecode, defined_here)
             self.generate_cvariable_declarations(module, modulecode, defined_here)
             self.generate_cfunction_declarations(module, modulecode, defined_here)
 
@@ -1912,16 +1913,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                         "Py_VISIT(traverse_module_state->%s);" % (
                         entry.type.typeobj_cname))
         code.putln("#endif")
-    
-    def generate_cypclass_typeobj_declarations(self, env, code, definition):
-        for entry in env.cypclass_entries:
-            if definition or entry.defined_in_pxd:
-                code.putln("static PyTypeObject *%s = 0;" % (
-                    entry.type.typeptr_cname))
-            cyp_scope = entry.type.scope
-            if cyp_scope:
-                # generate declarations for nested cycplasses
-                self.generate_cypclass_typeobj_declarations(cyp_scope, code, definition)
 
     def generate_cvariable_declarations(self, env, code, definition):
         if env.is_cython_builtin: