diff --git a/news/imphook.rst b/news/imphook.rst new file mode 100644 index 000000000..c8e5307c5 --- /dev/null +++ b/news/imphook.rst @@ -0,0 +1,21 @@ +**Added:** None + +**Changed:** + +* ``xonsh.imphooks`` does not install the import hooks automatically, you now + need to explicitly call the `install_hook()` method defined in this module. + For example: ``from xonsh.imphooks import install_hook; install_hook()``. The + ``install_hook`` method can safely be called several times. If you need + compatibility with previous versions of Xonsh you can use the following:: + + from xonsh import imphooks + getattr(imphooks, 'install_hook', lambda:None)() + + +**Deprecated:** None + +**Removed:** None + +**Fixed:** None + +**Security:** None diff --git a/tests/test_imphooks.py b/tests/test_imphooks.py index ce7d40748..eb519db7d 100644 --- a/tests/test_imphooks.py +++ b/tests/test_imphooks.py @@ -2,13 +2,13 @@ """Testing xonsh import hooks""" import pytest -from xonsh import imphooks # noqa -from xonsh import built_ins +from xonsh import imphooks from xonsh.environ import Env -from xonsh.execer import Execer from xonsh.built_ins import load_builtins, unload_builtins import builtins +imphooks.install_hook() + @pytest.yield_fixture(autouse=True) def imp_env(xonsh_execer): diff --git a/tests/test_xontribs.py b/tests/test_xontribs.py index b25325fa8..6d20952e6 100644 --- a/tests/test_xontribs.py +++ b/tests/test_xontribs.py @@ -53,4 +53,16 @@ _foobar = 3 """) ctx = xontrib_context('spameggs') - assert ctx == {'spam': 1, '_foobar': 3} \ No newline at end of file + assert ctx == {'spam': 1, '_foobar': 3} + +def test_xshxontrib(tmpmod): + """ + Test that .xsh xontribs are loadable + """ + with tmpmod.mkdir("xontrib").join("script.xsh").open('w') as x: + x.write(""" +hello = 'world' +""") + + ctx = xontrib_context('script') + assert ctx == {'hello': 'world'} diff --git a/xonsh/imphooks.py b/xonsh/imphooks.py index e5f84c216..44a46a73e 100644 --- a/xonsh/imphooks.py +++ b/xonsh/imphooks.py @@ -84,4 +84,14 @@ class XonshImportHook(MetaPathFinder, SourceLoader): return code -sys.meta_path.append(XonshImportHook()) +def install_hook(): + """ + Install Xonsh import hook in `sys.metapath` in order for `.xsh` files to be + importable. + + Can safely be called many times, will be no-op if a xonsh import hook is + already present. + """ + + if XonshImportHook not in {type(hook) for hook in sys.meta_path}: + sys.meta_path.append(XonshImportHook()) diff --git a/xonsh/main.py b/xonsh/main.py index 6b4f80896..18dbfe64d 100644 --- a/xonsh/main.py +++ b/xonsh/main.py @@ -18,6 +18,7 @@ from xonsh.platform import HAS_PYGMENTS, ON_WINDOWS from xonsh.codecache import run_script_with_cache, run_code_with_cache from xonsh.xonfig import xonfig_main from xonsh.lazyimps import pygments, pyghooks +from xonsh.imphooks import install_hook def get_setproctitle(): @@ -188,6 +189,7 @@ def premain(argv=None): args.mode = XonshMode.interactive shell_kwargs['completer'] = True shell_kwargs['login'] = True + install_hook() builtins.__xonsh_shell__ = Shell(**shell_kwargs) env = builtins.__xonsh_env__ env['XONSH_LOGIN'] = shell_kwargs['login']