diff --git a/IPython/frontend.py b/IPython/frontend.py index f47b7b7..105721c 100644 --- a/IPython/frontend.py +++ b/IPython/frontend.py @@ -12,11 +12,7 @@ working, though a warning will be printed. # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from __future__ import print_function - -# Stdlib import sys -import types from warnings import warn warn("The top-level `frontend` package has been deprecated. " @@ -27,5 +23,7 @@ from IPython.utils.shimmodule import ShimModule # Unconditionally insert the shim into sys.modules so that further import calls # trigger the custom attribute access above -sys.modules['IPython.frontend.html.notebook'] = ShimModule('notebook', mirror='IPython.html') -sys.modules['IPython.frontend'] = ShimModule('frontend', mirror='IPython') +sys.modules['IPython.frontend.html.notebook'] = ShimModule( + src='IPython.frontend.html.notebook', mirror='IPython.html') +sys.modules['IPython.frontend'] = ShimModule( + src='IPython.frontend', mirror='IPython') diff --git a/IPython/kernel/__init__.py b/IPython/kernel/__init__.py index c1b0e61..3630d80 100644 --- a/IPython/kernel/__init__.py +++ b/IPython/kernel/__init__.py @@ -14,14 +14,18 @@ warn("The `IPython.kernel` package has been deprecated. " from IPython.utils.shimmodule import ShimModule # zmq subdir is gone -sys.modules['IPython.kernel.zmq.session'] = ShimModule('session', mirror='jupyter_client.session') -sys.modules['IPython.kernel.zmq'] = ShimModule('zmq', mirror='ipython_kernel') +sys.modules['IPython.kernel.zmq.session'] = ShimModule( + src='IPython.kernel.zmq.session', mirror='jupyter_client.session') +sys.modules['IPython.kernel.zmq'] = ShimModule( + src='IPython.kernel.zmq', mirror='ipython_kernel') for pkg in ('comm', 'inprocess'): - sys.modules['IPython.kernel.%s' % pkg] = ShimModule(pkg, mirror='ipython_kernel.%s' % pkg) + src = 'IPython.kernel.%s' % pkg + sys.modules[src] = ShimModule(src=src, mirror='ipython_kernel.%s' % pkg) for pkg in ('ioloop', 'blocking'): - sys.modules['IPython.kernel.%s' % pkg] = ShimModule(pkg, mirror='jupyter_client.%s' % pkg) + src = 'IPython.kernel.%s' % pkg + sys.modules[src] = ShimModule(src=src, mirror='jupyter_client.%s' % pkg) # required for `from IPython.kernel import PKG` from ipython_kernel import comm, inprocess diff --git a/IPython/nbconvert.py b/IPython/nbconvert.py index 8af4342..9b47688 100644 --- a/IPython/nbconvert.py +++ b/IPython/nbconvert.py @@ -4,8 +4,6 @@ Shim to maintain backwards compatibility with old IPython.nbconvert imports. # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from __future__ import print_function - import sys from warnings import warn @@ -17,4 +15,5 @@ from IPython.utils.shimmodule import ShimModule # Unconditionally insert the shim into sys.modules so that further import calls # trigger the custom attribute access above -sys.modules['IPython.nbconvert'] = ShimModule('nbconvert', mirror='jupyter_nbconvert') +sys.modules['IPython.nbconvert'] = ShimModule( + src='IPython.nbconvert', mirror='jupyter_nbconvert') diff --git a/IPython/nbformat.py b/IPython/nbformat.py index e5d44e1..5d866ed 100644 --- a/IPython/nbformat.py +++ b/IPython/nbformat.py @@ -4,11 +4,7 @@ Shim to maintain backwards compatibility with old IPython.nbformat imports. # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from __future__ import print_function - -# Stdlib import sys -import types from warnings import warn warn("The `IPython.nbformat` package has been deprecated. " @@ -19,4 +15,5 @@ from IPython.utils.shimmodule import ShimModule # Unconditionally insert the shim into sys.modules so that further import calls # trigger the custom attribute access above -sys.modules['IPython.nbformat'] = ShimModule('nbformat', mirror='jupyter_nbformat') +sys.modules['IPython.nbformat'] = ShimModule( + src='IPython.nbformat', mirror='jupyter_nbformat') diff --git a/IPython/parallel.py b/IPython/parallel.py index 70dca71..a97da91 100644 --- a/IPython/parallel.py +++ b/IPython/parallel.py @@ -4,8 +4,6 @@ Shim to maintain backwards compatibility with old IPython.parallel imports. # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from __future__ import print_function - import sys from warnings import warn @@ -17,4 +15,6 @@ from IPython.utils.shimmodule import ShimModule # Unconditionally insert the shim into sys.modules so that further import calls # trigger the custom attribute access above -sys.modules['IPython.parallel'] = ShimModule('parallel', mirror='ipython_parallel') +sys.modules['IPython.parallel'] = ShimModule( + src='IPython.parallel', mirror='ipython_parallel') + diff --git a/IPython/qt.py b/IPython/qt.py index bccdb4b..9f4a4b5 100644 --- a/IPython/qt.py +++ b/IPython/qt.py @@ -4,11 +4,7 @@ Shim to maintain backwards compatibility with old IPython.qt imports. # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from __future__ import print_function - -# Stdlib import sys -import types from warnings import warn warn("The `IPython.qt` package has been deprecated. " @@ -19,4 +15,4 @@ from IPython.utils.shimmodule import ShimModule # Unconditionally insert the shim into sys.modules so that further import calls # trigger the custom attribute access above -sys.modules['IPython.qt'] = ShimModule('qt', mirror='jupyter_qtconsole') +sys.modules['IPython.qt'] = ShimModule(src='IPython.qt', mirror='jupyter_qtconsole') diff --git a/IPython/terminal/console.py b/IPython/terminal/console.py index 60a217f..b93989c 100644 --- a/IPython/terminal/console.py +++ b/IPython/terminal/console.py @@ -17,4 +17,5 @@ from IPython.utils.shimmodule import ShimModule # Unconditionally insert the shim into sys.modules so that further import calls # trigger the custom attribute access above -sys.modules['IPython.terminal.console'] = ShimModule('console', mirror='jupyter_console') +sys.modules['IPython.terminal.console'] = ShimModule( + src='IPython.terminal.console', mirror='jupyter_console') diff --git a/IPython/utils/shimmodule.py b/IPython/utils/shimmodule.py index 023c8dd..d05879d 100644 --- a/IPython/utils/shimmodule.py +++ b/IPython/utils/shimmodule.py @@ -3,14 +3,67 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +import sys import types +from .importstring import import_item + + +class ShimImporter(object): + """Import hook for a shim. + + This ensures that submodule imports return the real target module, + not a clone that will confuse `is` and `isinstance` checks. + """ + def __init__(self, src, mirror): + self.src = src + self.mirror = mirror + + def _mirror_name(self, fullname): + """get the name of the mirrored module""" + + return self.mirror + fullname[len(self.src):] + + def find_module(self, fullname, path=None): + """Return self if we should be used to import the module.""" + if fullname.startswith(self.src + '.'): + mirror_name = self._mirror_name(fullname) + try: + mod = import_item(mirror_name) + except ImportError: + return + else: + if not isinstance(mod, types.ModuleType): + # not a module + return None + return self + + def load_module(self, fullname): + """Import the mirrored module, and insert it into sys.modules""" + mirror_name = self._mirror_name(fullname) + mod = import_item(mirror_name) + sys.modules[fullname] = mod + return mod + + class ShimModule(types.ModuleType): def __init__(self, *args, **kwargs): self._mirror = kwargs.pop("mirror") + src = kwargs.pop("src", None) + if src: + kwargs['name'] = src.rsplit('.', 1)[-1] super(ShimModule, self).__init__(*args, **kwargs) - + # add import hook for descendent modules + if src: + sys.meta_path.append( + ShimImporter(src=src, mirror=self._mirror) + ) + + @property + def __path__(self): + return [] + @property def __spec__(self): """Don't produce __spec__ until requested""" @@ -19,26 +72,7 @@ class ShimModule(types.ModuleType): def __getattr__(self, key): # Use the equivalent of import_item(name), see below name = "%s.%s" % (self._mirror, key) - - # NOTE: the code below was copied *verbatim* from - # importstring.import_item. For some very strange reason that makes no - # sense to me, if we call it *as a function*, it doesn't work. This - # has something to do with the deep bowels of the import machinery and - # I couldn't find a way to make the code work as a standard function - # call. But at least since it's an unmodified copy of import_item, - # which is used extensively and has a test suite, we can be reasonably - # confident this is OK. If anyone finds how to call the function, all - # the below could be replaced simply with: - # - # from IPython.utils.importstring import import_item - # return import_item('MIRROR.' + key) - - parts = name.rsplit('.', 1) - if len(parts) == 2: - # called with 'foo.bar....' - package, obj = parts - module = __import__(package, fromlist=[obj]) - return getattr(module, obj) - else: - # called with un-dotted string - return __import__(parts[0]) + try: + return import_item(name) + except ImportError: + raise AttributeError(key)