Merge remote-tracking branch 'upstream/master' into vox-fs

This commit is contained in:
Jamie Bliss 2016-12-15 22:21:33 -05:00
commit d2de666ac7
16 changed files with 178 additions and 51 deletions

View file

@ -43,8 +43,11 @@ install:
pip install -r requirements-tests.txt;
fi
before_script:
- rvm get head || true
script:
- set -e
- if [[ $BUILD_DOCS = true ]]; then
cd docs;
make html;

View file

@ -40,6 +40,7 @@ the xonsh shell
"People xonshtantly mispronounce these things",
"WHAT...is your favorite shell?",
"Conches for the xonsh god!",
"Python-powered, cross-platform, Unix-gazing shell",
"Exploiting the workers and hanging on to outdated imperialist dogma since 2015."
];
document.write(taglines[Math.floor(Math.random() * taglines.length)]);
@ -50,11 +51,11 @@ the xonsh shell
</span>
</p>
Xonsh is a Python-ish, BASHwards-looking shell language and command prompt.
The language is a superset of Python 3.4+ with additional shell primitives
that you are used to from Bash and IPython. It works on all major systems including
Linux, Mac OSX, and Windows. Xonsh is meant for the daily use of experts and novices
alike.
Xonsh is a Python-powered, cross-platform, Unix-gazing shell language and
command prompt. The language is a superset of Python 3.4+ with additional
shell primitives that you are used to from Bash and IPython. It works on
all major systems including Linux, Mac OSX, and Windows. Xonsh is meant
for the daily use of experts and novices alike.
**Try it out!**

View file

@ -11,6 +11,7 @@
* clear.exe
* cls
* cmd
* ex
* fish
* htop
* ksh
@ -21,6 +22,8 @@
* nano
* psql
* ranger
* rview
* rvim
* scp
* sh
* ssh
@ -29,6 +32,7 @@
* tcsh
* top
* vi
* view
* vim
* vimpager
* xo

13
news/stdout_storage.rst Normal file
View file

@ -0,0 +1,13 @@
**Added:** None
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:**
* STDOUT is only stored when ``$XONSH_STORE_STDOUT=True``
**Security:** None

View file

@ -0,0 +1,13 @@
**Added:** None
**Changed:**
* Updated the effectivity of ``$XONSH_DEBUG`` on debug messages.
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

View file

