Repository: cython/cython
Pull request: pyximport: Search also pythonpath when searching for .pyx/.py file
~~This PR fixes only searching for .pyx files.~~

Fixes #5615


Problem statement:
[BUG] pyximport is ignoring `sys.path`
## Describe your issue

Consider following file tree:
```cython
########## mydir/mytest.pyx ##########
print('Hello world!')
```

Running command:

```sh
PYTHONPATH=mydir python -c 'import pyximport; pyximport.install(); import mytest'
```
from parent directory of `mydir` will fail with following error:
```
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'mytest'
```

## Expected behaviour

Compilation success since the module is present in `PYTHONPATH`.

## OS
any

## Python version
any

## Cython version
master

## Additional context
This bug is regression. Cython 0.29.X works correctly.

Hints from issue discussion:
A workaround hack is to import from the old version _pyximport2 for all python versions, e.g.

```
import pyximport._pyximport2 as pyximport
pyximport.install()
```

This is a pretty bad bug, however.

Commit message: Code style

Patch to review:
diff --git a/pyximport/pyximport.py b/pyximport/pyximport.py
index 94f7d9dc09c..7b69db6cdc3 100644
--- a/pyximport/pyximport.py
+++ b/pyximport/pyximport.py
@@ -229,7 +229,7 @@ def __init__(self, extension=PYX_EXT, pyxbuild_dir=None, inplace=False, language
 
     def find_spec(self, fullname, path, target=None):
         if not path:
-            path = [os.getcwd()]  # top level import --
+            path = [os.getcwd()] + sys.path  # top level import --
         if "." in fullname:
             *parents, name = fullname.split(".")
         else:
@@ -264,8 +264,11 @@ def __init__(self, extension=PY_EXT, pyxbuild_dir=None, inplace=False, language_
         self.blocked_modules = ['Cython', 'pyxbuild', 'pyximport.pyxbuild',
                                 'distutils', 'cython']
         self.blocked_packages = ['Cython.', 'distutils.']
+        self.found = False
 
     def find_spec(self, fullname, path, target=None):
+        if self.found:
+            return None
         if fullname in sys.modules:
             return None
         if any([fullname.startswith(pkg) for pkg in self.blocked_packages]):
@@ -277,7 +280,7 @@ def find_spec(self, fullname, path, target=None):
         self.blocked_modules.append(fullname)
         name = fullname
         if not path:
-            path = [os.getcwd()]  # top level import --
+            path = [os.getcwd()] + sys.path  # top level import --
         try:
             for entry in path:
                 if os.path.isdir(os.path.join(entry, name)):
@@ -290,6 +293,7 @@ def find_spec(self, fullname, path, target=None):
                 if not os.path.exists(filename):
                     continue
 
+                self.found = True
                 return spec_from_file_location(
                     fullname, filename,
                     loader=PyxImportLoader(filename, self.pyxbuild_dir, self.inplace, self.language_level),
diff --git a/tests/pyximport/pyximport_pyimport_pythonpath.srctree b/tests/pyximport/pyximport_pyimport_pythonpath.srctree
new file mode 100644
index 00000000000..33116e631a4
--- /dev/null
+++ b/tests/pyximport/pyximport_pyimport_pythonpath.srctree
@@ -0,0 +1,26 @@
+PYTHON -c "import pyximport_test; pyximport_test.test()"
+
+########## mydir/mytest.py ##########
+
+import cython
+
+test_string = "MY_PY_TEST"
+compiled = cython.compiled
+
+######## pyximport_test.py ########
+
+import os
+import sys
+import pyximport
+
+sys.path.append('mydir')
+
+pyximport.DEBUG_IMPORT = True
+
+pyximport.install(pyximport=False, pyimport=True,
+                  build_dir=os.path.join(os.path.dirname(__file__), "TEST_TMP"))
+
+def test():
+    import mytest
+    assert mytest.test_string == "MY_PY_TEST"
+    assert mytest.compiled
diff --git a/tests/pyximport/pyximport_pythonpath.srctree b/tests/pyximport/pyximport_pythonpath.srctree
new file mode 100644
index 00000000000..0013c58f0f7
--- /dev/null
+++ b/tests/pyximport/pyximport_pythonpath.srctree
@@ -0,0 +1,21 @@
+PYTHON -c "import pyximport_test; pyximport_test.test()"
+
+########## mydir/mytest.pyx ##########
+
+test_string = "MY_TEST"
+
+######## pyximport_test.py ########
+
+import os
+import sys
+import pyximport
+
+sys.path.append('mydir')
+
+pyximport.DEBUG_IMPORT = True
+
+pyximport.install(build_dir=os.path.join(os.path.dirname(__file__), "TEST_TMP"))
+
+def test():
+    import mytest
+    assert mytest.test_string == "MY_TEST"


Code review comments:
diff_hunk: @@ -0,0 +1,26 @@
+PYTHON -c "import pyximport_test; pyximport_test.test()"
+
+########## mydir/mytest.py ##########
+
+import cython
+
+test_string = "MY_PY_TEST"
+compiled = cython.compiled
+
+######## pyximport_test.py ########
+
+import os
+import sys
+import pyximport
+
+sys.path.append('mydir')
+
+pyximport.DEBUG_IMPORT = True
path: tests/pyximport/pyximport_pyimport_pythonpath.srctree
text: @user1:
Is this needed?

@author:
This allows printing to console on `_debug()` call. It is also in other tests, so I used it here too. I suppose it is for debugging purposes in case of failed test.