Repository: ipython/ipython

Issue:
%run does not work with import hooks
Hi,

I ran into an issue where the use of `imp` vs `__import__` matters. `imp` is bypassing some things that import statements do, such as calling custom [import hooks](https://docs.python.org/3/reference/import.html#import-hooks). Is there are reason `imp` is being used? If not, can this be updated to use `__import__`?

As a note, this comes up when using `%run -m foo`.

Thanks!

Discussion:
Carreau: Test are due to a regression in mpl, [fixed there](https://github.com/matplotlib/matplotlib/pull/16709)

Patch:
diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py
index 2b26ebe96..fb2df90b5 100644
--- a/IPython/core/magics/execution.py
+++ b/IPython/core/magics/execution.py
@@ -692,6 +692,11 @@ def run(self, parameter_s='', runner=None,
                 warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"')
             error(msg)
             return
+        except TypeError:
+            if fpath in sys.meta_path:
+                filename = ""
+            else:
+                raise
 
         if filename.lower().endswith(('.ipy', '.ipynb')):
             with preserve_keys(self.shell.user_ns, '__file__'):
diff --git a/IPython/utils/module_paths.py b/IPython/utils/module_paths.py
index 0570c322e..32ecab850 100644
--- a/IPython/utils/module_paths.py
+++ b/IPython/utils/module_paths.py
@@ -20,6 +20,7 @@
 # Stdlib imports
 import importlib
 import os
+import sys
 
 # Third-party imports
 
@@ -61,6 +62,8 @@ def find_mod(module_name):
     loader = importlib.util.find_spec(module_name)
     module_path = loader.origin
     if module_path is None:
+        if loader.loader in sys.meta_path:
+            return loader.loader
         return None
     else:
         split_path = module_path.split(".")


Test patch:
diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py
index 5f58b110e..e5ad820c1 100644
--- a/IPython/core/tests/test_magic.py
+++ b/IPython/core/tests/test_magic.py
@@ -1198,3 +1198,49 @@ def test_timeit_arguments():
         # 3.7 optimize no-op statement like above out, and complain there is
         # nothing in the for loop.
         _ip.magic("timeit -n1 -r1 a=('#')")
+
+
+TEST_MODULE = """
+print('Loaded my_tmp')
+if __name__ == "__main__":
+    print('I just ran a script')
+"""
+
+
+def test_run_module_from_import_hook():
+    "Test that a module can be loaded via an import hook"
+    with TemporaryDirectory() as tmpdir:
+        fullpath = os.path.join(tmpdir, 'my_tmp.py')
+        with open(fullpath, 'w') as f:
+            f.write(TEST_MODULE)
+
+        class MyTempImporter(object):
+            def __init__(self):
+                pass
+
+            def find_module(self, fullname, path=None):
+                if 'my_tmp' in fullname:
+                    return self
+                return None
+
+            def load_module(self, name):
+                import imp
+                return imp.load_source('my_tmp', fullpath)
+
+            def get_code(self, fullname):
+                with open(fullpath, 'r') as f:
+                    return compile(f.read(), 'foo', 'exec')
+
+            def is_package(self, __):
+                return False
+
+        sys.meta_path.insert(0, MyTempImporter())
+
+        with capture_output() as captured:
+            _ip.magic("run -m my_tmp")
+            _ip.run_cell("import my_tmp")
+
+        output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
+        nt.assert_equal(output, captured.stdout)
+
+        sys.meta_path.pop(0)