@ -295,7 +295,7 @@ def main():
scripts.append('scripts/xonsh')
skw = dict(
name='xonsh',
description='A general purpose, Python-ish shell',
description='Python-powered, cross-platform, Unix-gazing shell',
long_description=readme,
license='BSD',
version=XONSH_VERSION,

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""Tests the xonsh history."""
"""Tests the json history backend."""
# pylint: disable=protected-access
import io
import os
@ -31,9 +31,19 @@ def test_hist_init(hist):
def test_hist_append(hist, xonsh_builtins):
"""Verify appending to the history works."""
xonsh_builtins.__xonsh_env__['HISTCONTROL'] = set()
hf = hist.append({'joco': 'still alive'})
hf = hist.append({'inp': 'still alive', 'rtn': 0})
assert hf is None
assert 'still alive' == hist.buffer[0]['joco']
assert 'still alive' == hist.buffer[0]['inp']
assert 0 == hist.buffer[0]['rtn']
assert 0 == hist.rtns[-1]
hf = hist.append({'inp': 'dead now', 'rtn': 1})
assert 'dead now' == hist.buffer[1]['inp']
assert 1 == hist.buffer[1]['rtn']
assert 1 == hist.rtns[-1]
hf = hist.append({'inp': 'reborn', 'rtn': 0})
assert 'reborn' == hist.buffer[2]['inp']
assert 0 == hist.buffer[2]['rtn']
assert 0 == hist.rtns[-1]
def test_hist_flush(hist, xonsh_builtins):
@ -41,20 +51,61 @@ def test_hist_flush(hist, xonsh_builtins):
hf = hist.flush()
assert hf is None
xonsh_builtins.__xonsh_env__['HISTCONTROL'] = set()
hist.append({'joco': 'still alive'})
hist.append({'inp': 'still alive?', 'rtn': 0, 'out': 'yes'})
hf = hist.flush()
assert hf is not None
while hf.is_alive():
pass
with LazyJSON(hist.filename) as lj:
obs = lj['cmds'][0]['joco']
assert 'still alive' == obs
assert len(lj['cmds']) == 1
cmd = lj['cmds'][0]
assert cmd['inp'] == 'still alive?'
assert not cmd.get('out', None)
def test_hist_flush_with_store_stdout(hist, xonsh_builtins):
"""Verify explicit flushing of the history works."""
hf = hist.flush()
assert hf is None
xonsh_builtins.__xonsh_env__['HISTCONTROL'] = set()
xonsh_builtins.__xonsh_env__['XONSH_STORE_STDOUT'] = True
hist.append({'inp': 'still alive?', 'rtn': 0, 'out': 'yes'})
hf = hist.flush()
assert hf is not None
while hf.is_alive():
pass
with LazyJSON(hist.filename) as lj:
assert len(lj['cmds']) == 1
assert lj['cmds'][0]['inp'] == 'still alive?'
assert lj['cmds'][0]['out'].strip() == 'yes'
def test_hist_flush_with_hist_control(hist, xonsh_builtins):
"""Verify explicit flushing of the history works."""
hf = hist.flush()
assert hf is None
xonsh_builtins.__xonsh_env__['HISTCONTROL'] = 'ignoredups,ignoreerr'
hist.append({'inp': 'ls foo1', 'rtn': 0})
hist.append({'inp': 'ls foo1', 'rtn': 1})
hist.append({'inp': 'ls foo1', 'rtn': 0})
hist.append({'inp': 'ls foo2', 'rtn': 2})
hist.append({'inp': 'ls foo3', 'rtn': 0})
hf = hist.flush()
assert hf is not None
while hf.is_alive():
pass
assert len(hist.buffer) == 0
with LazyJSON(hist.filename) as lj:
cmds = list(lj['cmds'])
assert len(cmds) == 2
assert [x['inp'] for x in cmds] == ['ls foo1', 'ls foo3']
assert [x['rtn'] for x in cmds] == [0, 0]
def test_cmd_field(hist, xonsh_builtins):
# in-memory
xonsh_builtins.__xonsh_env__['HISTCONTROL'] = set()
hf = hist.append({'rtn': 1})
hf = hist.append({'inp': 'ls foo', 'rtn': 1})
assert hf is None
assert 1 == hist.rtns[0]
assert 1 == hist.rtns[-1]
@ -106,53 +157,71 @@ def test_histcontrol(hist, xonsh_builtins):
# An error, buffer remains empty
hist.append({'inp': 'ls foo', 'rtn': 2})
assert len(hist.buffer) == 0
assert len(hist.buffer) == 1
assert hist.rtns[-1] == 2
assert hist.inps[-1] == 'ls foo'
# Success
hist.append({'inp': 'ls foobazz', 'rtn': 0})
assert len(hist.buffer) == 1
assert len(hist.buffer) == 2
assert 'ls foobazz' == hist.buffer[-1]['inp']
assert 0 == hist.buffer[-1]['rtn']
assert hist.rtns[-1] == 0
assert hist.inps[-1] == 'ls foobazz'
# Error
hist.append({'inp': 'ls foo', 'rtn': 2})
assert len(hist.buffer) == 1
assert 'ls foobazz' == hist.buffer[-1]['inp']
assert 0 == hist.buffer[-1]['rtn']
assert len(hist.buffer) == 3
assert 'ls foo' == hist.buffer[-1]['inp']
assert 2 == hist.buffer[-1]['rtn']
assert hist.rtns[-1] == 2
assert hist.inps[-1] == 'ls foo'
# File now exists, success
hist.append({'inp': 'ls foo', 'rtn': 0})
assert len(hist.buffer) == 2
assert len(hist.buffer) == 4
assert 'ls foo' == hist.buffer[-1]['inp']
assert 0 == hist.buffer[-1]['rtn']
assert hist.rtns[-1] == 0
assert hist.inps[-1] == 'ls foo'
# Success
hist.append({'inp': 'ls', 'rtn': 0})
assert len(hist.buffer) == 3
assert len(hist.buffer) == 5
assert 'ls' == hist.buffer[-1]['inp']
assert 0 == hist.buffer[-1]['rtn']
assert hist.rtns[-1] == 0
assert hist.inps[-1] == 'ls'
# Dup
hist.append({'inp': 'ls', 'rtn': 0})
assert len(hist.buffer) == 3
assert len(hist.buffer) == 6
assert hist.rtns[-1] == 0
assert hist.inps[-1] == 'ls'
# Success
hist.append({'inp': '/bin/ls', 'rtn': 0})
assert len(hist.buffer) == 4
assert len(hist.buffer) == 7
assert '/bin/ls' == hist.buffer[-1]['inp']
assert 0 == hist.buffer[-1]['rtn']
assert hist.rtns[-1] == 0
assert hist.inps[-1] == '/bin/ls'
# Error
hist.append({'inp': 'ls bazz', 'rtn': 1})
assert len(hist.buffer) == 4
assert '/bin/ls' == hist.buffer[-1]['inp']
assert 0 == hist.buffer[-1]['rtn']
assert len(hist.buffer) == 8
assert 'ls bazz' == hist.buffer[-1]['inp']
assert 1 == hist.buffer[-1]['rtn']
assert hist.rtns[-1] == 1
assert hist.inps[-1] == 'ls bazz'
# Error
hist.append({'inp': 'ls bazz', 'rtn': -1})
assert len(hist.buffer) == 4
assert '/bin/ls' == hist.buffer[-1]['inp']
assert 0 == hist.buffer[-1]['rtn']
assert len(hist.buffer) == 9
assert 'ls bazz' == hist.buffer[-1]['inp']
assert -1 == hist.buffer[-1]['rtn']
assert hist.rtns[-1] == -1
assert hist.inps[-1] == 'ls bazz'
@pytest.mark.parametrize('args', [ '-h', '--help', 'show -h', 'show --help'])

View file

@ -256,6 +256,7 @@ def default_threadable_predictors():
'clear': predict_false,
'cls': predict_false,
'cmd': predict_shell,
'ex': predict_false,
'fish': predict_shell,
'htop': predict_help_ver,
'ksh': predict_shell,
@ -267,6 +268,8 @@ def default_threadable_predictors():
'nano': predict_help_ver,
'psql': predict_false,
'ranger': predict_help_ver,
'rview': predict_false,
'rvim': predict_false,
'scp': predict_false,
'sh': predict_shell,
'ssh': predict_false,
@ -275,6 +278,7 @@ def default_threadable_predictors():
'tcsh': predict_shell,
'top': predict_help_ver,
'vi': predict_false,
'view': predict_false,
'vim': predict_false,
'vimpager': predict_help_ver,
'xo': predict_help_ver,

View file

@ -298,7 +298,7 @@ def DEFAULT_VALUES():
'XONSH_COLOR_STYLE': 'default',
'XONSH_CONFIG_DIR': xonsh_config_dir,
'XONSH_DATA_DIR': xonsh_data_dir,
'XONSH_DEBUG': False,
'XONSH_DEBUG': 0,
'XONSH_ENCODING': DEFAULT_ENCODING,
'XONSH_ENCODING_ERRORS': 'surrogateescape',
'XONSH_HISTORY_BACKEND': 'json',
@ -615,10 +615,13 @@ def DEFAULT_DOCS():
'This is the location where xonsh configuration information is stored.',
configurable=False, default="``$XDG_CONFIG_HOME/xonsh``"),
'XONSH_DEBUG': VarDocs(
'Sets the xonsh debugging level. This may be an integer or a boolean, '
'with higher values cooresponding to higher debuging levels and more '
'information presented. Setting this variable prior to stating xonsh '
'will supress amalgamated imports.', configurable=False),
'Sets the xonsh debugging level. This may be an integer or a boolean. '
'Setting this variable prior to stating xonsh to ``1`` or ``True`` '
'will supress amalgamated imports. Setting it to ``2`` will get some '
'basic information like input transformation, command replacement. '
'With ``3`` or a higher number will make more debugging information '
'presented, like PLY parsing messages.',
configurable=False),
'XONSH_DATA_DIR': VarDocs(
'This is the location where xonsh data files are stored, such as '
'history.', default="``$XDG_DATA_HOME/xonsh``"),

View file

@ -54,7 +54,7 @@ class Execer(object):
filename = self.filename
if not transform:
return self.parser.parse(input, filename=filename, mode=mode,
debug_level=(self.debug_level > 1))
debug_level=(self.debug_level > 2))
# Parsing actually happens in a couple of phases. The first is a
# shortcut for a context-free parser. Normally, all subprocess
@ -161,7 +161,7 @@ class Execer(object):
tree = self.parser.parse(input,
filename=filename,
mode=mode,
debug_level=(self.debug_level > 1))
debug_level=(self.debug_level > 2))
parsed = True
except IndentationError as e:
if original_error is None:
@ -221,7 +221,7 @@ class Execer(object):
# anything
raise original_error
else:
if self.debug_level:
if self.debug_level > 1:
msg = ('{0}:{1}:{2}{3} - {4}\n'
'{0}:{1}:{2}{3} + {5}')
mstr = '' if maxcol is None else ':' + str(maxcol)

View file

@ -584,7 +584,7 @@ def load_foreign_aliases(shells=None, config=None, issue_warning=True):
shaliases = {} if shaliases is None else shaliases
for alias in set(shaliases) & set(xonsh_aliases):
del shaliases[alias]
if builtins.__xonsh_env__.get('XONSH_DEBUG'):
if builtins.__xonsh_env__.get('XONSH_DEBUG') > 1:
print('aliases: ignoring alias {!r} of shell {!r} '
'which tries to override xonsh alias.'
''.format(alias, shell['shell']),

View file

@ -176,12 +176,28 @@ class JsonHistoryFlusher(threading.Thread):
def dump(self):
"""Write the cached history to external storage."""
opts = builtins.__xonsh_env__.get('HISTCONTROL')
last_inp = None
cmds = []
for cmd in self.buffer:
if 'ignoredups' in opts and cmd['inp'] == last_inp:
# Skipping dup cmd
continue
if 'ignoreerr' in opts and cmd['rtn'] != 0:
# Skipping failed cmd
continue
cmds.append(cmd)
last_inp = cmd['inp']
with open(self.filename, 'r', newline='\n') as f:
hist = xlj.LazyJSON(f).load()
hist['cmds'].extend(self.buffer)
load_hist_len = len(hist['cmds'])
hist['cmds'].extend(cmds)
if self.at_exit:
hist['ts'][1] = time.time() # apply end time
hist['locked'] = False
if not builtins.__xonsh_env__.get('XONSH_STORE_STDOUT', False):
[cmd.pop('out') for cmd in hist['cmds'][load_hist_len:]
if 'out' in cmd]
with open(self.filename, 'w', newline='\n') as f:
xlj.ljdump(hist, f, sort_keys=True)
@ -314,15 +330,6 @@ class JsonHistory(History):
hf : JsonHistoryFlusher or None
The thread that was spawned to flush history
"""
opts = builtins.__xonsh_env__.get('HISTCONTROL')
if ('ignoredups' in opts and len(self) > 0 and
cmd['inp'] == self.inps[-1]):
# Skipping dup cmd
return None
elif 'ignoreerr' in opts and cmd['rtn'] != 0:
# Skipping failed cmd
return None
self.buffer.append(cmd)
self._len += 1 # must come before flushing
if len(self.buffer) >= self.buffersize:
@ -363,7 +370,7 @@ class JsonHistory(History):
"""
Returns all history as found in XONSH_DATA_DIR.
return format: (cmd, start_time, index)
yeild format: {'inp': cmd, 'rtn': 0, ...}
"""
while self.gc and self.gc.is_alive():
time.sleep(0.011) # gc sleeps for 0.01 secs, sleep a beat longer

View file

@ -9,6 +9,7 @@ from prompt_toolkit.shortcuts import (create_prompt_application,
create_eventloop, create_asyncio_eventloop, create_output)
from xonsh.platform import ptk_version_info
import xonsh.tools as xt
class Prompter(object):
@ -106,9 +107,13 @@ class Prompter(object):
try:
with patch_context:
document = cli.run(reset_current_buffer=False)
if document:
return document.text
except Exception:
xt.print_exception()
# return something to prevent xonsh crash when any
# exceptions raise
return ''
finally:
eventloop.close()

View file

@ -49,7 +49,8 @@ def fire_precommand(src, show_diff=True):
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:
debug_level = builtins.__xonsh_env__.get('XONSH_DEBUG')
if show_diff and debug_level > 1 and src != raw:
sys.stderr.writelines(difflib.unified_diff(
raw.splitlines(keepends=True),
src.splitlines(keepends=True),

View file

@ -21,7 +21,7 @@
"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``."]
"format), please set ``$XONSH_DEBUG`` to ``2`` or higher."]
},
{"name": "distributed",
"package": "xonsh",

View file

@ -3,4 +3,8 @@
@events.on_precommand
def bash_preproc(cmd):
if not __xonsh_history__.inps:
if cmd.strip() == '!!':
return ''
return cmd
return cmd.replace('!!', __xonsh_history__.inps[-1].strip())