Merge branch 'master' into bgmod

This commit is contained in:
Anthony Scopatz 2016-06-29 12:41:19 -04:00
commit f58ef01161
24 changed files with 301 additions and 121 deletions

View file

@ -158,7 +158,7 @@ Dependencies
Prep your environment for running the tests::
$ pip install requirements-tests.txt
$ pip install -r requirements-tests.txt
----------------------------------

View file

@ -259,14 +259,14 @@ Helpful Links
=============
* `Documentation <http://xon.sh>`_
* `Gitter <https://gitter.im/scopatz/xonsh>`_
* `Gitter <https://gitter.im/xonsh/xonsh>`_
* `Mailing list <https://groups.google.com/forum/#!forum/xonsh>`_
* `IRC: channel #xonsh on OFTC <http://www.oftc.net/>`_
* `GitHub Repository <https://github.com/scopatz/xonsh>`_
* `GitHub Repository <https://github.com/xonsh/xonsh>`_
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. raw:: html
<a href="https://github.com/scopatz/xonsh"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/52760788cde945287fbb584134c4cbc2bc36f904/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f77686974655f6666666666662e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png"></a>
<a href="https://github.com/xonsh/xonsh"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/52760788cde945287fbb584134c4cbc2bc36f904/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f77686974655f6666666666662e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png"></a>

View file

@ -24,8 +24,8 @@ You can install xonsh using ``conda``, ``pip``, or from source.
$ pip install xonsh
**source:** Download the source `from github <https://github.com/scopatz/xonsh>`_
(`zip file <https://github.com/scopatz/xonsh/archive/master.zip>`_), then run
**source:** Download the source `from github <https://github.com/xonsh/xonsh>`_
(`zip file <https://github.com/xonsh/xonsh/archive/master.zip>`_), then run
the following from the source directory,
.. code-block:: bash

View file

@ -31,8 +31,8 @@ You can install xonsh using homebrew, conda, pip, or from source.
$ pip install xonsh
**source:** Download the source `from github <https://github.com/scopatz/xonsh>`_
(`zip file <https://github.com/scopatz/xonsh/archive/master.zip>`_), then run
**source:** Download the source `from github <https://github.com/xonsh/xonsh>`_
(`zip file <https://github.com/xonsh/xonsh/archive/master.zip>`_), then run
the following from the source directory,
.. code-block:: bash

View file

@ -19,8 +19,8 @@ Version Date Zip Tar
0.1.0 2015-03-09 `zip 0.1`_ `tar 0.1`_
======= ========== ============== ==============
.. _zip 0.2: https://github.com/scopatz/xonsh/zipball/0.2
.. _tar 0.2: https://github.com/scopatz/xonsh/tarball/0.2
.. _zip 0.1: https://github.com/scopatz/xonsh/zipball/0.1
.. _tar 0.1: https://github.com/scopatz/xonsh/tarball/0.1
.. _zip 0.2: https://github.com/xonsh/xonsh/zipball/0.2
.. _tar 0.2: https://github.com/xonsh/xonsh/tarball/0.2
.. _zip 0.1: https://github.com/xonsh/xonsh/zipball/0.1
.. _tar 0.1: https://github.com/xonsh/xonsh/tarball/0.1

View file

@ -180,7 +180,7 @@ Of course, your under no obligation to register your xontrib. Users will
still be able to load your xontrib, as long as they have it installed.
To register a xontrib, add an entry to
`the xontribs.json file <https://github.com/scopatz/xonsh/blob/master/xonsh/xontribs.json>`_
`the xontribs.json file <https://github.com/xonsh/xonsh/blob/master/xonsh/xontribs.json>`_
in the main xonsh repository. A pull request is probably best, but if you
are having trouble figuring it out please contact one of the xonsh devs
with the relevant information.

View file

@ -67,7 +67,7 @@ Next, run xonsh:
snail@home ~ $
.. _Python v3.4+: https://www.python.org/downloads/windows/
.. _xonsh-master.zip: https://github.com/scopatz/xonsh/archive/master.zip
.. _xonsh-master.zip: https://github.com/xonsh/xonsh/archive/master.zip
.. _cmder: http://cmder.net/
.. _conemu: https://conemu.github.io/

13
news/rmenv.rst Normal file
View file

@ -0,0 +1,13 @@
**Added:** None
**Changed:** None
**Deprecated:** None
**Removed:**
* Removed the ``xonsh.built_ins.ENV`` global instance of the Env class.
**Fixed:** None
**Security:** None

15
news/sortglob.rst Normal file
View file

@ -0,0 +1,15 @@
**Added:**
* The results of glob expressions are sorted if ``$GLOB_SORTED`` is set.
**Changed:**
* ``GLOB_SORTED`` is enabled by default.
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

16
news/sub.rst Normal file
View file

@ -0,0 +1,16 @@
**Added:**
* LazyObjects will now load themselves on ``__getitem__()``
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:**
* Bug with setting hist size not being settable due to lazy object loading
has been resolved.
**Security:** None

View file

@ -2,7 +2,7 @@
# -*- coding: ascii -*-
"""The xonsh installer."""
# Note: Do not embed any non-ASCII characters in this file until pip has been
# fixed. See https://github.com/scopatz/xonsh/issues/487.
# fixed. See https://github.com/xonsh/xonsh/issues/487.
from __future__ import print_function, unicode_literals
import os
import sys
@ -247,7 +247,7 @@ def main():
author='Anthony Scopatz',
maintainer='Anthony Scopatz',
author_email='scopatz@gmail.com',
url='https://github.com/scopatz/xonsh',
url='https://github.com/xonsh/xonsh',
platforms='Cross Platform',
classifiers=['Programming Language :: Python :: 3'],
packages=['xonsh', 'xonsh.ply', 'xonsh.ptk', 'xonsh.parsers',

View file

@ -50,8 +50,8 @@ def test_eval_recursive():
@pytest.mark.skipif(ON_WINDOWS, reason='Unix stuff')
def test_eval_recursive_callable_partial():
built_ins.ENV = Env(HOME=os.path.expanduser('~'))
with mock_xonsh_env(built_ins.ENV):
env = Env(HOME=os.path.expanduser('~'))
with mock_xonsh_env(env):
assert ALIASES.get('indirect_cd')(['arg2', 'arg3']) == ['..', 'arg2', 'arg3']
class TestWhich:

View file

@ -24,8 +24,8 @@ def test_reglob_tests():
@pytest.mark.skipif(ON_WINDOWS, reason='Unix stuff')
def test_repath_backslash():
home = os.path.expanduser('~')
built_ins.ENV = Env(HOME=home)
with mock_xonsh_env(built_ins.ENV):
env = Env(HOME=home)
with mock_xonsh_env(env):
exp = os.listdir(home)
exp = {p for p in exp if re.match(r'\w\w.*', p)}
exp = {os.path.join(home, p) for p in exp}
@ -35,8 +35,8 @@ def test_repath_backslash():
@pytest.mark.skipif(ON_WINDOWS, reason='Unix stuff')
def test_repath_home_itself():
exp = os.path.expanduser('~')
built_ins.ENV = Env(HOME=exp)
with mock_xonsh_env(built_ins.ENV):
env = Env(HOME=exp)
with mock_xonsh_env(env):
obs = pathsearch(regexsearch, '~')
assert 1 == len(obs)
assert exp == obs[0]
@ -44,8 +44,8 @@ def test_repath_home_itself():
@pytest.mark.skipif(ON_WINDOWS, reason='Unix stuff')
def test_repath_home_contents():
home = os.path.expanduser('~')
built_ins.ENV = Env(HOME=home)
with mock_xonsh_env(built_ins.ENV):
env = Env(HOME=home)
with mock_xonsh_env(env):
exp = os.listdir(home)
exp = {os.path.join(home, p) for p in exp}
obs = set(pathsearch(regexsearch, '~/.*'))
@ -54,8 +54,8 @@ def test_repath_home_contents():
@pytest.mark.skipif(ON_WINDOWS, reason='Unix stuff')
def test_repath_home_var():
exp = os.path.expanduser('~')
built_ins.ENV = Env(HOME=exp)
with mock_xonsh_env(built_ins.ENV):
env = Env(HOME=exp)
with mock_xonsh_env(env):
obs = pathsearch(regexsearch, '$HOME')
assert 1 == len(obs)
assert exp == obs[0]
@ -63,8 +63,8 @@ def test_repath_home_var():
@pytest.mark.skipif(ON_WINDOWS, reason='Unix stuff')
def test_repath_home_var_brace():
exp = os.path.expanduser('~')
built_ins.ENV = Env(HOME=exp)
with mock_xonsh_env(built_ins.ENV):
env = Env(HOME=exp)
with mock_xonsh_env(env):
obs = pathsearch(regexsearch, '${"HOME"}')
assert 1 == len(obs)
assert exp == obs[0]

View file

@ -4,13 +4,14 @@ from __future__ import unicode_literals, print_function
from xonsh import imphooks # noqa
from xonsh import built_ins
from xonsh.environ import Env
from xonsh.execer import Execer
from xonsh.built_ins import load_builtins, unload_builtins
from tools import mock_xonsh_env
LOADED_HERE = False
IMP_ENV = {'PATH': [], 'PATHEXT': []}
IMP_ENV = Env({'PATH': [], 'PATHEXT': []})
def setup_module():
global LOADED_HERE

11
tests/test_lazyasd.py Normal file
View file

@ -0,0 +1,11 @@
"""Tests lazy and self destruictive objects."""
from xonsh.lazyasd import LazyObject
#
# LazyObject Tests
#
def test_lazyobject_getitem():
lo = LazyObject(lambda: {'x': 1}, {}, 'lo')
assert 1 == lo['x']

View file

@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
"""Tests the xonsh lexer."""
"""Tests xonsh tools."""
import os
import pathlib
from tempfile import TemporaryDirectory
import stat
import builtins
import pytest
@ -15,7 +16,7 @@ from xonsh.tools import (
bool_or_int_to_str, bool_to_str, check_for_partial_string,
dynamic_cwd_tuple_to_str, ensure_int_or_slice, ensure_string,
env_path_to_str, escape_windows_cmd_string, executables_in,
expand_case_matching, find_next_break, is_bool, is_bool_or_int,
expand_case_matching, find_next_break, iglobpath, is_bool, is_bool_or_int,
is_callable, is_dynamic_cwd_width, is_env_path, is_float, is_int,
is_int_as_str, is_logfile_opt, is_slice_as_str, is_string,
is_string_or_callable, logfile_opt_to_str, str_to_env_path,
@ -25,6 +26,8 @@ from xonsh.tools import (
pathsep_to_upper_seq, seq_to_upper_pathsep,
)
from xonsh.commands_cache import CommandsCache
from xonsh.built_ins import expand_path
from xonsh.environ import Env
from tools import mock_xonsh_env
@ -317,6 +320,7 @@ def test_subexpr_from_unbalanced_parens():
obs = subexpr_from_unbalanced(expr, '(', ')')
assert exp == obs
def test_find_next_break():
cases = [
('ls && echo a', 0, 4),
@ -331,6 +335,41 @@ def test_find_next_break():
assert exp == obs
def test_iglobpath():
with TemporaryDirectory() as test_dir:
# Create files 00.test to 99.test in unsorted order
num = 18
for _ in range(100):
s = str(num).zfill(2)
path = os.path.join(test_dir, s + '.test')
with open(path, 'w') as file:
file.write(s + '\n')
num = (num + 37) % 100
# Create one file not matching the '*.test'
with open(os.path.join(test_dir, '07'), 'w') as file:
file.write('test\n')
with mock_xonsh_env(Env(EXPAND_ENV_VARS=False)):
builtins.__xonsh_expand_path__ = expand_path
paths = list(iglobpath(os.path.join(test_dir, '*.test'),
ignore_case=False, sort_result=False))
assert len(paths) == 100
paths = list(iglobpath(os.path.join(test_dir, '*'),
ignore_case=True, sort_result=False))
assert len(paths) == 101
paths = list(iglobpath(os.path.join(test_dir, '*.test'),
ignore_case=False, sort_result=True))
assert len(paths) == 100
assert paths == sorted(paths)
paths = list(iglobpath(os.path.join(test_dir, '*'),
ignore_case=True, sort_result=True))
assert len(paths) == 101
assert paths == sorted(paths)
def test_is_int():
cases = [
(42, True),
@ -389,7 +428,7 @@ def test_is_slice_as_str():
(None, False),
('42', False),
('-42', False),
(slice(1,2,3), False),
(slice(1, 2, 3), False),
([], False),
(False, False),
(True, False),
@ -527,8 +566,6 @@ def test_seq_to_upper_pathsep():
assert exp == obs
def test_is_env_path():
cases = [
('/home/wakka', False),
@ -566,8 +603,9 @@ def test_env_path_to_str():
def test_env_path():
# lambda to expand the expected paths
expand = lambda path: os.path.expanduser(os.path.expandvars(path))
def expand(path):
return os.path.expanduser(os.path.expandvars(path))
getitem_cases = [
('xonsh_dir', 'xonsh_dir'),
('.', '.'),
@ -604,11 +642,11 @@ def test_env_path():
# cases that involve pathlib.Path objects
pathlib_cases = [
(pathlib.Path('/home/wakka'), ['/home/wakka'.replace('/',os.sep)]),
(pathlib.Path('/home/wakka'), ['/home/wakka'.replace('/', os.sep)]),
(pathlib.Path('~/'), ['~']),
(pathlib.Path('.'), ['.']),
(['/home/wakka', pathlib.Path('/home/jakka'), '~/'],
['/home/wakka', '/home/jakka'.replace('/',os.sep), '~/']),
['/home/wakka', '/home/jakka'.replace('/', os.sep), '~/']),
(['/home/wakka', pathlib.Path('../'), '../'],
['/home/wakka', '..', '../']),
(['/home/wakka', pathlib.Path('~/'), '~/'],
@ -623,7 +661,8 @@ def test_env_path():
def test_env_path_slices():
# build os-dependent paths properly
mkpath = lambda *paths: os.sep + os.sep.join(paths)
def mkpath(*paths):
return os.sep + os.sep.join(paths)
# get all except the last element in a slice
slice_last = [
@ -790,6 +829,7 @@ def test_is_dynamic_cwd_width():
obs = is_dynamic_cwd_width(inp)
assert exp == obs
def test_is_logfile_opt():
cases = [
('throwback.log', True),
@ -808,6 +848,7 @@ def test_is_logfile_opt():
obs = is_logfile_opt(inp)
assert exp == obs
def test_to_logfile_opt():
cases = [
(True, None),
@ -823,6 +864,7 @@ def test_to_logfile_opt():
obs = to_logfile_opt(inp)
assert exp == obs
def test_logfile_opt_to_str():
cases = [
(None, ''),
@ -834,6 +876,7 @@ def test_logfile_opt_to_str():
obs = logfile_opt_to_str(inp)
assert exp == obs
def test_to_dynamic_cwd_tuple():
cases = [
('20', (20.0, 'c')),

View file

@ -13,7 +13,6 @@ import pytest
from xonsh.built_ins import ensure_list_of_strs
from xonsh.environ import Env
builtins.__xonsh_env__ = Env()
from xonsh.base_shell import BaseShell
from xonsh.execer import Execer
from xonsh.tools import XonshBlockError
@ -38,9 +37,11 @@ skip_if_py35plus = pytest.mark.skipif(VER_MAJOR_MINOR < VER_3_5,
def sp(cmd):
return subprocess.check_output(cmd, universal_newlines=True)
class DummyStyler():
styles = defaultdict(None.__class__)
class DummyBaseShell(BaseShell):
def __init__(self):
@ -106,12 +107,14 @@ def mock_xonsh_env(xenv):
DEBUG_LEVEL = 0
EXECER = None
def execer_setup():
# only setup one parser
global EXECER
if EXECER is None:
EXECER = Execer(debug_level=DEBUG_LEVEL, login=False)
def check_exec(input, **kwargs):
with mock_xonsh_env(None):
if not input.endswith('\n'):
@ -119,15 +122,17 @@ def check_exec(input, **kwargs):
EXECER.debug_level = DEBUG_LEVEL
EXECER.exec(input, **kwargs)
def check_eval(input):
env = {'AUTO_CD': False, 'XONSH_ENCODING' :'utf-8',
'XONSH_ENCODING_ERRORS': 'strict', 'PATH': []}
env = Env({'AUTO_CD': False, 'XONSH_ENCODING': 'utf-8',
'XONSH_ENCODING_ERRORS': 'strict', 'PATH': []})
if ON_WINDOWS:
env['PATHEXT'] = ['.COM', '.EXE', '.BAT', '.CMD']
with mock_xonsh_env(env):
EXECER.debug_level = DEBUG_LEVEL
EXECER.eval(input)
def check_parse(input):
with mock_xonsh_env(None):
EXECER.debug_level = DEBUG_LEVEL

View file

@ -339,7 +339,7 @@ class AWitchAWitch(Action):
def __call__(self, parser, namespace, values, option_string=None):
import webbrowser
webbrowser.open('https://github.com/scopatz/xonsh/commit/f49b400')
webbrowser.open('https://github.com/xonsh/xonsh/commit/f49b400')
parser.exit()

View file

@ -36,7 +36,6 @@ from xonsh.tools import (
from xonsh.commands_cache import CommandsCache
ENV = None
BUILTINS_LOADED = False
INSPECTOR = LazyObject(Inspector, globals(), 'INSPECTOR')
AT_EXIT_SIGNALS = (signal.SIGABRT, signal.SIGFPE, signal.SIGILL, signal.SIGSEGV,
@ -86,8 +85,7 @@ def superhelper(x, name=''):
def expand_path(s):
"""Takes a string path and expands ~ to home and environment vars."""
global ENV
if ENV.get('EXPAND_ENV_VARS'):
if builtins.__xonsh_env__.get('EXPAND_ENV_VARS'):
s = expandvars(s)
return os.path.expanduser(s)
@ -138,7 +136,9 @@ def regexsearch(s):
def globsearch(s):
csc = builtins.__xonsh_env__.get('CASE_SENSITIVE_COMPLETIONS')
return globpath(s, ignore_case=(not csc), return_empty=True)
glob_sorted = builtins.__xonsh_env__.get('GLOB_SORTED')
return globpath(s, ignore_case=(not csc), return_empty=True,
sort_result=glob_sorted)
def pathsearch(func, s, pymode=False):
@ -345,7 +345,7 @@ def run_subproc(cmds, captured=False):
Lastly, the captured argument affects only the last real command.
"""
global ENV
env = builtins.__xonsh_env__
background = False
procinfo = {}
if cmds[-1] == '&':
@ -448,7 +448,7 @@ def run_subproc(cmds, captured=False):
raise XonshError(e.format(cmd[0]))
_stdin_file = None
if (stdin is not None and
ENV.get('XONSH_STORE_STDIN') and
env.get('XONSH_STORE_STDIN') and
captured == 'object' and
__xonsh_commands_cache__.lazy_locate_binary('cat') and
__xonsh_commands_cache__.lazy_locate_binary('tee')):
@ -478,7 +478,7 @@ def run_subproc(cmds, captured=False):
prev_is_proxy = False
usetee = ((stdout is None) and
(not background) and
ENV.get('XONSH_STORE_STDOUT', False))
env.get('XONSH_STORE_STDOUT', False))
cls = TeePTYProc if usetee else Popen
subproc_kwargs = {}
if ON_POSIX and cls is Popen:
@ -489,15 +489,15 @@ def run_subproc(cmds, captured=False):
os.setpgid(0, _pipeline_group)
signal.signal(signal.SIGTSTP, lambda n, f: signal.pause())
subproc_kwargs['preexec_fn'] = _subproc_pre
env = ENV.detype()
denv = env.detype()
if ON_WINDOWS:
# Over write prompt variable as xonsh's $PROMPT does
# not make much sense for other subprocs
env['PROMPT'] = '$P$G'
denv['PROMPT'] = '$P$G'
try:
proc = cls(aliased_cmd,
universal_newlines=uninew,
env=env,
env=denv,
stdin=stdin,
stdout=stdout,
stderr=stderr,
@ -508,9 +508,9 @@ def run_subproc(cmds, captured=False):
except FileNotFoundError:
cmd = aliased_cmd[0]
e = 'xonsh: subprocess mode: command not found: {0}'.format(cmd)
sug = suggest_commands(cmd, ENV, builtins.aliases)
sug = suggest_commands(cmd, env, builtins.aliases)
if len(sug.strip()) > 0:
e += '\n' + suggest_commands(cmd, ENV, builtins.aliases)
e += '\n' + suggest_commands(cmd, env, builtins.aliases)
raise XonshError(e)
procs.append(proc)
prev_proc = proc
@ -523,8 +523,8 @@ def run_subproc(cmds, captured=False):
'obj': prev_proc,
'bg': background
})
if (ENV.get('XONSH_INTERACTIVE') and
not ENV.get('XONSH_STORE_STDOUT') and
if (env.get('XONSH_INTERACTIVE') and
not env.get('XONSH_STORE_STDOUT') and
not _capture_streams):
# set title here to get current command running
try:
@ -559,8 +559,8 @@ def run_subproc(cmds, captured=False):
if _capture_streams:
# to get proper encoding from Popen, we have to
# use a byte stream and then implement universal_newlines here
output = output.decode(encoding=ENV.get('XONSH_ENCODING'),
errors=ENV.get('XONSH_ENCODING_ERRORS'))
output = output.decode(encoding=env.get('XONSH_ENCODING'),
errors=env.get('XONSH_ENCODING_ERRORS'))
output = output.replace('\r\n', '\n')
else:
hist.last_cmd_out = output
@ -578,8 +578,8 @@ def run_subproc(cmds, captured=False):
elif unnamed:
errout = prev_proc.stderr.read()
if named or unnamed:
errout = errout.decode(encoding=ENV.get('XONSH_ENCODING'),
errors=ENV.get('XONSH_ENCODING_ERRORS'))
errout = errout.decode(encoding=env.get('XONSH_ENCODING'),
errors=env.get('XONSH_ENCODING_ERRORS'))
errout = errout.replace('\r\n', '\n')
procinfo['stderr'] = errout
@ -593,7 +593,7 @@ def run_subproc(cmds, captured=False):
if (not prev_is_proxy and
hist.last_cmd_rtn is not None and
hist.last_cmd_rtn > 0 and
ENV.get('RAISE_SUBPROC_ERROR')):
env.get('RAISE_SUBPROC_ERROR')):
raise CalledProcessError(hist.last_cmd_rtn, aliased_cmd, output=output)
if captured == 'stdout':
return output
@ -675,10 +675,10 @@ def load_builtins(execer=None, config=None, login=False, ctx=None):
"""Loads the xonsh builtins into the Python builtins. Sets the
BUILTINS_LOADED variable to True.
"""
global BUILTINS_LOADED, ENV
global BUILTINS_LOADED
# private built-ins
builtins.__xonsh_config__ = {}
builtins.__xonsh_env__ = ENV = Env(default_env(config=config, login=login))
builtins.__xonsh_env__ = env = Env(default_env(config=config, login=login))
builtins.__xonsh_help__ = helper
builtins.__xonsh_superhelp__ = superhelper
builtins.__xonsh_pathsearch__ = pathsearch
@ -720,7 +720,7 @@ def load_builtins(execer=None, config=None, login=False, ctx=None):
builtins.aliases.update(load_foreign_aliases(issue_warning=False))
# history needs to be started after env and aliases
# would be nice to actually include non-detyped versions.
builtins.__xonsh_history__ = History(env=ENV.detype(),
builtins.__xonsh_history__ = History(env=env.detype(),
ts=[time.time(), None], locked=True)
atexit.register(_lastflush)
for sig in AT_EXIT_SIGNALS:
@ -737,10 +737,10 @@ def unload_builtins():
"""Removes the xonsh builtins from the Python builtins, if the
BUILTINS_LOADED is True, sets BUILTINS_LOADED to False, and returns.
"""
global BUILTINS_LOADED, ENV
if ENV is not None:
ENV.undo_replace_env()
ENV = None
global BUILTINS_LOADED
env = getattr(builtins, '__xonsh_env__', None)
if isinstance(env, Env):
env.undo_replace_env()
if hasattr(builtins, '__xonsh_pyexit__'):
builtins.exit = builtins.__xonsh_pyexit__
if hasattr(builtins, '__xonsh_pyquit__'):

View file

@ -105,9 +105,11 @@ def _add_cdpaths(paths, prefix):
"""Completes current prefix using CDPATH"""
env = builtins.__xonsh_env__
csc = env.get('CASE_SENSITIVE_COMPLETIONS')
glob_sorted = env.get('GLOB_SORTED')
for cdp in env.get('CDPATH'):
test_glob = os.path.join(cdp, prefix) + '*'
for s in iglobpath(test_glob, ignore_case=(not csc)):
for s in iglobpath(test_glob, ignore_case=(not csc),
sort_result=glob_sorted):
if os.path.isdir(s):
paths.add(os.path.basename(s))
@ -222,9 +224,10 @@ def _subsequence_match_iter(ref, typed):
def _expand_one(sofar, nextone, csc):
out = set()
glob_sorted = builtins.__xonsh_env__.get('GLOB_SORTED')
for i in sofar:
_glob = os.path.join(_joinpath(i), '*') if i is not None else '*'
for j in iglobpath(_glob):
for j in iglobpath(_glob, sort_result=glob_sorted):
j = os.path.basename(j)
if subsequence_match(j, nextone, csc):
out.add((i or ()) + (j, ))
@ -247,7 +250,9 @@ def complete_path(prefix, line, start, end, ctx, cdpath=True, filtfunc=None):
paths = set()
env = builtins.__xonsh_env__
csc = env.get('CASE_SENSITIVE_COMPLETIONS')
for s in iglobpath(prefix + '*', ignore_case=(not csc)):
glob_sorted = env.get('GLOB_SORTED')
for s in iglobpath(prefix + '*', ignore_case=(not csc),
sort_result=glob_sorted):
paths.add(s)
if len(paths) == 0 and env.get('SUBSEQUENCE_PATH_COMPLETION'):
# this block implements 'subsequence' matching, similar to fish and zsh.
@ -267,7 +272,8 @@ def complete_path(prefix, line, start, end, ctx, cdpath=True, filtfunc=None):
paths |= {_joinpath(i) for i in matches_so_far}
if len(paths) == 0 and env.get('FUZZY_PATH_COMPLETION'):
threshold = env.get('SUGGEST_THRESHOLD')
for s in iglobpath(os.path.dirname(prefix) + '*', ignore_case=(not csc)):
for s in iglobpath(os.path.dirname(prefix) + '*', ignore_case=(not csc),
sort_result=glob_sorted):
if levenshtein(prefix, s, threshold) < threshold:
paths.add(s)
if tilde in prefix:

View file

@ -100,6 +100,7 @@ DEFAULT_ENSURERS = {
dynamic_cwd_tuple_to_str),
'FORCE_POSIX_PATHS': (is_bool, to_bool, bool_to_str),
'FUZZY_PATH_COMPLETION': (is_bool, to_bool, bool_to_str),
'GLOB_SORTED': (is_bool, to_bool, bool_to_str),
'HISTCONTROL': (is_string_set, csv_to_set, set_to_csv),
'IGNOREEOF': (is_bool, to_bool, bool_to_str),
'INTENSIFY_COLORS_ON_WIN':(always_false, intensify_colors_on_win_setter,
@ -222,6 +223,7 @@ DEFAULT_VALUES = {
'EXPAND_ENV_VARS': True,
'FORCE_POSIX_PATHS': False,
'FUZZY_PATH_COMPLETION': True,
'GLOB_SORTED': True,
'HISTCONTROL': set(),
'IGNOREEOF': False,
'INDENT': ' ',
@ -379,6 +381,9 @@ DEFAULT_DOCS = {
"used as a fallback if no other completions succeed but can be used "
"as a way to adjust for typographical errors. If ``True``, then, e.g.,"
" ``xonhs`` will match ``xonsh``."),
'GLOB_SORTED': VarDocs(
"Toggles whether globbing results are manually sorted. If ``False``, "
"the results are returned in arbitrary order."),
'HISTCONTROL': VarDocs(
'A set of strings (comma-separated list in string form) of options '
'that determine what commands are saved to the history list. By '

View file

@ -27,7 +27,7 @@ class LazyObject(object):
load : function with no arguments
A loader function that performs the actual object construction.
ctx : Mapping
Context to replace the LazyAndSelfDestructiveObject instance in
Context to replace the LazyObject instance in
with the object returned by load().
name : str
Name in the context to give the loaded object. This *should*
@ -64,6 +64,10 @@ class LazyObject(object):
obj = self._lazy_obj()
yield from obj
def __getitem__(self, item):
obj = self._lazy_obj()
return obj[item]
class LazyDict(abc.MutableMapping):
@ -89,7 +93,7 @@ class LazyDict(abc.MutableMapping):
A mapping of loader function that performs the actual value
construction upon acces.
ctx : Mapping
Context to replace the LazyAndSelfDestructiveDict instance in
Context to replace the LazyDict instance in
with the the fully loaded mapping.
name : str
Name in the context to give the loaded mapping. This *should*
@ -153,7 +157,7 @@ class LazyBool(object):
load : function with no arguments
A loader function that performs the actual boolean evaluation.
ctx : Mapping
Context to replace the LazyAndSelfDestructiveDict instance in
Context to replace the LazyBool instance in
with the the fully loaded mapping.
name : str
Name in the context to give the loaded mapping. This *should*

View file

@ -38,10 +38,8 @@ from subprocess import CalledProcessError
# adding further imports from xonsh modules is discouraged to avoid circular
# dependencies
from xonsh.lazyasd import LazyObject, LazyDict
from xonsh.platform import (
has_prompt_toolkit, scandir,
DEFAULT_ENCODING, ON_LINUX, ON_WINDOWS, PYTHON_VERSION_INFO,
)
from xonsh.platform import (has_prompt_toolkit, scandir, DEFAULT_ENCODING,
ON_LINUX, ON_WINDOWS, PYTHON_VERSION_INFO)
@functools.lru_cache(1)
@ -145,8 +143,8 @@ class EnvPath(collections.MutableSequence):
# in order to be able to retrieve it later, for cases such as
# when a generator expression was passed as an argument
args = list(args)
if not all(isinstance(i, (str, bytes, pathlib.Path)) \
for i in args):
if not all(isinstance(i, (str, bytes, pathlib.Path))
for i in args):
# make TypeError's message as informative as possible
# when given an invalid initialization sequence
raise TypeError(
@ -222,7 +220,8 @@ def _is_not_lparen_and_rparen(lparens, rtok):
def find_next_break(line, mincol=0, lexer=None):
"""Returns the column number of the next logical break in subproc mode.
This function may be useful in finding the maxcol argument of subproc_toks().
This function may be useful in finding the maxcol argument of
subproc_toks().
"""
if mincol >= 1:
line = line[mincol:]
@ -409,13 +408,15 @@ def get_sep():
""" Returns the appropriate filepath separator char depending on OS and
xonsh options set
"""
return (os.altsep if ON_WINDOWS
and builtins.__xonsh_env__.get('FORCE_POSIX_PATHS') else
os.sep)
if ON_WINDOWS and builtins.__xonsh_env__.get('FORCE_POSIX_PATHS'):
return os.altsep
else:
return os.sep
def fallback(cond, backup):
"""Decorator for returning the object if cond is true and a backup if cond is false.
"""Decorator for returning the object if cond is true and a backup if cond
is false.
"""
def dec(obj):
return obj if cond else backup
@ -485,9 +486,10 @@ def _executables_in_posix(path):
return
elif PYTHON_VERSION_INFO < (3, 5, 0):
for fname in os.listdir(path):
fpath = os.path.join(path, fname)
if (os.path.exists(fpath) and os.access(fpath, os.X_OK) and \
(not os.path.isdir(fpath))):
fpath = os.path.join(path, fname)
if (os.path.exists(fpath) and
os.access(fpath, os.X_OK) and
(not os.path.isdir(fpath))):
yield fname
else:
yield from _yield_accessible_unix_file_names(path)
@ -561,9 +563,10 @@ def suggest_commands(cmd, env, aliases):
for path in filter(os.path.isdir, env.get('PATH')):
for _file in executables_in(path):
if _file not in suggested \
and levenshtein(_file.lower(), cmd, thresh) < thresh:
suggested[_file] = 'Command ({0})'.format(os.path.join(path, _file))
if (_file not in suggested and
levenshtein(_file.lower(), cmd, thresh) < thresh):
suggested[_file] = \
'Command ({0})'.format(os.path.join(path, _file))
suggested = collections.OrderedDict(
sorted(suggested.items(),
@ -658,6 +661,7 @@ def is_writable_file(filepath):
# and ensure that directory is writable instead
return os.access(os.path.dirname(filepath), os.W_OK)
# Modified from Public Domain code, by Magnus Lie Hetland
# from http://hetland.org/coding/python/levenshtein.py
def levenshtein(a, b, max_dist=float('inf')):
@ -757,6 +761,7 @@ def is_int(x):
"""Tests if something is an integer"""
return isinstance(x, int)
def is_int_as_str(x):
"""
Tests if something is an integer
@ -780,10 +785,12 @@ def is_string(x):
"""Tests if something is a string"""
return isinstance(x, str)
def is_slice(x):
"""Tests if something is a slice"""
return isinstance(x, slice)
def is_slice_as_str(x):
"""
Tests if a str is a slice
@ -802,6 +809,7 @@ def is_slice_as_str(x):
return True
return False
def is_callable(x):
"""Tests if something is callable"""
return callable(x)
@ -841,7 +849,9 @@ def str_to_env_path(x):
def env_path_to_str(x):
"""Converts an environment path to a string by joining on the OS separator."""
"""Converts an environment path to a string by joining on the OS
separator.
"""
return os.pathsep.join(x)
@ -857,8 +867,10 @@ def is_logfile_opt(x):
"""
if x is None:
return True
return False if not isinstance(x, str) else \
(is_writable_file(x) or x == '')
if not isinstance(x, str):
return False
else:
return (is_writable_file(x) or x == '')
def to_logfile_opt(x):
@ -905,7 +917,9 @@ def to_bool(x):
def bool_to_str(x):
"""Converts a bool to an empty string if False and the string '1' if True."""
"""Converts a bool to an empty string if False and the string '1' if
True.
"""
return '1' if x else ''
@ -1150,12 +1164,14 @@ HISTORY_UNITS = LazyObject(lambda: {
}, globals(), 'HISTORY_UNITS')
"""Maps lowercase unit names to canonical name and conversion utilities."""
def is_history_tuple(x):
"""Tests if something is a proper history value, units tuple."""
if isinstance(x, abc.Sequence) and len(x) == 2 and \
isinstance(x[0], (int, float)) and \
x[1].lower() in CANON_HISTORY_UNITS:
return True
if (isinstance(x, abc.Sequence) and
len(x) == 2 and
isinstance(x[0], (int, float)) and
x[1].lower() in CANON_HISTORY_UNITS):
return True
return False
@ -1163,8 +1179,10 @@ def is_dynamic_cwd_width(x):
""" Determine if the input is a valid input for the DYNAMIC_CWD_WIDTH
environement variable.
"""
return isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], float) and \
(x[1] in set('c%'))
return (isinstance(x, tuple) and
len(x) == 2 and
isinstance(x[0], float) and
x[1] in set('c%'))
def to_dynamic_cwd_tuple(x):
@ -1193,6 +1211,7 @@ RE_HISTORY_TUPLE = LazyObject(
lambda: re.compile('([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\s*([A-Za-z]*)'),
globals(), 'RE_HISTORY_TUPLE')
def to_history_tuple(x):
"""Converts to a canonincal history tuple."""
if not isinstance(x, (abc.Sequence, float, int)):
@ -1323,7 +1342,7 @@ _STRINGS = (_RE_STRING_TRIPLE_DOUBLE,
_RE_STRING_DOUBLE,
_RE_STRING_SINGLE)
RE_BEGIN_STRING = LazyObject(
lambda: re.compile("(" + _RE_STRING_START + \
lambda: re.compile("(" + _RE_STRING_START +
'(' + "|".join(_STRINGS) + '))'),
globals(), 'RE_BEGIN_STRING')
"""Regular expression matching the start of a string, including quotes and
@ -1416,13 +1435,14 @@ def check_for_partial_string(x):
# source code (root/Lib/ntpath.py, line 353)
def _is_in_env(name):
ENV = builtins.__xonsh_env__
return name in ENV._d or name in ENV._defaults
env = builtins.__xonsh_env__
return name in env._d or name in env._defaults
def _get_env_string(name):
ENV = builtins.__xonsh_env__
value = ENV.get(name)
ensurer = ENV.get_ensurer(name)
env = builtins.__xonsh_env__
value = env.get(name)
ensurer = env.get_ensurer(name)
if ensurer.detype is bool_to_str:
value = ensure_string(value)
else:
@ -1432,12 +1452,12 @@ def _get_env_string(name):
def expandvars(path):
"""Expand shell variables of the forms $var, ${var} and %var%.
Unknown variables are left unchanged."""
ENV = builtins.__xonsh_env__
Unknown variables are left unchanged.
"""
env = builtins.__xonsh_env__
if isinstance(path, bytes):
path = path.decode(encoding=ENV.get('XONSH_ENCODING'),
errors=ENV.get('XONSH_ENCODING_ERRORS'))
path = path.decode(encoding=env.get('XONSH_ENCODING'),
errors=env.get('XONSH_ENCODING_ERRORS'))
elif isinstance(path, pathlib.Path):
# get the path's string representation
path = str(path)
@ -1531,6 +1551,7 @@ def expandvars(path):
# File handling tools
#
def backup_file(fname):
"""Moves an existing file to a new name that has the current time right
before the extension.
@ -1595,26 +1616,41 @@ def expand_case_matching(s):
return ''.join(t)
def globpath(s, ignore_case=False, return_empty=False):
def globpath(s, ignore_case=False, return_empty=False, sort_result=None):
"""Simple wrapper around glob that also expands home and env vars."""
o, s = _iglobpath(s, ignore_case=ignore_case)
o, s = _iglobpath(s, ignore_case=ignore_case, sort_result=sort_result)
o = list(o)
no_match = [] if return_empty else [s]
return o if len(o) != 0 else no_match
def _iglobpath(s, ignore_case=False):
def _iglobpath(s, ignore_case=False, sort_result=None):
s = builtins.__xonsh_expand_path__(s)
if sort_result is None:
sort_result = builtins.__xonsh_env__.get('GLOB_SORTED')
if ignore_case:
s = expand_case_matching(s)
if sys.version_info > (3, 5):
if '**' in s and '**/*' not in s:
s = s.replace('**', '**/*')
# `recursive` is only a 3.5+ kwarg.
return glob.iglob(s, recursive=True), s
if sort_result:
paths = glob.glob(s, recursive=True)
paths.sort()
paths = iter(paths)
else:
paths = glob.iglob(s, recursive=True)
return paths, s
else:
return glob.iglob(s), s
if sort_result:
paths = glob.glob(s)
paths.sort()
paths = iter(paths)
else:
paths = glob.iglob(s)
return paths, s
def iglobpath(s, ignore_case=False):
def iglobpath(s, ignore_case=False, sort_result=None):
"""Simple wrapper around iglob that also expands home and env vars."""
return _iglobpath(s, ignore_case)[0]
return _iglobpath(s, ignore_case=ignore_case, sort_result=sort_result)[0]

View file

@ -44,6 +44,17 @@
"package": "xonsh-pacman-tabcomplete",
"url": "https://github.com/gforsyth/xonsh-pacman-tabcomplete",
"description": ["Adds tabcomplete functionality to pacman 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": "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"]
}
],
"packages": {
@ -83,6 +94,20 @@
"install": {
"pip": "pip install xonsh-pacman-tabcomplete"
}
},
"xonsh-scrapy-tabcomplete": {
"license": "GPLv3",
"url": "https://github.com/Granitas/xonsh-scrapy-tabcomplete",
"install": {
"pip": "pip install xonsh-scrapy-tabcomplete"
}
},
"xonsh-autoxsh": {
"license": "GPLv3",
"url": "https://github.com/Granitas/xonsh-autoxsh",
"install": {
"pip": "pip install xonsh-autoxsh"
}
}
}
}