Merge branch 'master' into stderr

This commit is contained in:
Anthony Scopatz 2016-11-22 23:33:04 -05:00
commit 5c57154577
10 changed files with 134 additions and 57 deletions

14
news/bashisms.rst Normal file
View file

@ -0,0 +1,14 @@
**Added:**
* New ``bashisms`` xontrib provides additional Bash-like syntax, such as ``!!``.
This xontrib only affects the command line, and not xonsh scripts.
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

13
tests/test_bashisms.py Normal file
View file

@ -0,0 +1,13 @@
"""Tests bashisms xontrib."""
import pytest
@pytest.mark.parametrize('inp, exp', [
('x = 42', 'x = 42'),
('!!', 'ls'),
])
def test_preproc(inp, exp, xonsh_builtins):
"""Test the bash preprocessor."""
from xontrib.bashisms import bash_preproc
xonsh_builtins.__xonsh_history__.inps = ['ls\n']
obs = bash_preproc(inp)
assert exp == obs

View file

@ -146,7 +146,6 @@ def test_test_repo(test_repo):
dotdir = os.path.isdir(os.path.join(test_repo['dir'],
'.{}'.format(test_repo['name'])))
assert dotdir
if test_repo['name'] == 'git':
assert os.path.isfile(os.path.join(test_repo['dir'], 'test-file'))
@ -156,7 +155,8 @@ def test_vc_get_branch(test_repo, xonsh_builtins):
# get corresponding function from vc module
fun = 'get_{}_branch'.format(test_repo['name'])
obs = getattr(vc, fun)()
assert obs == VC_BRANCH[test_repo['name']]
if obs is not None:
assert obs == VC_BRANCH[test_repo['name']]
def test_current_branch_calls_locate_binary_for_empty_cmds_cache(xonsh_builtins):
@ -164,9 +164,7 @@ def test_current_branch_calls_locate_binary_for_empty_cmds_cache(xonsh_builtins)
xonsh_builtins.__xonsh_env__ = DummyEnv(VC_BRANCH_TIMEOUT=1)
cache.is_empty = Mock(return_value=True)
cache.locate_binary = Mock(return_value='')
vc.current_branch()
assert cache.locate_binary.called
@ -177,7 +175,5 @@ def test_current_branch_does_not_call_locate_binary_for_non_empty_cmds_cache(xon
cache.locate_binary = Mock(return_value='')
# make lazy locate return nothing to avoid running vc binaries
cache.lazy_locate_binary = Mock(return_value='')
vc.current_branch()
assert not cache.locate_binary.called

View file

@ -55,8 +55,6 @@ else:
_sys.modules['xonsh.proc'] = __amalgam__
xontribs = __amalgam__
_sys.modules['xonsh.xontribs'] = __amalgam__
base_shell = __amalgam__
_sys.modules['xonsh.base_shell'] = __amalgam__
dirstack = __amalgam__
_sys.modules['xonsh.dirstack'] = __amalgam__
history = __amalgam__
@ -67,8 +65,6 @@ else:
_sys.modules['xonsh.xonfig'] = __amalgam__
environ = __amalgam__
_sys.modules['xonsh.environ'] = __amalgam__
readline_shell = __amalgam__
_sys.modules['xonsh.readline_shell'] = __amalgam__
tracer = __amalgam__
_sys.modules['xonsh.tracer'] = __amalgam__
replay = __amalgam__
@ -83,8 +79,12 @@ else:
_sys.modules['xonsh.imphooks'] = __amalgam__
shell = __amalgam__
_sys.modules['xonsh.shell'] = __amalgam__
base_shell = __amalgam__
_sys.modules['xonsh.base_shell'] = __amalgam__
main = __amalgam__
_sys.modules['xonsh.main'] = __amalgam__
readline_shell = __amalgam__
_sys.modules['xonsh.readline_shell'] = __amalgam__
del __amalgam__
except ImportError:
pass

View file

@ -15,6 +15,7 @@ from xonsh.codecache import (should_use_cache, code_cache_name,
from xonsh.completer import Completer
from xonsh.prompt.base import multiline_prompt, PromptFormatter
from xonsh.events import events
from xonsh.shell import fire_precommand
if ON_WINDOWS:
import ctypes
@ -307,7 +308,6 @@ class BaseShell(object):
src, code = self.push(line)
if code is None:
return
events.on_precommand.fire(src)
env = builtins.__xonsh_env__
hist = builtins.__xonsh_history__ # pylint: disable=no-member
ts1 = None
@ -355,14 +355,12 @@ class BaseShell(object):
info['out'] = last_out
else:
info['out'] = tee_out + '\n' + last_out
events.on_postcommand.fire(
info['inp'],
info['rtn'],
info.get('out', None),
info['ts']
)
)
hist.append(info)
hist.last_cmd_rtn = hist.last_cmd_out = None
@ -380,11 +378,17 @@ class BaseShell(object):
"""Pushes a line onto the buffer and compiles the code in a way that
enables multiline input.
"""
code = None
self.buffer.append(line)
if self.need_more_lines:
return None, code
return None, None
src = ''.join(self.buffer)
src = fire_precommand(src)
return self.compile(src)
def compile(self, src):
"""Compiles source code and returns the (possibly modified) source and
a valid code object.
"""
_cache = should_use_cache(self.execer, 'single')
if _cache:
codefname = code_cache_name(src)
@ -405,7 +409,7 @@ class BaseShell(object):
partial_string_info = check_for_partial_string(src)
in_partial_string = (partial_string_info[0] is not None and
partial_string_info[1] is None)
if ((line == '\n' and not in_partial_string)):
if (src == '\n' or src.endswith('\n\n')) and not in_partial_string:
self.reset_buffer()
print_exception()
return src, None

View file

@ -6,8 +6,10 @@ from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.filters import (Condition, IsMultiline, HasSelection,
EmacsInsertMode, ViInsertMode)
from prompt_toolkit.keys import Keys
from xonsh.aliases import xonsh_exit
from xonsh.tools import ON_WINDOWS, check_for_partial_string
from xonsh.shell import fire_precommand
env = builtins.__xonsh_env__
DEDENT_TOKENS = frozenset(['raise', 'return', 'pass', 'break', 'continue'])
@ -70,6 +72,7 @@ def _is_blank(l):
def can_compile(src):
"""Returns whether the code can be compiled, i.e. it is valid xonsh."""
src = src if src.endswith('\n') else src + '\n'
src = fire_precommand(src, show_diff=False)
src = src.lstrip()
try:
builtins.__xonsh_execer__.compile(src, mode='single', glbs=None,

View file

@ -30,13 +30,11 @@ class PromptToolkitShell(BaseShell):
self.prompter = Prompter()
self.history = PromptToolkitHistory()
self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx)
key_bindings_manager_args = {
'enable_auto_suggest_bindings': True,
'enable_search': True,
'enable_abort_and_exit_bindings': True,
}
'enable_auto_suggest_bindings': True,
'enable_search': True,
'enable_abort_and_exit_bindings': True,
}
self.key_bindings_manager = KeyBindingManager(**key_bindings_manager_args)
load_xonsh_bindings(self.key_bindings_manager)
@ -92,7 +90,7 @@ class PromptToolkitShell(BaseShell):
line = self.prompter.prompt(**prompt_args)
return line
def push(self, line):
def _push(self, line):
"""Pushes a line onto the buffer and compiles the code in a way that
enables multiline input.
"""

