mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-06 17:30:59 +01:00
Merge branch 'master' of https://github.com/scopatz/xonsh
This commit is contained in:
commit
e0fe044859
25 changed files with 352 additions and 109 deletions
|
@ -474,7 +474,7 @@ result is automatically converted to a string. For example,
|
|||
4
|
||||
>>> echo @([42, 'yo'])
|
||||
42 yo
|
||||
>>> echo "hello" | @(lambda a, s=None: s.strip + " world")
|
||||
>>> echo "hello" | @(lambda a, s=None: s.strip() + " world\n")
|
||||
hello world
|
||||
|
||||
This syntax can be used inside of a captured or uncaptured subprocess, and can
|
||||
|
@ -1104,8 +1104,9 @@ must have one of the following two signatures
|
|||
return 0
|
||||
|
||||
|
||||
Adding and Removing Aliases
|
||||
---------------------------
|
||||
Adding, Modifying, and Removing Aliases
|
||||
---------------------------------------
|
||||
|
||||
We can dynamically alter the aliases present simply by modifying the
|
||||
built-in mapping. Here is an example using a function value:
|
||||
|
||||
|
@ -1117,6 +1118,23 @@ built-in mapping. Here is an example using a function value:
|
|||
>>> banana
|
||||
'My spoon is tooo big!'
|
||||
|
||||
|
||||
To redefine an alias, simply assign a new function, here using a python lambda
|
||||
with keyword arguments:
|
||||
|
||||
.. code-block:: xonshcon
|
||||
|
||||
>>> aliases['banana'] = lambda args,stdin=None: "Banana for scale.\n"
|
||||
>>> banana
|
||||
Banana for scale.
|
||||
|
||||
|
||||
Removing an alias is as easy as deleting the key from the alias dictionary:
|
||||
|
||||
.. code-block:: xonshcon
|
||||
|
||||
>>> del aliases['banana']
|
||||
|
||||
.. note::
|
||||
|
||||
Alias functions should generally be defined with a leading underscore.
|
||||
|
|
14
news/fix_get_color_index.rst
Normal file
14
news/fix_get_color_index.rst
Normal file
|
@ -0,0 +1,14 @@
|
|||
**Added:** None
|
||||
|
||||
**Changed:** None
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* Fix a startup problem on windows caused by a refactor of Prompt_toolkit.
|
||||
https://github.com/jonathanslenders/python-prompt-toolkit/commit/a9df2a2
|
||||
|
||||
**Security:** None
|
25
news/history-timestamp-filtering.rst
Normal file
25
news/history-timestamp-filtering.rst
Normal file
|
@ -0,0 +1,25 @@
|
|||
**Added:**
|
||||
|
||||
* ``history show`` args ``-t``, ``-f``, ``-T`` ``+T`` to filter commands by timestamp
|
||||
|
||||
* ``ensure_timestamp`` in xonsh.tools to try and convert an object to a timestamp a.k.a float
|
||||
|
||||
* ``$XONSH_DATETIME_FORMAT`` envvar, the default format to be used with ``datetime.datetime.strptime()``
|
||||
|
||||
**Changed:**
|
||||
|
||||
* ``_hist_parse_args`` implementation refactor
|
||||
|
||||
* moved all parameter checking in ``_hist_get``
|
||||
|
||||
* ``_hist_show`` to handle numeration and timestamp printing of commands
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* ``ensure_slice`` bugfix for -1 index/slice
|
||||
|
||||
**Security:** None
|
16
news/impacc.rst
Normal file
16
news/impacc.rst
Normal file
|
@ -0,0 +1,16 @@
|
|||
**Added:** None
|
||||
|
||||
**Changed:**
|
||||
|
||||
* ``imphooks`` now checks directory access rights.
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* Xonsh will no longer fail to start in directories where the user doesn't have
|
||||
read access.
|
||||
|
||||
**Security:** None
|
21
news/imphook.rst
Normal file
21
news/imphook.rst
Normal file
|
@ -0,0 +1,21 @@
|
|||
**Added:** None
|
||||
|
||||
**Changed:**
|
||||
|
||||
* ``xonsh.imphooks`` does not install the import hooks automatically, you now
|
||||
need to explicitly call the `install_hook()` method defined in this module.
|
||||
For example: ``from xonsh.imphooks import install_hook; install_hook()``. The
|
||||
``install_hook`` method can safely be called several times. If you need
|
||||
compatibility with previous versions of Xonsh you can use the following::
|
||||
|
||||
from xonsh import imphooks
|
||||
getattr(imphooks, 'install_hook', lambda:None)()
|
||||
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:** None
|
||||
|
||||
**Security:** None
|
13
news/lazy_union.rst
Normal file
13
news/lazy_union.rst
Normal file
|
@ -0,0 +1,13 @@
|
|||
**Added:** None
|
||||
|
||||
**Changed:** None
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* LazyObject supports set union
|
||||
|
||||
**Security:** None
|
13
news/pcrash.rst
Normal file
13
news/pcrash.rst
Normal file
|
@ -0,0 +1,13 @@
|
|||
**Added:** None
|
||||
|
||||
**Changed:** None
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* Made pip completer more robust to when pip is not installed.
|
||||
|
||||
**Security:** None
|
|
@ -1,6 +1,7 @@
|
|||
[pytest]
|
||||
flake8-max-line-length = 180
|
||||
flake8-ignore =
|
||||
*.py E122
|
||||
*.py E402
|
||||
tests/tools.py E128
|
||||
xonsh/ast.py F401
|
||||
|
|
|
@ -178,6 +178,10 @@ def test_parser_show(args, exp):
|
|||
'session': exp[1],
|
||||
'slices': exp[2],
|
||||
'numerate': exp[3],
|
||||
'reverse': exp[4]}
|
||||
'reverse': exp[4],
|
||||
'start_time': None,
|
||||
'end_time': None,
|
||||
'datetime_format': None,
|
||||
'timestamp': False}
|
||||
ns = _hist_parse_args(shlex.split(args))
|
||||
assert ns.__dict__ == exp_ns
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
"""Testing xonsh import hooks"""
|
||||
import pytest
|
||||
|
||||
from xonsh import imphooks # noqa
|
||||
from xonsh import built_ins
|
||||
from xonsh import imphooks
|
||||
from xonsh.environ import Env
|
||||
from xonsh.execer import Execer
|
||||
from xonsh.built_ins import load_builtins, unload_builtins
|
||||
import builtins
|
||||
|
||||
imphooks.install_hook()
|
||||
|
||||
|
||||
@pytest.yield_fixture(autouse=True)
|
||||
def imp_env(xonsh_execer):
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Tests xonsh tools."""
|
||||
import builtins
|
||||
import datetime as dt
|
||||
import os
|
||||
import pathlib
|
||||
from tempfile import TemporaryDirectory
|
||||
import stat
|
||||
import builtins
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -22,7 +23,8 @@ from xonsh.tools import (
|
|||
subexpr_from_unbalanced, subproc_toks, to_bool, to_bool_or_int,
|
||||
to_dynamic_cwd_tuple, to_logfile_opt, pathsep_to_set, set_to_pathsep,
|
||||
is_string_seq, pathsep_to_seq, seq_to_pathsep, is_nonstring_seq_of_strings,
|
||||
pathsep_to_upper_seq, seq_to_upper_pathsep, expandvars, is_int_as_str, is_slice_as_str
|
||||
pathsep_to_upper_seq, seq_to_upper_pathsep, expandvars, is_int_as_str, is_slice_as_str,
|
||||
ensure_timestamp,
|
||||
)
|
||||
from xonsh.commands_cache import CommandsCache
|
||||
from xonsh.built_ins import expand_path
|
||||
|
@ -816,6 +818,7 @@ def test_bool_or_int_to_str(inp, exp):
|
|||
(42, slice(42, 43)),
|
||||
(None, slice(None, None, None)),
|
||||
(slice(1,2), slice(1,2)),
|
||||
('-1', slice(-1, None, None)),
|
||||
('42', slice(42, 43)),
|
||||
('-42', slice(-42, -41)),
|
||||
('1:2:3', slice(1, 2, 3)),
|
||||
|
@ -1120,3 +1123,16 @@ def test_expandvars(inp, exp, xonsh_builtins):
|
|||
env = Env({'foo':'bar', 'spam': 'eggs', 'a_bool': True, 'an_int': 42, 'none': None})
|
||||
xonsh_builtins.__xonsh_env__ = env
|
||||
assert expandvars(inp) == exp
|
||||
|
||||
|
||||
@pytest.mark.parametrize('inp, fmt, exp',[
|
||||
(572392800.0, None, 572392800.0),
|
||||
('42.1459', None, 42.1459),
|
||||
(dt.datetime(2016, 8, 2, 13, 24), None, dt.datetime(2016, 8, 2, 13, 24).timestamp()),
|
||||
('2016-8-10 16:14', None, dt.datetime(2016, 8, 10, 16, 14).timestamp()),
|
||||
('2016/8/10 16:14:40', '%Y/%m/%d %H:%M:%S', dt.datetime(2016, 8, 10, 16, 14, 40).timestamp()),
|
||||
])
|
||||
def test_ensure_timestamp(inp, fmt, exp, xonsh_builtins):
|
||||
xonsh_builtins.__xonsh_env__['XONSH_DATETIME_FORMAT'] = '%Y-%m-%d %H:%M'
|
||||
obs = ensure_timestamp(inp, fmt)
|
||||
assert exp == obs
|
||||
|
|
|
@ -53,4 +53,16 @@ _foobar = 3
|
|||
""")
|
||||
|
||||
ctx = xontrib_context('spameggs')
|
||||
assert ctx == {'spam': 1, '_foobar': 3}
|
||||
assert ctx == {'spam': 1, '_foobar': 3}
|
||||
|
||||
def test_xshxontrib(tmpmod):
|
||||
"""
|
||||
Test that .xsh xontribs are loadable
|
||||
"""
|
||||
with tmpmod.mkdir("xontrib").join("script.xsh").open('w') as x:
|
||||
x.write("""
|
||||
hello = 'world'
|
||||
""")
|
||||
|
||||
ctx = xontrib_context('script')
|
||||
assert ctx == {'hello': 'world'}
|
||||
|
|
|
@ -11,8 +11,11 @@ PIP_LIST_RE = xl.LazyObject(lambda: re.compile("pip(?:\d|\.)* (?:uninstall|show)
|
|||
|
||||
@xl.lazyobject
|
||||
def ALL_COMMANDS():
|
||||
help_text = str(subprocess.check_output(['pip', '--help'],
|
||||
stderr=subprocess.DEVNULL))
|
||||
try:
|
||||
help_text = str(subprocess.check_output(['pip', '--help'],
|
||||
stderr=subprocess.DEVNULL))
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
commands = re.findall(" (\w+) ", help_text)
|
||||
return [c for c in commands if c not in ['completion', 'help']]
|
||||
|
||||
|
@ -24,7 +27,11 @@ def complete_pip(prefix, line, begidx, endidx, ctx):
|
|||
(not PIP_RE.search(line)):
|
||||
return
|
||||
if PIP_LIST_RE.search(line):
|
||||
items = subprocess.check_output(['pip', 'list'], stderr=subprocess.DEVNULL)
|
||||
try:
|
||||
items = subprocess.check_output(['pip', 'list'],
|
||||
stderr=subprocess.DEVNULL)
|
||||
except FileNotFoundError:
|
||||
return set()
|
||||
items = items.decode('utf-8').splitlines()
|
||||
return set(i.split()[0] for i in items)
|
||||
|
||||
|
|
|
@ -89,7 +89,10 @@ Ensurer.__doc__ = """Named tuples whose elements are functions that
|
|||
represent environment variable validation, conversion, detyping.
|
||||
"""
|
||||
|
||||
DEFAULT_ENSURERS = LazyObject(lambda: {
|
||||
|
||||
@lazyobject
|
||||
def DEFAULT_ENSURERS():
|
||||
return {
|
||||
'AUTO_CD': (is_bool, to_bool, bool_to_str),
|
||||
'AUTO_PUSHD': (is_bool, to_bool, bool_to_str),
|
||||
'AUTO_SUGGEST': (is_bool, to_bool, bool_to_str),
|
||||
|
@ -148,9 +151,9 @@ DEFAULT_ENSURERS = LazyObject(lambda: {
|
|||
'XONSH_SHOW_TRACEBACK': (is_bool, to_bool, bool_to_str),
|
||||
'XONSH_STORE_STDOUT': (is_bool, to_bool, bool_to_str),
|
||||
'XONSH_STORE_STDIN': (is_bool, to_bool, bool_to_str),
|
||||
'XONSH_TRACEBACK_LOGFILE': (is_logfile_opt, to_logfile_opt,
|
||||
logfile_opt_to_str)
|
||||
}, globals(), 'DEFAULT_ENSURERS')
|
||||
'XONSH_TRACEBACK_LOGFILE': (is_logfile_opt, to_logfile_opt, logfile_opt_to_str),
|
||||
'XONSH_DATETIME_FORMAT': (is_string, ensure_string, ensure_string),
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
|
@ -303,7 +306,8 @@ def DEFAULT_VALUES():
|
|||
'XONSH_SHOW_TRACEBACK': False,
|
||||
'XONSH_STORE_STDIN': False,
|
||||
'XONSH_STORE_STDOUT': False,
|
||||
'XONSH_TRACEBACK_LOGFILE': None
|
||||
'XONSH_TRACEBACK_LOGFILE': None,
|
||||
'XONSH_DATETIME_FORMAT': '%Y-%m-%d %H:%M',
|
||||
}
|
||||
if hasattr(locale, 'LC_MESSAGES'):
|
||||
dv['LC_MESSAGES'] = locale.setlocale(locale.LC_MESSAGES)
|
||||
|
@ -333,8 +337,11 @@ store_as_str : bool, optional
|
|||
# iterates from back
|
||||
VarDocs.__new__.__defaults__ = (True, DefaultNotGiven, False)
|
||||
|
||||
|
||||
# Please keep the following in alphabetic order - scopatz
|
||||
DEFAULT_DOCS = LazyObject(lambda: {
|
||||
@lazyobject
|
||||
def DEFAULT_DOCS():
|
||||
return {
|
||||
'ANSICON': VarDocs('This is used on Windows to set the title, '
|
||||
'if available.', configurable=False),
|
||||
'AUTO_CD': VarDocs(
|
||||
|
@ -637,7 +644,10 @@ DEFAULT_DOCS = LazyObject(lambda: {
|
|||
'XONSH_SHOW_TRACEBACK has been set. Its value must be a writable file '
|
||||
'or None / the empty string if traceback logging is not desired. '
|
||||
'Logging to a file is not enabled by default.'),
|
||||
}, globals(), 'DEFAULT_DOCS')
|
||||
'XONSH_DATETIME_FORMAT': VarDocs(
|
||||
'The format that is used for ``datetime.strptime()`` in various places'
|
||||
'i.e the history timestamp option'),
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
|
|
100
xonsh/history.py
100
xonsh/history.py
|
@ -18,7 +18,7 @@ import collections.abc as abc
|
|||
from xonsh.lazyasd import lazyobject
|
||||
from xonsh.lazyjson import LazyJSON, ljdump, LJNode
|
||||
from xonsh.tools import (ensure_slice, to_history_tuple,
|
||||
expanduser_abs_path)
|
||||
expanduser_abs_path, ensure_timestamp)
|
||||
from xonsh.diff_history import _dh_create_parser, _dh_main_action
|
||||
|
||||
|
||||
|
@ -343,14 +343,23 @@ 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', help='displays session history, default action')
|
||||
show = subp.add_parser('show', prefix_chars='-+',
|
||||
help='displays session history, default action')
|
||||
show.add_argument('-r', dest='reverse', default=False,
|
||||
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('-t', dest='timestamp', default=False,
|
||||
action='store_true', help='show command timestamps')
|
||||
show.add_argument('-T', dest='end_time', default=None,
|
||||
help='show only commands before timestamp')
|
||||
show.add_argument('+T', dest='start_time', default=None,
|
||||
help='show only commands after timestamp')
|
||||
show.add_argument('-f', dest='datetime_format', default=None,
|
||||
help='the datetime format to be used for filtering and printing')
|
||||
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=[],
|
||||
show.add_argument('slices', nargs='*', default=None,
|
||||
help='display history entries or range of entries')
|
||||
# 'id' subcommand
|
||||
subp.add_parser('id', help='displays the current session id')
|
||||
|
@ -388,7 +397,7 @@ def _hist_create_parser():
|
|||
def _hist_get_portion(commands, slices):
|
||||
"""Yield from portions of history commands."""
|
||||
if len(slices) == 1:
|
||||
s = ensure_slice(slices[0])
|
||||
s = slices[0]
|
||||
try:
|
||||
yield from itertools.islice(commands, s.start, s.stop, s.step)
|
||||
return
|
||||
|
@ -396,26 +405,17 @@ def _hist_get_portion(commands, slices):
|
|||
pass
|
||||
commands = list(commands)
|
||||
for s in slices:
|
||||
s = ensure_slice(s)
|
||||
yield from commands[s]
|
||||
|
||||
|
||||
def _hist_filter_ts(commands, start_time=None, end_time=None):
|
||||
def _hist_filter_ts(commands, start_time, end_time):
|
||||
"""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
|
||||
|
||||
|
||||
def _hist_get(session='session', slices=None,
|
||||
def _hist_get(session='session', *, slices=None, datetime_format=None,
|
||||
start_time=None, end_time=None, location=None):
|
||||
"""Get the requested portion of shell history.
|
||||
|
||||
|
@ -437,8 +437,18 @@ def _hist_get(session='session', slices=None,
|
|||
"""
|
||||
cmds = _HIST_SESSIONS[session](location=location)
|
||||
if slices:
|
||||
# transform/check all slices
|
||||
slices = [ensure_slice(s) for s in slices]
|
||||
cmds = _hist_get_portion(cmds, slices)
|
||||
if start_time or end_time:
|
||||
if start_time is None:
|
||||
start_time = 0.0
|
||||
else:
|
||||
start_time = ensure_timestamp(start_time, datetime_format)
|
||||
if end_time is None:
|
||||
end_time = float('inf')
|
||||
else:
|
||||
end_time = ensure_timestamp(end_time, datetime_format)
|
||||
cmds = _hist_filter_ts(cmds, start_time, end_time)
|
||||
return cmds
|
||||
|
||||
|
@ -447,18 +457,31 @@ 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:
|
||||
if ns.reverse:
|
||||
commands = reversed(list(commands))
|
||||
if not ns.numerate:
|
||||
for c, _, _ in commands:
|
||||
print(c)
|
||||
else:
|
||||
for c, _, i in commands:
|
||||
print('{}: {}'.format(i, c))
|
||||
commands = _hist_get(ns.session,
|
||||
slices=ns.slices,
|
||||
start_time=ns.start_time,
|
||||
end_time=ns.end_time,
|
||||
datetime_format=ns.datetime_format)
|
||||
except ValueError as err:
|
||||
print("history: error: {}".format(err), file=sys.stderr)
|
||||
return
|
||||
if ns.reverse:
|
||||
commands = reversed(list(commands))
|
||||
if not ns.numerate and not ns.timestamp:
|
||||
for c, _, _ in commands:
|
||||
print(c)
|
||||
elif not ns.timestamp:
|
||||
for c, _, i in commands:
|
||||
print('{}: {}'.format(i, c))
|
||||
elif not ns.numerate:
|
||||
for c, ts, _ in commands:
|
||||
dt = datetime.datetime.fromtimestamp(ts).ctime()
|
||||
print('({}) {}'.format(dt, c))
|
||||
else:
|
||||
for c, ts, i in commands:
|
||||
dt = datetime.datetime.fromtimestamp(ts).ctime()
|
||||
print('{}:({}) {}'.format(i, dt, c))
|
||||
|
||||
|
||||
# Interface to History
|
||||
|
@ -640,23 +663,28 @@ def _HIST_MAIN_ACTIONS():
|
|||
|
||||
|
||||
def _hist_parse_args(args):
|
||||
"""Parse arguments using the history argument parser."""
|
||||
"""Prepare and parse arguments for the history command.
|
||||
|
||||
Add default action for ``history`` and
|
||||
default session for ``history show``.
|
||||
"""
|
||||
parser = _hist_create_parser()
|
||||
if not args:
|
||||
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)
|
||||
if args[0] == 'show':
|
||||
if not any(a in _HIST_SESSIONS for a in args):
|
||||
args.insert(1, 'session')
|
||||
ns, slices = parser.parse_known_args(args)
|
||||
if slices:
|
||||
if not ns.slices:
|
||||
ns.slices = slices
|
||||
else:
|
||||
ns.slices.extend(slices)
|
||||
else:
|
||||
ns = parser.parse_args(args)
|
||||
return ns
|
||||
|
||||
|
||||
def history_main(args=None, stdin=None):
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
This module registers the hooks it defines when it is imported.
|
||||
"""
|
||||
import builtins
|
||||
from importlib.abc import MetaPathFinder, SourceLoader
|
||||
from importlib.machinery import ModuleSpec
|
||||
import os
|
||||
import sys
|
||||
import builtins
|
||||
from importlib.machinery import ModuleSpec
|
||||
from importlib.abc import MetaPathFinder, SourceLoader
|
||||
|
||||
from xonsh.execer import Execer
|
||||
from xonsh.platform import scandir
|
||||
|
@ -48,7 +48,7 @@ class XonshImportHook(MetaPathFinder, SourceLoader):
|
|||
for p in path:
|
||||
if not isinstance(p, str):
|
||||
continue
|
||||
if not os.path.isdir(p):
|
||||
if not os.path.isdir(p) or not os.access(p, os.R_OK):
|
||||
continue
|
||||
if fname not in (x.name for x in scandir(p)):
|
||||
continue
|
||||
|
@ -84,4 +84,14 @@ class XonshImportHook(MetaPathFinder, SourceLoader):
|
|||
return code
|
||||
|
||||
|
||||
sys.meta_path.append(XonshImportHook())
|
||||
def install_hook():
|
||||
"""
|
||||
Install Xonsh import hook in `sys.metapath` in order for `.xsh` files to be
|
||||
importable.
|
||||
|
||||
Can safely be called many times, will be no-op if a xonsh import hook is
|
||||
already present.
|
||||
"""
|
||||
|
||||
if XonshImportHook not in {type(hook) for hook in sys.meta_path}:
|
||||
sys.meta_path.append(XonshImportHook())
|
||||
|
|
|
@ -112,6 +112,10 @@ class LazyObject(object):
|
|||
obj = self._lazy_obj()
|
||||
return hash(obj)
|
||||
|
||||
def __or__(self, other):
|
||||
obj = self._lazy_obj()
|
||||
return obj | other
|
||||
|
||||
|
||||
def lazyobject(f):
|
||||
"""Decorator for constructing lazy objects from a function."""
|
||||
|
|
|
@ -18,6 +18,7 @@ from xonsh.platform import HAS_PYGMENTS, ON_WINDOWS
|
|||
from xonsh.codecache import run_script_with_cache, run_code_with_cache
|
||||
from xonsh.xonfig import xonfig_main
|
||||
from xonsh.lazyimps import pygments, pyghooks
|
||||
from xonsh.imphooks import install_hook
|
||||
|
||||
|
||||
def get_setproctitle():
|
||||
|
@ -188,6 +189,7 @@ def premain(argv=None):
|
|||
args.mode = XonshMode.interactive
|
||||
shell_kwargs['completer'] = True
|
||||
shell_kwargs['login'] = True
|
||||
install_hook()
|
||||
builtins.__xonsh_shell__ = Shell(**shell_kwargs)
|
||||
env = builtins.__xonsh_env__
|
||||
env['XONSH_LOGIN'] = shell_kwargs['login']
|
||||
|
|
|
@ -11,11 +11,10 @@ from xonsh.aliases import xonsh_exit
|
|||
from xonsh.tools import ON_WINDOWS
|
||||
|
||||
env = builtins.__xonsh_env__
|
||||
indent_ = env.get('INDENT')
|
||||
DEDENT_TOKENS = frozenset(['raise', 'return', 'pass', 'break', 'continue'])
|
||||
|
||||
|
||||
def carriage_return(b, cli):
|
||||
def carriage_return(b, cli, *, autoindent=True):
|
||||
"""
|
||||
Preliminary parser to determine if 'Enter' key should send command to
|
||||
the xonsh parser for execution or should insert a newline for continued
|
||||
|
@ -29,35 +28,37 @@ def carriage_return(b, cli):
|
|||
- Any text exists below cursor position (relevant when editing previous
|
||||
multiline blocks)
|
||||
"""
|
||||
doc = b.document
|
||||
at_end_of_line = _is_blank(doc.current_line_after_cursor)
|
||||
current_line_blank = _is_blank(doc.current_line)
|
||||
|
||||
at_end_of_line = _is_blank(b.document.current_line_after_cursor)
|
||||
current_line_blank = _is_blank(b.document.current_line)
|
||||
indent = env.get('INDENT') if autoindent else ''
|
||||
|
||||
# indent after a colon
|
||||
if (b.document.current_line_before_cursor.strip().endswith(':') and
|
||||
if (doc.current_line_before_cursor.strip().endswith(':') and
|
||||
at_end_of_line):
|
||||
b.newline()
|
||||
b.insert_text(indent_, fire_event=False)
|
||||
b.newline(copy_margin=autoindent)
|
||||
b.insert_text(indent, fire_event=False)
|
||||
# if current line isn't blank, check dedent tokens
|
||||
elif (not current_line_blank and
|
||||
b.document.current_line.split(maxsplit=1)[0] in DEDENT_TOKENS and
|
||||
b.document.line_count > 1):
|
||||
b.newline(copy_margin=True)
|
||||
_ = b.delete_before_cursor(count=len(indent_))
|
||||
elif (not b.document.on_first_line and
|
||||
doc.current_line.split(maxsplit=1)[0] in DEDENT_TOKENS and
|
||||
doc.line_count > 1):
|
||||
b.newline(copy_margin=autoindent)
|
||||
b.delete_before_cursor(count=len(indent))
|
||||
elif (not doc.on_first_line and
|
||||
not current_line_blank):
|
||||
b.newline(copy_margin=True)
|
||||
elif (b.document.char_before_cursor == '\\' and
|
||||
b.newline(copy_margin=autoindent)
|
||||
elif (doc.char_before_cursor == '\\' and
|
||||
not (not builtins.__xonsh_env__.get('FORCE_POSIX_PATHS')
|
||||
and ON_WINDOWS)):
|
||||
b.newline()
|
||||
elif (b.document.find_next_word_beginning() is not None and
|
||||
b.newline(copy_margin=autoindent)
|
||||
elif (doc.find_next_word_beginning() is not None and
|
||||
(any(not _is_blank(i)
|
||||
for i
|
||||
in b.document.lines_from_current[1:]))):
|
||||
b.newline(copy_margin=True)
|
||||
elif not current_line_blank and not can_compile(b.document.text):
|
||||
b.newline()
|
||||
in doc.lines_from_current[1:]))):
|
||||
b.newline(copy_margin=autoindent)
|
||||
elif not current_line_blank and not can_compile(doc.text):
|
||||
b.newline(copy_margin=autoindent)
|
||||
else:
|
||||
b.accept_action.validate_and_handle(cli, b)
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@ Implementations:
|
|||
import builtins
|
||||
import collections
|
||||
import collections.abc as abc
|
||||
import contextlib
|
||||
import ctypes
|
||||
import datetime
|
||||
import functools
|
||||
import glob
|
||||
import os
|
||||
|
@ -31,7 +33,6 @@ import sys
|
|||
import threading
|
||||
import traceback
|
||||
import warnings
|
||||
import contextlib
|
||||
|
||||
# adding further imports from xonsh modules is discouraged to avoid circular
|
||||
# dependencies
|
||||
|
@ -933,7 +934,10 @@ def ensure_slice(x):
|
|||
return x
|
||||
try:
|
||||
x = int(x)
|
||||
s = slice(x, x+1)
|
||||
if x != -1:
|
||||
s = slice(x, x+1)
|
||||
else:
|
||||
s = slice(-1, None, None)
|
||||
except ValueError:
|
||||
x = x.strip('[]()')
|
||||
m = SLICE_REG.fullmatch(x)
|
||||
|
@ -1271,7 +1275,10 @@ def _get_color_indexes(style_map):
|
|||
for token in style_map:
|
||||
attr = pt_style.token_to_attrs[token]
|
||||
if attr.color is not None:
|
||||
index = table.lookup_color(attr.color, attr.bgcolor)
|
||||
try:
|
||||
index = table.lookup_color(attr.color, attr.bgcolor)
|
||||
except AttributeError:
|
||||
index = table.lookup_fg_color(attr.color)
|
||||
try:
|
||||
rgb = (int(attr.color[0:2], 16),
|
||||
int(attr.color[2:4], 16),
|
||||
|
@ -1585,3 +1592,19 @@ def _iglobpath(s, ignore_case=False, sort_result=None):
|
|||
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=ignore_case, sort_result=sort_result)[0]
|
||||
|
||||
|
||||
def ensure_timestamp(t, datetime_format=None):
|
||||
if isinstance(t, (int, float)):
|
||||
return t
|
||||
try:
|
||||
return float(t)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if datetime_format is None:
|
||||
datetime_format = builtins.__xonsh_env__['XONSH_DATETIME_FORMAT']
|
||||
if isinstance(t, datetime.datetime):
|
||||
t = t.timestamp()
|
||||
else:
|
||||
t = datetime.datetime.strptime(t, datetime_format).timestamp()
|
||||
return t
|
||||
|
|
|
@ -281,8 +281,10 @@ def make_xonfig_wizard(default_file=None, confirm=False):
|
|||
])
|
||||
if confirm:
|
||||
q = ("Would you like to run the xonsh configuration wizard now?\n\n"
|
||||
"1. Yes\n2. No, but ask me later.\n3. No, and don't ask me again."
|
||||
"\n\n1, 2, or 3 [default: 2]? ")
|
||||
"1. Yes (You can abort at any time)\n"
|
||||
"2. No, but ask me next time.\n"
|
||||
"3. No, and don't ask me again.\n\n"
|
||||
"1, 2, or 3 [default: 2]? ")
|
||||
passer = wiz.Pass()
|
||||
saver = wiz.Save(check=False, ask_filename=False,
|
||||
default_file=default_file)
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
"""Hooks for the distributed parallel computing library."""
|
||||
from xonsh.contexts import Functor
|
||||
|
||||
__all__ = 'DSubmitter', 'dsubmit'
|
||||
|
||||
|
||||
def dworker(args, stdin=None):
|
||||
"""Programatic access to the dworker utility, to allow launching
|
||||
workers that also have access to xonsh builtins.
|
||||
"""
|
||||
from distributed.cli import dworker as _dworker
|
||||
_dworker.main.main(args=args, prog_name='dworker', standalone_mode=False)
|
||||
from distributed.cli import dworker
|
||||
dworker.main.main(args=args, prog_name='dworker', standalone_mode=False)
|
||||
|
||||
|
||||
aliases['dworker'] = dworker
|
||||
|
@ -67,7 +69,3 @@ def dsubmit(*a, args=(), kwargs=None, rtn='', **kw):
|
|||
e = Executor(*a, **kw)
|
||||
dsub = DSubmitter(e, args=args, kwargs=kwargs, rtn=rtn)
|
||||
return dsub
|
||||
|
||||
|
||||
# some cleanup
|
||||
del dworker, Functor
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
"""Matplotlib xontribution."""
|
||||
|
||||
from xonsh.proc import foreground as _foreground
|
||||
from xonsh.proc import foreground as foreground
|
||||
|
||||
__all__ = ()
|
||||
|
||||
|
||||
@_foreground
|
||||
def _mpl(args, stdin=None):
|
||||
@foreground
|
||||
def mpl(args, stdin=None):
|
||||
"""Hooks to matplotlib"""
|
||||
from xontrib.mplhooks import show
|
||||
show()
|
||||
|
||||
|
||||
aliases['mpl'] = _mpl
|
||||
aliases['mpl'] = mpl
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
"""Python virtual environment manager for xonsh."""
|
||||
|
||||
import sys as _sys
|
||||
import xontrib.voxapi as _voxapi
|
||||
import xonsh.lazyasd as _lazyasd
|
||||
import sys
|
||||
import xontrib.voxapi as voxapi
|
||||
import xonsh.lazyasd as lazyasd
|
||||
|
||||
__all__ = ()
|
||||
|
||||
|
||||
class _VoxHandler:
|
||||
class VoxHandler:
|
||||
"""Vox is a virtual environment manager for xonsh."""
|
||||
|
||||
def parser():
|
||||
|
@ -57,7 +59,7 @@ class _VoxHandler:
|
|||
subparsers.add_parser('help', help='Show this help message')
|
||||
return parser
|
||||
|
||||
parser = _lazyasd.LazyObject(parser, locals(), 'parser')
|
||||
parser = lazyasd.LazyObject(parser, locals(), 'parser')
|
||||
|
||||
aliases = {
|
||||
'create': 'new',
|
||||
|
@ -71,7 +73,7 @@ class _VoxHandler:
|
|||
}
|
||||
|
||||
def __init__(self):
|
||||
self.vox = _voxapi.Vox()
|
||||
self.vox = voxapi.Vox()
|
||||
|
||||
def __call__(self, args, stdin=None):
|
||||
"""Call the right handler method for a given command."""
|
||||
|
@ -98,7 +100,7 @@ class _VoxHandler:
|
|||
try:
|
||||
self.vox.activate(args.name)
|
||||
except KeyError:
|
||||
print('This environment doesn\'t exist. Create it with "vox new %s".\n' % name, file=_sys.stderr)
|
||||
print('This environment doesn\'t exist. Create it with "vox new %s".\n' % name, file=sys.stderr)
|
||||
return None
|
||||
else:
|
||||
print('Activated "%s".\n' % args.name)
|
||||
|
@ -107,7 +109,7 @@ class _VoxHandler:
|
|||
"""Deactive the active virtual environment."""
|
||||
|
||||
if self.vox.active() is None:
|
||||
print('No environment currently active. Activate one with "vox activate".\n', file=_sys.stderr)
|
||||
print('No environment currently active. Activate one with "vox activate".\n', file=sys.stderr)
|
||||
return None
|
||||
env_name = self.vox.deactivate()
|
||||
print('Deactivated "%s".\n' % env_name)
|
||||
|
@ -122,7 +124,7 @@ class _VoxHandler:
|
|||
return None
|
||||
|
||||
if not envs:
|
||||
print('No environments available. Create one with "vox new".\n', file=_sys.stderr)
|
||||
print('No environments available. Create one with "vox new".\n', file=sys.stderr)
|
||||
return None
|
||||
|
||||
print('Available environments:')
|
||||
|
@ -134,9 +136,9 @@ class _VoxHandler:
|
|||
for name in args.names:
|
||||
try:
|
||||
del self.vox[name]
|
||||
except _voxapi.EnvironmentInUse:
|
||||
except voxapi.EnvironmentInUse:
|
||||
print('The "%s" environment is currently active. In order to remove it, deactivate it first with "vox deactivate %s".\n' % (name, name),
|
||||
file=_sys.stderr)
|
||||
file=sys.stderr)
|
||||
return
|
||||
else:
|
||||
print('Environment "%s" removed.' % name)
|
||||
|
@ -152,4 +154,4 @@ class _VoxHandler:
|
|||
return vox(args, stdin=stdin)
|
||||
|
||||
|
||||
aliases['vox'] = _VoxHandler.handle
|
||||
aliases['vox'] = VoxHandler.handle
|
||||
|
|
|
@ -7,6 +7,7 @@ import collections.abc
|
|||
|
||||
from xonsh.platform import ON_POSIX, ON_WINDOWS, scandir
|
||||
|
||||
|
||||
VirtualEnvironment = collections.namedtuple('VirtualEnvironment', ['env', 'bin'])
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue