This commit is contained in:
Bob Hyman 2016-08-04 20:53:21 -04:00
commit 0d71b85744
28 changed files with 422 additions and 344 deletions

View file

@ -1,21 +0,0 @@
package: xonsh
platform:
- linux-64
engine:
- python=3.5
- python=3.4
script:
- conda build recipe
after_success:
- conda convert -p all -f /opt/miniconda/conda-bld/linux-64/xonsh-*.tar.bz2 -o /opt/miniconda/conda-bld
after_failure:
- echo "Build failed!"
build_targets:
- /opt/miniconda/conda-bld/*/*.tar.bz2

View file

@ -8,8 +8,8 @@ xonsh
.. image:: https://travis-ci.org/xonsh/xonsh.svg?branch=master
:target: https://travis-ci.org/xonsh/xonsh
.. image:: https://ci.appveyor.com/api/projects/status/ufqtigii8ma3rctt/branch/master?svg=true
:target: https://ci.appveyor.com/project/rbrewer123/xonsh-unq93
.. image:: https://ci.appveyor.com/api/projects/status/github/xonsh/xonsh?svg=true
:target: https://ci.appveyor.com/project/xonsh/xonsh
.. image:: https://landscape.io/github/xonsh/xonsh/master/landscape.svg?style=flat
:target: https://landscape.io/github/xonsh/xonsh/master

View file

@ -1 +0,0 @@
.binstar.yml

View file

@ -14,8 +14,6 @@ You can install xonsh using ``conda``, ``pip``, or from source.
$ conda config --add channels conda-forge
$ conda install xonsh
.. note:: For the bleeding edge development version use ``conda install -c xonsh/channel/dev xonsh``
**pip:**

View file

@ -20,8 +20,6 @@ You can install xonsh using homebrew, conda, pip, or from source.
$ conda config --add channels conda-forge
$ conda install xonsh
.. note:: For the bleeding edge development version use ``conda install -c xonsh/channel/dev xonsh``
**pip:**

View file

@ -69,8 +69,8 @@ default, they cannot be unloaded (easily).
.. note::
When a xontrib is loaded from a config file or via the xontrib command,
its public variables are placed in the current execution context, just
like variables set in run control files.
its public variables are placed in the current execution context unless
``__all__`` is defined, just like in regular Python modules.
Loading xontribs in the config file is as simple as adding a list of string
xontrib names to the top-level ``"xontribs"`` key. For example, the following

View file

@ -23,8 +23,6 @@ Install xonsh with the following command:
> conda config --add channels conda-forge
> conda install xonsh
.. note:: For the bleeding edge development version use ``conda install -c xonsh/channel/dev xonsh``
This will install xonsh and all the recommended dependencies. Next, run xonsh:
.. code-block:: bat

13
news/fix-xonsh-python.rst Normal file
View file

@ -0,0 +1,13 @@
**Added:** None
**Changed:**
* xon.sh uses the interpreter used to install instead of the default python3.
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

21
news/history-refactor.rst Normal file
View file

@ -0,0 +1,21 @@
**Added:**
* ``_hist_get`` that uses generators to filter and fetch
the history commands of each session.
* ``-n`` option to the show subcommand to choose
to numerate the commands.
**Changed:**
* ``_hist_show`` now uses ``_hist_get`` to print out the commands.
**Deprecated:** None
**Removed:** None
**Fixed:**
* ``_zsh_hist_parser`` not parsing history files without timestamps.
**Security:** None

View file

@ -0,0 +1,17 @@
**Added:** None
**Changed:** None
**Deprecated:** None
**Removed:**
* Anaconda Build is shutting down so we can no longer build conda development packages.
All references to these packages are removed from the documentation.
* Removed conda build recipe since the it is no longer used for Anaconda Build.
The recipe used to build xonsh on conda-forge can be found here:
https://github.com/conda-forge/xonsh-feedstock/blob/master/recipe/meta.yaml
**Fixed:** None
**Security:** None

14
news/srcenc.rst Normal file
View file

@ -0,0 +1,14 @@
**Added:** None
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:**
* Fixed error with not sourcing files with ``$XONSH_ENCODING`` and
``$XONSH_ENCODING_ERRORS``.
**Security:** None

13
news/xontrib-all.rst Normal file
View file

@ -0,0 +1,13 @@
**Added:** None
**Changed:**
* Xontribs may now define ``__all__`` as a module top-level to limit what gets exported to the shell context
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

14
news/xontribs.rst Normal file
View file

@ -0,0 +1,14 @@
**Added:**
* Added xontribs:
* `z (Tracks your most used directories, based on 'frecency'.) <https://github.com/astronouth7303/xontrib-z>`_
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

View file

@ -1,37 +0,0 @@
package:
name: xonsh
version: {{ GIT_DESCRIBE_TAG + '.dev' + GIT_DESCRIBE_NUMBER }}
source:
git_url: ../
build:
script: python setup.py install --single-version-externally-managed --record=record.txt
number: 0
string: {{'py' + CONDA_PY + '_' + GIT_DESCRIBE_HASH }}
skip: True # [py2k]
entry_points:
- xonsh = xonsh.main:main
requirements:
build:
- python
- ply
- setuptools
- jupyter
run:
- python
- ply
- prompt_toolkit
- setproctitle
- pygments
app:
entry: xonsh
icon: ../docs/_static/ascii_conch_part_color.png
about:
home: http://xon.sh/
license: BSD
summary: xonsh is a Python-ish, BASHwards-facing shell.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

View file

@ -1,11 +0,0 @@
{
"menu_name": "Anaconda${PY_VER} ${PLATFORM}",
"menu_items":
[
{
"name": "xonsh",
"pyscript": "${PYTHON_SCRIPTS}/xonsh-script.py",
"icon": "${MENU_DIR}/xonsh.ico"
}
]
}

View file

@ -181,7 +181,7 @@ class xinstall(install):
import traceback
traceback.print_exc()
print('Installing Jupyter hook failed.')
install.run(self)
super().run()
if dirty:
restore_version()
@ -193,7 +193,7 @@ class xsdist(sdist):
build_tables()
amalgamate_source()
dirty = dirty_version()
sdist.make_release_tree(self, basedir, files)
super().make_release_tree(basedir, files)
if dirty:
restore_version()
@ -213,11 +213,31 @@ class install_scripts_quoted_shebang(install_scripts):
contents = contents.replace(shebang, quoted_shebang)
super().write_script(script_name, contents, mode, *ignored)
class install_scripts_rewrite(install_scripts):
"""Change default python3 to the concrete python binary used to install/develop inside xon.sh script"""
def run(self):
super().run()
if not self.dry_run:
for file in self.get_outputs():
if file.endswith('xon.sh'):
# this is the value distutils use on its shebang translation
bs_cmd = self.get_finalized_command('build_scripts')
exec_param = getattr(bs_cmd, 'executable', None)
with open(file, 'r') as f:
content = f.read()
processed = content.replace(' python3 ', ' "{}" '.format(exec_param))
with open(file, 'w') as f:
f.write(processed)
# The custom install needs to be used on Windows machines
if os.name == 'nt':
cmdclass = {'install': xinstall, 'sdist': xsdist, 'install_scripts': install_scripts_quoted_shebang}
else:
cmdclass = {'install': xinstall, 'sdist': xsdist}
cmdclass = {'install': xinstall, 'sdist': xsdist, 'install_scripts': install_scripts_rewrite}
if HAVE_SETUPTOOLS:
@ -231,6 +251,12 @@ if HAVE_SETUPTOOLS:
if dirty:
restore_version()
def install_script(self, dist, script_name, script_text, dev_path=None):
if script_name == 'xon.sh':
# change default python3 to the concrete python binary used to install/develop inside xon.sh script
script_text = script_text.replace(' python3 ', ' "{}" '.format(sys.executable))
super().install_script(dist, script_name, script_text, dev_path)
def main():
"""The main entry point."""

View file

@ -1,10 +1,6 @@
# -*- coding: utf-8 -*-
"""Tests the xonsh history."""
# pylint: disable=protected-access
# TODO: Remove the following pylint directive when it correctly handles calls
# to nose assert_xxx functions.
# pylint: disable=no-value-for-parameter
from __future__ import unicode_literals, print_function
import io
import os
import sys
@ -71,63 +67,34 @@ def test_cmd_field(hist, xonsh_builtins):
assert 1 == hist.rtns[-1]
assert None == hist.outs[-1]
def run_show_cmd(hist_args, commands, base_idx=0, step=1):
"""Run and evaluate the output of the given show command."""
stdout = sys.stdout
stdout.seek(0, io.SEEK_SET)
stdout.truncate()
history.history_main(hist_args)
stdout.seek(0, io.SEEK_SET)
hist_lines = stdout.readlines()
assert len(commands) == len(hist_lines)
for idx, (cmd, actual) in enumerate(zip(commands, hist_lines)):
expected = ' {:d}: {:s}\n'.format(base_idx + idx * step, cmd)
assert expected == actual
def test_show_cmd(hist, xonsh_builtins):
cmds = ['ls', 'cat hello kitty', 'abc', 'def', 'touch me', 'grep from me']
@pytest.mark.parametrize('inp, commands, offset', [
('', cmds, (0, 1)),
('-r', list(reversed(cmds)), (len(cmds)- 1, -1)),
('0', cmds[0:1], (0, 1)),
('1', cmds[1:2], (1, 1)),
('-2', cmds[-2:-1], (len(cmds) -2 , 1)),
('1:3', cmds[1:3], (1, 1)),
('1::2', cmds[1::2], (1, 2)),
('-4:-2', cmds[-4:-2], (len(cmds) - 4, 1))
])
def test_show_cmd_numerate(inp, commands, offset, hist, xonsh_builtins, capsys):
"""Verify that CLI history commands work."""
cmds = ['ls', 'cat hello kitty', 'abc', 'def', 'touch me', 'grep from me']
sys.stdout = io.StringIO()
base_idx, step = offset
xonsh_builtins.__xonsh_history__ = hist
xonsh_builtins.__xonsh_env__['HISTCONTROL'] = set()
for ts,cmd in enumerate(cmds): # populate the shell history
hist.append({'inp': cmd, 'rtn': 0, 'ts':(ts+1, ts+1.5)})
# Verify an implicit "show" emits show history
run_show_cmd([], cmds)
exp = ('{}: {}'.format(base_idx + idx * step, cmd)
for idx, cmd in enumerate(list(commands)))
exp = '\n'.join(exp)
# Verify an explicit "show" with no qualifiers emits
# show history.
run_show_cmd(['show'], cmds)
# Verify an explicit "show" with a reversed qualifier
# emits show history in reverse order.
run_show_cmd(['show', '-r'], list(reversed(cmds)),
len(cmds) - 1, -1)
# Verify that showing a specific history entry relative to
# the start of the history works.
run_show_cmd(['show', '0'], [cmds[0]], 0)
run_show_cmd(['show', '1'], [cmds[1]], 1)
# Verify that showing a specific history entry relative to
# the end of the history works.
run_show_cmd(['show', '-2'], [cmds[-2]],
len(cmds) - 2)
# Verify that showing a history range relative to the start of the
# history works.
run_show_cmd(['show', '0:2'], cmds[0:2], 0)
run_show_cmd(['show', '1::2'], cmds[1::2], 1, 2)
# Verify that showing a history range relative to the end of the
# history works.
run_show_cmd(['show', '-2:'],
cmds[-2:], len(cmds) - 2)
run_show_cmd(['show', '-4:-2'],
cmds[-4:-2], len(cmds) - 4)
sys.stdout = sys.__stdout__
history.history_main(['show', '-n'] + shlex.split(inp))
out, err = capsys.readouterr()
assert out.rstrip() == exp
def test_histcontrol(hist, xonsh_builtins):
@ -195,16 +162,22 @@ def test_parse_args_help(args, capsys):
@pytest.mark.parametrize('args, exp', [
('', ('show', 'session', [])),
('show', ('show', 'session', [])),
('show session', ('show', 'session', [])),
('show session 15', ('show', 'session', ['15'])),
('show bash 3:5 15:66', ('show', 'bash', ['3:5', '15:66'])),
('show zsh 3 5:6 16 9:3', ('show', 'zsh', ['3', '5:6', '16', '9:3'])),
('', ('show', 'session', [], False, False)),
('1:5', ('show', 'session', ['1:5'], False, False)),
('show', ('show', 'session', [], False, False)),
('show 15', ('show', 'session', ['15'], False, False)),
('show bash 3:5 15:66', ('show', 'bash', ['3:5', '15:66'], False, False)),
('show -r', ('show', 'session', [], False, True)),
('show -rn bash', ('show', 'bash', [], True, True)),
('show -n -r -30:20', ('show', 'session', ['-30:20'], True, True)),
('show -n zsh 1:2:3', ('show', 'zsh', ['1:2:3'], True, False))
])
def test_parser_show(args, exp):
args = _hist_parse_args(shlex.split(args))
action, session, slices = exp
assert args.action == action
assert args.session == session
assert args.slices == slices
# use dict instead of argparse.Namespace for pretty pytest diff
exp_ns = {'action': exp[0],
'session': exp[1],
'slices': exp[2],
'numerate': exp[3],
'reverse': exp[4]}
ns = _hist_parse_args(shlex.split(args))
assert ns.__dict__ == exp_ns

View file

@ -815,6 +815,7 @@ def test_bool_or_int_to_str(inp, exp):
@pytest.mark.parametrize('inp, exp', [
(42, slice(42, 43)),
(None, slice(None, None, None)),
(slice(1,2), slice(1,2)),
('42', slice(42, 43)),
('-42', slice(-42, -41)),
('1:2:3', slice(1, 2, 3)),
@ -823,25 +824,28 @@ def test_bool_or_int_to_str(inp, exp):
('1:', slice(1, None, None)),
('[1:2:3]', slice(1, 2, 3)),
('(1:2:3)', slice(1, 2, 3)),
((4, 8, 10), slice(4, 8, 10)),
([10,20], slice(10,20))
])
def test_ensure_slice(inp, exp):
obs = ensure_slice(inp)
assert exp == obs
@pytest.mark.parametrize('inp, error', [
('42.3', ValueError),
('3:asd5:1', ValueError),
('test' , ValueError),
('6.53:100:5', ValueError),
('4:-', ValueError),
('2:15-:3', ValueError),
('50:-:666', ValueError),
(object(), TypeError),
([], TypeError)
@pytest.mark.parametrize('inp', [
'42.3',
'3:asd5:1',
'test' ,
'6.53:100:5',
'4:-',
'2:15-:3',
'50:-:666',
object(),
[1,5,3,4],
('foo')
])
def test_ensure_slice_invalid(inp, error):
with pytest.raises(error):
def test_ensure_slice_invalid(inp):
with pytest.raises(ValueError):
obs = ensure_slice(inp)

View file

@ -43,3 +43,25 @@ def test_activate(xonsh_builtins, tmpdir):
assert xonsh_builtins.__xonsh_env__['VIRTUAL_ENV'] == vox['spam'].env
vox.deactivate()
assert 'VIRTUAL_ENV' not in xonsh_builtins.__xonsh_env__
@skip_if_on_conda
def test_path(xonsh_builtins, tmpdir):
"""
Test to make sure Vox properly activates and deactivates by examining $PATH
"""
xonsh_builtins.__xonsh_env__['VIRTUALENV_HOME'] = str(tmpdir)
# I consider the case that the user doesn't have a PATH set to be unreasonable
xonsh_builtins.__xonsh_env__.setdefault('PATH', [])
oldpath = list(xonsh_builtins.__xonsh_env__['PATH'])
vox = Vox()
vox.create('eggs')
vox.activate('eggs')
assert oldpath != xonsh_builtins.__xonsh_env__['PATH']
vox.deactivate()
assert oldpath == xonsh_builtins.__xonsh_env__['PATH']

View file

@ -1,6 +1,56 @@
"""xontrib tests, such as they are"""
from xonsh.xontribs import xontrib_metadata
import sys
import pytest
from xonsh.xontribs import xontrib_metadata, xontrib_context
def test_load_xontrib_metadata():
# Simply tests that the xontribs JSON files isn't malformed.
xontrib_metadata()
xontrib_metadata()
@pytest.yield_fixture
def tmpmod(tmpdir):
"""
Same as tmpdir but also adds/removes it to the front of sys.path.
Also cleans out any modules loaded as part of the test.
"""
sys.path.insert(0, str(tmpdir))
loadedmods = set(sys.modules.keys())
try:
yield tmpdir
finally:
del sys.path[0]
newmods = set(sys.modules.keys()) - loadedmods
for m in newmods:
del sys.modules[m]
def test_noall(tmpmod):
"""
Tests what get's exported from a module without __all__
"""
with tmpmod.mkdir("xontrib").join("spameggs.py").open('w') as x:
x.write("""
spam = 1
eggs = 2
_foobar = 3
""")
ctx = xontrib_context('spameggs')
assert ctx == {'spam': 1, 'eggs': 2}
def test_withall(tmpmod):
"""
Tests what get's exported from a module with __all__
"""
with tmpmod.mkdir("xontrib").join("spameggs.py").open('w') as x:
x.write("""
__all__ = 'spam', '_foobar'
spam = 1
eggs = 2
_foobar = 3
""")
ctx = xontrib_context('spameggs')
assert ctx == {'spam': 1, '_foobar': 3}

View file

@ -250,11 +250,15 @@ def source_foreign(args, stdin=None):
def source_alias(args, stdin=None):
"""Executes the contents of the provided files in the current context.
If sourced file isn't found in cwd, search for file along $PATH to source
instead"""
instead.
"""
env = builtins.__xonsh_env__
encoding = env.get('XONSH_ENCODING')
errors = env.get('XONSH_ENCODING_ERRORS')
for fname in args:
if not os.path.isfile(fname):
fname = locate_binary(fname)
with open(fname, 'r') as fp:
with open(fname, 'r', encoding=encoding, errors=errors) as fp:
builtins.execx(fp.read(), 'exec', builtins.__xonsh_ctx__)

View file

@ -2,15 +2,15 @@
"""Implements the xonsh history object."""
import os
import sys
import uuid
import time
import json
import glob
import json
import time
import uuid
import argparse
import operator
import datetime
import builtins
import datetime
import functools
import itertools
import threading
import collections
import collections.abc as abc
@ -236,36 +236,26 @@ class CommandField(abc.Sequence):
return self is self.hist._queue[0]
def _find_histfile_var(file_list=None, default=None):
if file_list is None:
return None
hist_file = None
found_hist = False
def _find_histfile_var(file_list, default=None):
"""Return the path of the history file
from the value of the envvar HISTFILE.
"""
for f in file_list:
f = expanduser_abs_path(f)
if not os.path.isfile(f):
continue
with open(f, 'r') as rc_file:
for line in rc_file:
if "HISTFILE=" in line:
evar = line.split(' ', 1)[-1]
hist_file = evar.split('=', 1)[-1]
for char in ['"', "'", '\n']:
hist_file = hist_file.replace(char, '')
if line.startswith('HISTFILE='):
hist_file = line.split('=', 1)[1].strip('\'"\n')
hist_file = expanduser_abs_path(hist_file)
if os.path.isfile(hist_file):
found_hist = True
break
if found_hist:
break
if hist_file is None:
default = expanduser_abs_path(default)
if os.path.isfile(default):
hist_file = default
return hist_file
return hist_file
else:
if default:
default = expanduser_abs_path(default)
if os.path.isfile(default):
return default
def _all_xonsh_parser(**kwargs):
@ -279,19 +269,17 @@ def _all_xonsh_parser(**kwargs):
files = [os.path.join(data_dir, f) for f in os.listdir(data_dir)
if f.startswith('xonsh-') and f.endswith('.json')]
file_hist = []
ind = 0
for f in files:
try:
json_file = LazyJSON(f, reopen=False)
file_hist.append(json_file.load()['cmds'])
except ValueError:
# Invalid json file
pass
commands = [(c['inp'][:-1] if c['inp'].endswith('\n') else c['inp'],
c['ts'][0])
for commands in file_hist for c in commands if c]
commands.sort(key=operator.itemgetter(1))
return [(c, t, ind) for ind, (c, t) in enumerate(commands)]
commands = json_file.load()['cmds']
for c in commands:
yield (c['inp'].rstrip(), c['ts'][0], ind)
ind += 1
def _curr_session_parser(hist=None, **kwargs):
@ -301,28 +289,22 @@ def _curr_session_parser(hist=None, **kwargs):
"""
if hist is None:
hist = builtins.__xonsh_history__
if not hist:
return None
start_times = [start for start, end in hist.tss]
names = [name[:-1] if name.endswith('\n') else name
for name in hist.inps]
commands = enumerate(zip(names, start_times))
return [(c, t, ind) for ind, (c, t) in commands]
start_times = (start for start, end in hist.tss)
names = (name.rstrip() for name in hist.inps)
for ind, (c, t) in enumerate(zip(names, start_times)):
yield (c, t, ind)
def _zsh_hist_parser(location=None, **kwargs):
default_location = os.path.join('~', '.zsh_history')
location_list = [os.path.join('~', '.zshrc'),
os.path.join('~', '.zprofile')]
"""Yield commands from zsh history file"""
if location is None:
location = _find_histfile_var(location_list, default_location)
z_hist_formatted = []
if location and os.path.isfile(location):
with open(location, 'r', errors='backslashreplace') as z_file:
z_txt = z_file.read()
z_hist = z_txt.splitlines()
if z_hist:
for ind, line in enumerate(z_hist):
location = _find_histfile_var([os.path.join('~', '.zshrc'),
os.path.join('~', '.zprofile')],
os.path.join('~', '.zsh_history'))
if location:
with open(location, 'r', errors='backslashreplace') as zsh_hist:
for ind, line in enumerate(zsh_hist):
if line.startswith(':'):
try:
start_time, command = line.split(';', 1)
except ValueError:
@ -331,29 +313,25 @@ def _zsh_hist_parser(location=None, **kwargs):
try:
start_time = float(start_time.split(':')[1])
except ValueError:
start_time = -1
z_hist_formatted.append((command, start_time, ind))
return z_hist_formatted
start_time = 0.0
yield (command.rstrip(), start_time, ind)
else:
yield (line.rstrip(), 0.0, ind)
else:
print("No zsh history file found", file=sys.stderr)
def _bash_hist_parser(location=None, **kwargs):
default_location = os.path.join('~', '.bash_history')
location_list = [os.path.join('~', '.bashrc'),
os.path.join('~', '.bash_profile')]
"""Yield commands from bash history file"""
if location is None:
location = _find_histfile_var(location_list, default_location)
bash_hist_formatted = []
if location and os.path.isfile(location):
with open(location, 'r', errors='backslashreplace') as bash_file:
b_txt = bash_file.read()
bash_hist = b_txt.splitlines()
if bash_hist:
for ind, command in enumerate(bash_hist):
bash_hist_formatted.append((command, 0.0, ind))
return bash_hist_formatted
location = _find_histfile_var([os.path.join('~', '.bashrc'),
os.path.join('~', '.bash_profile')],
os.path.join('~', '.bash_history'))
if location:
with open(location, 'r', errors='backslashreplace') as bash_hist:
for ind, line in enumerate(bash_hist):
yield (line.rstrip(), 0.0, ind)
else:
print("No bash history file", file=sys.stderr)
@ -365,11 +343,11 @@ def _hist_create_parser():
description='Tools for dealing with history')
subp = p.add_subparsers(title='action', dest='action')
# session action
show = subp.add_parser('show', aliases=['session'],
help='displays session history, default action')
show = subp.add_parser('show', help='displays session history, default action')
show.add_argument('-r', dest='reverse', default=False,
action='store_true',
help='reverses the direction')
action='store_true', help='reverses the direction')
show.add_argument('-n', dest='numerate', default=False, action='store_true',
help='numerate each command')
show.add_argument('session', nargs='?', choices=_HIST_SESSIONS.keys(), default='session',
help='Choose a history session, defaults to current session')
show.add_argument('slices', nargs=argparse.REMAINDER, default=[],
@ -407,103 +385,83 @@ def _hist_create_parser():
return p
def _hist_show(ns=None, hist=None, start_index=None, end_index=None,
start_time=None, end_time=None, location=None):
"""Show the requested portion of shell history.
Accepts multiple history sources (xonsh, bash, zsh)
def _hist_get_portion(commands, slices):
"""Yield from portions of history commands."""
if len(slices) == 1:
s = ensure_slice(slices[0])
try:
yield from itertools.islice(commands, s.start, s.stop, s.step)
return
except ValueError: # islice failed
pass
commands = list(commands)
for s in slices:
s = ensure_slice(s)
yield from commands[s]
May be invoked as an alias with history all/bash/zsh which will
provide history as stdout or with __xonsh_history__.show()
which will return the history as a list with each item
in the tuple form (name, start_time, index).
If invoked via __xonsh_history__.show() then the ns parameter
can be supplied as a str with the follow options::
def _hist_filter_ts(commands, start_time=None, end_time=None):
"""Yield only the commands between start and end time."""
if start_time is None:
start_time = 0.0
elif isinstance(start_time, datetime.datetime):
start_time = start_time.timestamp()
if end_time is None:
end_time = float('inf')
elif isinstance(end_time, datetime.datetime):
end_time = end_time.timestamp()
for cmd in commands:
if start_time <= cmd[1] < end_time:
yield cmd
session - returns xonsh history from current session
xonsh - returns xonsh history from all sessions
all - alias of xonsh
zsh - returns all zsh history
bash - returns all bash history
def _hist_get(session='session', slices=None,
start_time=None, end_time=None, location=None):
"""Get the requested portion of shell history.
Parameters
----------
session: {'session', 'all', 'xonsh', 'bash', 'zsh'}
The history session to get.
slices : list of slice-like objects, optional
Get only portions of history.
start_time, end_time: float, optional
Filter commands by timestamp.
location: string, optional
The history file location (bash or zsh)
Returns
-------
generator
A filtered list of commands
"""
# Check if ns is a string, meaning it was invoked from
if hist is None:
hist = bultins.__xonsh_history__
alias = True
if isinstance(ns, str) and ns in _HIST_SESSIONS:
ns = _hist_create_parser().parse_args([ns])
alias = False
if not ns:
ns = _hist_create_parser().parse_args(['show', 'xonsh'])
alias = False
cmds = _HIST_SESSIONS[session](location=location)
if slices:
cmds = _hist_get_portion(cmds, slices)
if start_time or end_time:
cmds = _hist_filter_ts(cmds, start_time, end_time)
return cmds
def _hist_show(ns, *args, **kwargs):
"""Show the requested portion of shell history.
Accepts same parameters with `_hist_get`.
"""
commands = _hist_get(ns.session, ns.slices, **kwargs)
try:
commands = _HIST_SESSIONS[ns.session](hist=hist, location=location)
except KeyError:
print("{} is not a valid history session".format(ns.action))
return None
if not commands:
return None
if start_time:
if isinstance(start_time, datetime.datetime):
start_time = start_time.timestamp()
if isinstance(start_time, float):
commands = [c for c in commands if c[1] >= start_time]
if ns.reverse:
commands = reversed(list(commands))
if not ns.numerate:
for c, _, _ in commands:
print(c)
else:
print("Invalid start time, must be float or datetime.")
if end_time:
if isinstance(end_time, datetime.datetime):
end_time = end_time.timestamp()
if isinstance(end_time, float):
commands = [c for c in commands if c[1] <= end_time]
else:
print("Invalid end time, must be float or datetime.")
idx = None
if ns:
_commands = []
for s in ns.slices:
try:
s = ensure_slice(s)
except (ValueError, TypeError):
print('{!r} is not a valid slice format'.format(s), file=sys.stderr)
return
if s:
try:
_commands.extend(commands[s])
except IndexError:
err = "Index likely not in range. Only {} commands."
print(err.format(len(commands)), file=sys.stderr)
return
else:
if _commands:
commands = _commands
else:
idx = slice(start_index, end_index)
if (isinstance(idx, slice) and
start_time is None and end_time is None):
commands = commands[idx]
if ns and ns.reverse:
commands = list(reversed(commands))
if commands:
digits = len(str(max([i for c, t, i in commands])))
if alias:
for c, t, i in commands:
for line_ind, line in enumerate(c.split('\n')):
if line_ind == 0:
print('{:>{width}}: {}'.format(i, line,
width=digits + 1))
else:
print(' {:>>{width}} {}'.format('', line,
width=digits + 1))
else:
return commands
for c, _, i in commands:
print('{}: {}'.format(i, c))
except ValueError as err:
print("history: error: {}".format(err), file=sys.stderr)
#
# Interface to History
#
class History(object):
"""Xonsh session history.
@ -625,17 +583,15 @@ class History(object):
return hf
def show(self, *args, **kwargs):
"""
Returns shell history as a list
"""Return shell history as a list
Valid options:
`session` - returns xonsh history from current session
`show` - alias of `session`
`xonsh` - returns xonsh history from all sessions
`zsh` - returns all zsh history
`bash` - returns all bash history
"""
return _hist_show(*args, **kwargs)
return list(_hist_get(*args, **kwargs))
def _hist_info(ns, hist):
@ -687,14 +643,19 @@ def _hist_parse_args(args):
"""Parse arguments using the history argument parser."""
parser = _hist_create_parser()
if not args:
args = ['show']
elif args[0] not in ['-h', '--help'] and args[0] not in _HIST_MAIN_ACTIONS:
args.insert(0, 'show')
if (args[0] == 'show'
and len(args) > 1
and args[1] not in ['-h', '--help', '-r']
and args[1] not in _HIST_SESSIONS):
args.insert(1, 'session')
args = ['show', 'session']
elif args[0] not in _HIST_MAIN_ACTIONS and args[0] not in ('-h', '--help'):
args = ['show', 'session'] + args
elif args[0] == 'show':
slices_index = 0
for i, a in enumerate(args[1:], 1):
if a in _HIST_SESSIONS:
break
elif a.startswith('-') and a.lstrip('-').isalpha():
# get last optional arg, before slices
slices_index = i
else: # no session arg found, insert before slices
args.insert(slices_index + 1, 'session')
return parser.parse_args(args)
@ -702,4 +663,5 @@ def history_main(args=None, stdin=None):
"""This is the history command entry point."""
hist = builtins.__xonsh_history__
ns = _hist_parse_args(args)
_HIST_MAIN_ACTIONS[ns.action](ns, hist)
if ns:
_HIST_MAIN_ACTIONS[ns.action](ns, hist)

View file

@ -159,7 +159,7 @@ def premain(argv=None):
parser.print_help()
parser.exit()
if args.version:
version = '/'.join(('xonsh', __version__)),
version = '/'.join(('xonsh', __version__))
print(version)
parser.exit()
shell_kwargs = {'shell_type': args.shell_type,
@ -224,8 +224,8 @@ def main(argv=None):
# run a script contained in a file
path = os.path.abspath(os.path.expanduser(args.file))
if os.path.isfile(path):
sys.argv = args.args
env['ARGS'] = [args.file] + args.args
sys.argv = [args.file] + args.args
env['ARGS'] = sys.argv[:] # $ARGS is not sys.argv
env['XONSH_SOURCE'] = path
run_script_with_cache(args.file, shell.execer, glb=shell.ctx,
loc=None, mode='exec')

View file

@ -926,9 +926,11 @@ def SLICE_REG():
def ensure_slice(x):
"""Convert a string or int to a slice."""
if x is None:
"""Try to convert an object into a slice, complain on failure"""
if not x:
return slice(None)
elif isinstance(x, slice):
return x
try:
x = int(x)
s = slice(x, x+1)
@ -941,7 +943,10 @@ def ensure_slice(x):
else:
raise ValueError('cannot convert {!r} to slice'.format(x))
except TypeError:
raise TypeError('ensure_slice() argument must be a string or a number not {}'.format(type(x)))
try:
s = slice(*(int(i) for i in x))
except (TypeError, ValueError):
raise ValueError('cannot convert {!r} to slice'.format(x))
return s

View file

@ -80,6 +80,11 @@
"package": "xontrib-avox",
"url": "https://github.com/astronouth7303/xontrib-avox",
"description": ["Automatic (de)activation of virtual environments as you cd around"]
},
{"name": "z",
"package": "xontrib-z",
"url": "https://github.com/astronouth7303/xontrib-z",
"description": ["Tracks your most used directories, based on 'frecency'."]
}
],
"packages": {
@ -161,6 +166,13 @@
"install": {
"pip": "pip install xontrib-avox"
}
},
"xontrib-z": {
"license": "GPLv3",
"url": "https://github.com/astronouth7303/xontrib-z",
"install": {
"pip": "pip install xontrib-z"
}
}
}
}

View file

@ -36,7 +36,11 @@ def xontrib_context(name):
ImportWarning)
return {}
m = importlib.import_module(spec.name)
ctx = {k: getattr(m, k) for k in dir(m) if not k.startswith('_')}
pubnames = getattr(m, '__all__', None)
if pubnames is not None:
ctx = {k: getattr(m, k) for k in pubnames}
else:
ctx = {k: getattr(m, k) for k in dir(m) if not k.startswith('_')}
return ctx

View file

@ -178,7 +178,7 @@ class Vox(collections.abc.Mapping):
if 'VIRTUAL_ENV' in env:
self.deactivate()
type(self).oldvars = {'PATH': env['PATH']}
type(self).oldvars = {'PATH': list(env['PATH'])}
env['PATH'].insert(0, bin_path)
env['VIRTUAL_ENV'] = env_path
if 'PYTHONHOME' in env: