Repository: cython/cython
Pull request: Marks view.* extension types as imported
so that they can be inherited from.

Closes https://github.com/cython/cython/issues/3396

Problem statement:
Create subclass to cython array
I wrote a simple class which subclass cython.array in **demo.pyx** file.

```
#cython: language_level = 3
#cython: embedsignature = True

from cython.view cimport array as cyarray

cdef class newarray(cyarray):
    pass
```
The code above compiled with no error. However, I could not import the module with error as below:

```
>>> import demo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "demo.pyx", line 1, in init demo
    #cython: language_level = 3
ModuleNotFoundError: No module named 'View'
```

I searched in **demo.c** file emitted by cython and found that the error occurred in one of the module's initialization function:

```
static int __Pyx_modinit_type_init_code(void)
...
 __pyx_t_1 = PyImport_ImportModule("View.MemoryView"); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 1, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__pyx_array_type = __Pyx_ImportType(__pyx_t_1, "View.MemoryView", "array", sizeof(struct __pyx_array_obj), __Pyx_ImportType_CheckSize_Warn);
```
Cython tried to import "View.MemoryView" using Python's default import routines. Since there was not any View module in search path ==> import fail.

However, I do not understand why Cython tried to import array type from other module. All information needed to create cython.array was already in the .c file. It only need to call PyModule_AddObject to add cython.array type to new module.

What did I do wrong or is that a bug?

I used Python 3.7.4 on MacOs, Cython 0.29.15.

Hints from issue discussion:

Commit message: Marks view.* extension types as imported

so that they can be inherited from.

Closes https://github.com/cython/cython/issues/3396

Patch to review:
diff --git a/Cython/Compiler/CythonScope.py b/Cython/Compiler/CythonScope.py
index 1c25d1a6b4c..2eb6cc3a507 100644
--- a/Cython/Compiler/CythonScope.py
+++ b/Cython/Compiler/CythonScope.py
@@ -127,6 +127,17 @@ def load_cythonscope(self):
                                             self.viewscope, cython_scope=self,
                                             whitelist=MemoryView.view_utility_whitelist)
 
+        # Marks the types as imported so that they can be extended from
+        # without Cython attempting to import cython.view
+        ext_types = [ entry.type
+                         for entry in view_utility_scope.entries.values()
+                         if entry.type.is_extension_type ]
+        for mod in self.context.modules.values():
+            if mod is self or mod.is_builtin_scope:
+                continue
+            mod.types_imported.update(ext_types)
+
+
         # self.entries["array"] = view_utility_scope.entries.pop("array")
 
 
diff --git a/tests/memoryview/cythonarray.pyx b/tests/memoryview/cythonarray.pyx
index 1f87a4e1800..098b18aacca 100644
--- a/tests/memoryview/cythonarray.pyx
+++ b/tests/memoryview/cythonarray.pyx
@@ -209,3 +209,11 @@ def test_cyarray_from_carray():
 
     mslice = a
     print mslice[0, 0], mslice[1, 0], mslice[2, 5]
+
+class InheritFrom(v.array):
+    """
+    Test is just to confirm it works, not to do anything meaningful with it
+    (Be aware that itemsize isn't necessarily right)
+    >>> inst = InheritFrom(shape=(3, 3, 3), itemsize=4, format="i")
+    """
+    pass


Code review comments:
diff_hunk: @@ -127,6 +127,17 @@ def load_cythonscope(self):
                                             self.viewscope, cython_scope=self,
                                             whitelist=MemoryView.view_utility_whitelist)
 
+        # Marks the types as imported so that they can be extended from
+        # without Cython attempting to import cython.view
+        ext_types = [ entry.type
+                         for entry in view_utility_scope.entries.values()
+                         if entry.type.is_extension_type ]
+        for mod in self.context.modules.values():
+            if mod is self or mod.is_builtin_scope:
+                continue
+            mod.types_imported.update(ext_types)
path: Cython/Compiler/CythonScope.py
text: @user1:
Hmm, this doesn't quite feel right at first sight. Why should we add the internal types to the imported types of all user defined modules? Shouldn't that happen only (and automatically) when a user cimports `cython.view`?

@author:
I've updated it with a different approach. I can't really work out where it'd be sensible detect that these types are being used and add them to `types_imported` so I've marked them with a flag so they don't get imported instead..