mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
first light on import hooks
This commit is contained in:
parent
1ff8b24cdf
commit
8fd9e05b9b
4 changed files with 101 additions and 2 deletions
3
tests/sample.xsh
Normal file
3
tests/sample.xsh
Normal file
|
@ -0,0 +1,3 @@
|
|||
# I am a test module.
|
||||
|
||||
x = $(echo "hello mom")
|
14
tests/test_imphooks.py
Normal file
14
tests/test_imphooks.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
"""Testing xonsh import hooks"""
|
||||
from __future__ import unicode_literals, print_function
|
||||
|
||||
import nose
|
||||
from nose.tools import assert_equal
|
||||
|
||||
from xonsh import imphooks
|
||||
|
||||
def test_relative_import():
|
||||
import sample
|
||||
assert_equal('hello mom\n', sample.x)
|
||||
|
||||
if __name__ == '__main__':
|
||||
nose.runmodule()
|
|
@ -17,7 +17,7 @@ class Execer(object):
|
|||
"""Executes xonsh code in a context."""
|
||||
|
||||
def __init__(self, filename='<xonsh-code>', debug_level=0,
|
||||
parser_args=None):
|
||||
parser_args=None, unload=True):
|
||||
"""Parameters
|
||||
----------
|
||||
filename : str, optional
|
||||
|
@ -26,16 +26,20 @@ class Execer(object):
|
|||
Debugging level to use in lexing and parsing.
|
||||
parser_args : dict, optional
|
||||
Arguments to pass down to the parser.
|
||||
unload : bool, optional
|
||||
Whether or not to unload xonsh builtins upon deletion.
|
||||
"""
|
||||
parser_args = parser_args or {}
|
||||
self.parser = Parser(**parser_args)
|
||||
self.filename = filename
|
||||
self.debug_level = debug_level
|
||||
self.unload = unload
|
||||
self.ctxtransformer = ast.CtxAwareTransformer(self.parser)
|
||||
load_builtins(execer=self)
|
||||
|
||||
def __del__(self):
|
||||
unload_builtins()
|
||||
if self.unload:
|
||||
unload_builtins()
|
||||
|
||||
def parse(self, input, ctx, mode='exec'):
|
||||
"""Parses xonsh code in a context-aware fashion. For context-free
|
||||
|
|
78
xonsh/imphooks.py
Normal file
78
xonsh/imphooks.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
"""Import hooks for importing xonsh source files. This module registers
|
||||
the hooks it defines when it is imported.
|
||||
"""
|
||||
from __future__ import print_function, unicode_literals
|
||||
import os
|
||||
import sys
|
||||
import builtins
|
||||
from importlib.machinery import ModuleSpec
|
||||
from importlib.abc import MetaPathFinder, SourceLoader
|
||||
|
||||
from xonsh.tools import string_types
|
||||
from xonsh.execer import Execer
|
||||
|
||||
class XonshImportHook(MetaPathFinder, SourceLoader):
|
||||
"""Implements the import hook for xonsh source files."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(XonshImportHook, self).__init__(*args, **kwargs)
|
||||
self._filenames = {}
|
||||
self._execer = None
|
||||
|
||||
@property
|
||||
def execer(self):
|
||||
if hasattr(builtins, '__xonsh_execer__'):
|
||||
execer = builtins.__xonsh_execer__
|
||||
if self._execer is not None:
|
||||
self._execer = None
|
||||
elif self._execer is None:
|
||||
self._execer = execer = Execer(unload=False)
|
||||
else:
|
||||
execer = self._execer
|
||||
return execer
|
||||
#
|
||||
# MetaPathFinder methods
|
||||
#
|
||||
def find_spec(self, fullname, path, target=None):
|
||||
"""Finds the spec for a xonsh module if it exists."""
|
||||
spec = None
|
||||
path = sys.path if path is None else path
|
||||
name = fullname.rsplit('.', 1)[-1]
|
||||
fname = name + '.xsh'
|
||||
for p in path:
|
||||
if not isinstance(p, string_types):
|
||||
continue
|
||||
if fname not in os.listdir(p):
|
||||
continue
|
||||
spec = ModuleSpec(fullname, self)
|
||||
self._filenames[fullname] = os.path.join(p, fname)
|
||||
break
|
||||
return spec
|
||||
|
||||
#
|
||||
# SourceLoader methods
|
||||
#
|
||||
def get_filename(self, fullname):
|
||||
"""Returns the filename for a module's fullname."""
|
||||
return self._filenames[fullname]
|
||||
|
||||
def get_data(self, path):
|
||||
"""Gets the bytes for a path."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_code(self, fullname):
|
||||
"""Gets the code object for a xonsh file."""
|
||||
filename = self._filenames.get(fullname, None)
|
||||
if filename is None:
|
||||
msg = "xonsh file {0!r} could not be found".format(fullname)
|
||||
raise ImportError(msg)
|
||||
with open(filename, 'r') as f:
|
||||
src = f.read()
|
||||
src = src if src.endswith('\n') else src + '\n'
|
||||
execer = self.execer
|
||||
execer.filename = filename
|
||||
ctx = {} # dummy for modules
|
||||
code = execer.compile(src, glbs=ctx, locs=ctx)
|
||||
return code
|
||||
|
||||
sys.meta_path.append(XonshImportHook())
|
Loading…
Add table
Reference in a new issue