View file

@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
"""The xonsh shell"""
import os
import sys
import random
import difflib
import builtins
import warnings
@ -10,12 +12,12 @@ from xonsh.environ import xonshrc_context
from xonsh.execer import Execer
from xonsh.platform import (best_shell_type, has_prompt_toolkit,
ptk_version_is_supported)
from xonsh.tools import XonshError, to_bool_or_int
from xonsh.tools import XonshError, to_bool_or_int, print_exception
from xonsh.events import events
events.doc('on_precommand', """
on_precommand(cmd: str) -> None
on_precommand(cmd: str) -> str
Fires just before a command is executed.
""")
@ -27,6 +29,34 @@ Fires just after a command is executed.
""")
def fire_precommand(src, show_diff=True):
"""Returns the results of firing the precommand handles."""
i = 0
limit = sys.getrecursionlimit()
lst = ''
raw = src
while src != lst:
lst = src
srcs = events.on_precommand.fire(src)
for s in srcs:
if s != lst:
src = s
break
i += 1
if i == limit:
print_exception('Modifcations to source input took more than '
'the recursion limit number of interations to '
'converge.')
if show_diff and builtins.__xonsh_env__.get('XONSH_DEBUG') and src != raw:
sys.stderr.writelines(difflib.unified_diff(
raw.splitlines(keepends=True),
src.splitlines(keepends=True),
fromfile='before precommand event',
tofile='after precommand event',
))
return src
class Shell(object):
"""Main xonsh shell.

View file

@ -1,4 +1,28 @@
{"xontribs": [
{"name": "apt_tabcomplete",
"package": "xonsh-apt-tabcomplete",
"url": "https://github.com/DangerOnTheRanger/xonsh-apt-tabcomplete",
"description": ["Adds tabcomplete functionality to apt-get/apt-cache inside of xonsh."]
},
{"name": "autoxsh",
"package": "xonsh-autoxsh",
"url": "https://github.com/Granitas/xonsh-autoxsh",
"description": ["Adds automatic execution of xonsh script files called",
"``.autoxsh`` when enterting a directory with ``cd`` function"]
},
{"name": "bashisms",
"package": "xonsh",
"url": "http://xon.sh",
"description": [
"Enables additional Bash-like syntax while at the command prompt. For ",
"example, the ``!!`` syntax for running the previous command is now usable.",
"Note that these features are implemented as precommand events and these ",
"additions do not affect the xonsh language when run as script. That said, ",
"you might find them useful if you have strong muscle memory.\n\n",
"**Warning:** This xontrib may modify user command line input to implement ",
"its behavior. To see the modifications as they are applied (in unified diff",
"format), please set ``$XONSH_DEBUG = True``."]
},
{"name": "distributed",
"package": "xonsh",
"url": "http://xon.sh",
@ -16,22 +40,37 @@
" print(res)\n\n",
"This is useful for long running or non-blocking jobs."]
},
{"name": "docker_tabcomplete",
"package": "xonsh-docker-tabcomplete",
"url": "https://github.com/xsteadfastx/xonsh-docker-tabcomplete",
"description": ["Adds tabcomplete functionality to docker inside of xonsh."]
},
{"name": "mpl",
"package": "xonsh",
"url": "http://xon.sh",
"description": ["Matplotlib hooks for xonsh, including the new 'mpl' alias ",
"that displays the current figure on the screen."]
},
{"name": "prompt_ret_code",
"package": "xonsh",
"url": "http://xon.sh",
"description": ["Adds return code info to the prompt"]
},
{"name": "scrapy_tabcomplete",
"package": "xonsh-scrapy-tabcomplete",
"url": "https://github.com/Granitas/xonsh-scrapy-tabcomplete",
"description": ["Adds tabcomplete functionality to scrapy inside of xonsh."]
},
{"name": "vox",
"package": "xonsh",
"url": "http://xon.sh",
"description": ["Python virtual environment manager for xonsh."]
},
{"name": "prompt_ret_code",
"package": "xonsh",
"url": "http://xon.sh",
"description": ["Adds return code info to the prompt"]
},
{"name": "vox_tabcomplete",
"package": "xonsh-vox-tabcomplete",
"url": "https://github.com/Granitas/xonsh-vox-tabcomplete",
"description": ["Adds tabcomplete functionality to vox inside of xonsh."]
},
{"name": "xo",
"package": "exofrills",
"url": "https://github.com/scopatz/xo",
@ -40,32 +79,6 @@
"bit of the startup time when running your favorite, minimal ",
"text editor."]
},
{"name": "apt_tabcomplete",
"package": "xonsh-apt-tabcomplete",
"url": "https://github.com/DangerOnTheRanger/xonsh-apt-tabcomplete",
"description": ["Adds tabcomplete functionality to apt-get/apt-cache inside of xonsh."]
},
{"name": "docker_tabcomplete",
"package": "xonsh-docker-tabcomplete",
"url": "https://github.com/xsteadfastx/xonsh-docker-tabcomplete",
"description": ["Adds tabcomplete functionality to docker inside of xonsh."]
},
{"name": "scrapy_tabcomplete",
"package": "xonsh-scrapy-tabcomplete",
"url": "https://github.com/Granitas/xonsh-scrapy-tabcomplete",
"description": ["Adds tabcomplete functionality to scrapy inside of xonsh."]
},
{"name": "vox_tabcomplete",
"package": "xonsh-vox-tabcomplete",
"url": "https://github.com/Granitas/xonsh-vox-tabcomplete",
"description": ["Adds tabcomplete functionality to vox inside of xonsh."]
},
{"name": "autoxsh",
"package": "xonsh-autoxsh",
"url": "https://github.com/Granitas/xonsh-autoxsh",
"description": ["Adds automatic execution of xonsh script files called",
"`.autoxsh` when enterting a directory with `cd` function"]
},
{"name": "xonda",
"package": "xonda",
"url": "https://github.com/gforsyth/xonda",

6
xontrib/bashisms.py Normal file
View file

@ -0,0 +1,6 @@
"""Bash-like interface extensions for xonsh."""
@events.on_precommand
def bash_preproc(cmd):
return cmd.replace('!!', __xonsh_history__.inps[-1].strip())