diff --git a/.gitignore b/.gitignore index a9e760802..49f28fd11 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ Actually_test.tst Actually.tst Complete_test.tst Complete.tst +tags diff --git a/docs/api/index.rst b/docs/api/index.rst index 5f06b6396..fc62625c4 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -54,6 +54,7 @@ For those of you who want the gritty details. events tools platform + jsonutils lazyjson lazyasd openpy diff --git a/docs/api/jsonutils.py b/docs/api/jsonutils.py new file mode 100644 index 000000000..7ae55cce2 --- /dev/null +++ b/docs/api/jsonutils.py @@ -0,0 +1,10 @@ +.. _xonsh_jsonutils: + +****************************************** +JSON Utilities (``xonsh.jsonutils``) +****************************************** + +.. automodule:: xonsh.jsonutils + :members: + :undoc-members: + diff --git a/news/ao.rst b/news/ao.rst new file mode 100644 index 000000000..62d19495b --- /dev/null +++ b/news/ao.rst @@ -0,0 +1,16 @@ +**Added:** None + +**Changed:** + +* The literal tokens ``and`` and ``or`` must be surrounded by + whitespace to delimit subprocess mode. If they do not have + whitespace on both sides in subproc mode, they are condisered + to be part of a command argument. + +**Deprecated:** None + +**Removed:** None + +**Fixed:** None + +**Security:** None diff --git a/news/fix_history_replay.rst b/news/fix_history_replay.rst new file mode 100644 index 000000000..6497676d2 --- /dev/null +++ b/news/fix_history_replay.rst @@ -0,0 +1,14 @@ +**Added:** None + +**Changed:** None + +**Deprecated:** None + +**Removed:** None + +**Fixed:** + +* ``history replay`` no longer barfs on ``style_name`` when setting up the + environment + +**Security:** None diff --git a/news/jsonutils.rst b/news/jsonutils.rst new file mode 100644 index 000000000..76c2141a5 --- /dev/null +++ b/news/jsonutils.rst @@ -0,0 +1,16 @@ +**Added:** + +* New ``jsonutils`` module available for serializing special + xonsh objects to JSON. + +**Changed:** None + +**Deprecated:** None + +**Removed:** None + +**Fixed:** + +* Wizard is now able to properly serialize envrionment paths. + +**Security:** None diff --git a/news/ntenv.rst b/news/ntenv.rst new file mode 100644 index 000000000..e66b0815b --- /dev/null +++ b/news/ntenv.rst @@ -0,0 +1,16 @@ +**Added:** None + +**Changed:** None + +**Deprecated:** None + +**Removed:** None + +**Fixed:** + +* On Windows, ``os.environ`` is case insensitive. This would potentially + change the case of envrionment variables set into the environment. + Xonsh now uses ``nt.envrion``, the case sensitive counterpart, to avoid + these issues on Windows. + +**Security:** None diff --git a/news/ptkcomp.rst b/news/ptkcomp.rst new file mode 100644 index 000000000..bbbde3b69 --- /dev/null +++ b/news/ptkcomp.rst @@ -0,0 +1,14 @@ +**Added:** None + +**Changed:** None + +**Deprecated:** None + +**Removed:** None + +**Fixed:** + +* PTK completions will now correctly deduplicate autosuggest completions + and display completions values based on the cursor position. + +**Security:** None diff --git a/tests/conftest.py b/tests/conftest.py index faf03b5cf..b5f223db0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,6 +43,7 @@ def xonsh_events(): @pytest.yield_fixture def xonsh_builtins(xonsh_events): """Mock out most of the builtins xonsh attributes.""" + old_builtins = set(dir(builtins)) builtins.__xonsh_env__ = DummyEnv() if ON_WINDOWS: builtins.__xonsh_env__['PATHEXT'] = ['.EXE', '.BAT', '.CMD'] @@ -72,40 +73,8 @@ def xonsh_builtins(xonsh_events): # be firing events on the global instance. builtins.events = xonsh_events yield builtins - if hasattr(builtins, '__xonsh_env__'): - del builtins.__xonsh_env__ - if hasattr(builtins, '__xonsh_ctx__'): - del builtins.__xonsh_ctx__ - del builtins.__xonsh_shell__ - if hasattr(builtins, '__xonsh_help__'): - del builtins.__xonsh_help__ - if hasattr(builtins, '__xonsh_glob__'): - del builtins.__xonsh_glob__ - if hasattr(builtins, '__xonsh_exit__'): - del builtins.__xonsh_exit__ - if hasattr(builtins, '__xonsh_superhelp__'): - del builtins.__xonsh_superhelp__ - del builtins.__xonsh_regexpath__ - if hasattr(builtins, '__xonsh_expand_path__'): - del builtins.__xonsh_expand_path__ - if hasattr(builtins, '__xonsh_stdout_uncaptured__'): - del builtins.__xonsh_stdout_uncaptured__ - if hasattr(builtins, '__xonsh_stderr_uncaptured__'): - del builtins.__xonsh_stderr_uncaptured__ - del builtins.__xonsh_subproc_captured__ - if hasattr(builtins, '__xonsh_subproc_uncaptured__'): - del builtins.__xonsh_subproc_uncaptured__ - del builtins.__xonsh_ensure_list_of_strs__ - del builtins.__xonsh_commands_cache__ - del builtins.__xonsh_all_jobs__ - if hasattr(builtins, '__xonsh_history__'): - del builtins.__xonsh_history__ - del builtins.__xonsh_enter_macro__ - del builtins.evalx - del builtins.execx - del builtins.compilex - del builtins.aliases - del builtins.events + for attr in set(dir(builtins)) - old_builtins: + delattr(builtins, attr) tasks.clear() # must to this to enable resetting all_jobs diff --git a/tests/test_jsonutils.py b/tests/test_jsonutils.py new file mode 100644 index 000000000..f1a8852f7 --- /dev/null +++ b/tests/test_jsonutils.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +"""Testing xonsh json hooks""" +import json + +import pytest + +from xonsh.tools import EnvPath +from xonsh.jsonutils import serialize_xonsh_json + + + +@pytest.mark.parametrize('inp', [ + 42, "yo", ["hello"], {"x": 65}, + EnvPath(["wakka", "jawaka"]), + ["y", EnvPath(["wakka", "jawaka"])], + {"z": EnvPath(["wakka", "jawaka"])}, +]) +def test_serialize_xonsh_json_roundtrip(inp): + s = json.dumps(inp, default=serialize_xonsh_json) + obs = json.loads(s) + assert inp == obs \ No newline at end of file diff --git a/tests/test_lexer.py b/tests/test_lexer.py index 8ac794eff..1758ba151 100644 --- a/tests/test_lexer.py +++ b/tests/test_lexer.py @@ -169,6 +169,82 @@ def test_ampersand(): assert check_token('&', ['AMPERSAND', '&', 0]) +def test_not_really_and_pre(): + inp = "![foo-and]" + exp = [ + ('BANG_LBRACKET', '![', 0), + ('NAME', 'foo', 2), + ('MINUS', '-', 5), + ('NAME', 'and', 6), + ('RBRACKET', ']', 9), + ] + assert check_tokens(inp, exp) + + +def test_not_really_and_post(): + inp = "![and-bar]" + exp = [ + ('BANG_LBRACKET', '![', 0), + ('NAME', 'and', 2), + ('MINUS', '-', 5), + ('NAME', 'bar', 6), + ('RBRACKET', ']', 9), + ] + assert check_tokens(inp, exp) + + +def test_not_really_and_pre_post(): + inp = "![foo-and-bar]" + exp = [ + ('BANG_LBRACKET', '![', 0), + ('NAME', 'foo', 2), + ('MINUS', '-', 5), + ('NAME', 'and', 6), + ('MINUS', '-', 9), + ('NAME', 'bar', 10), + ('RBRACKET', ']', 13), + ] + assert check_tokens(inp, exp) + + +def test_not_really_or_pre(): + inp = "![foo-or]" + exp = [ + ('BANG_LBRACKET', '![', 0), + ('NAME', 'foo', 2), + ('MINUS', '-', 5), + ('NAME', 'or', 6), + ('RBRACKET', ']', 8), + ] + assert check_tokens(inp, exp) + + +def test_not_really_or_post(): + inp = "![or-bar]" + exp = [ + ('BANG_LBRACKET', '![', 0), + ('NAME', 'or', 2), + ('MINUS', '-', 4), + ('NAME', 'bar', 5), + ('RBRACKET', ']', 8), + ] + assert check_tokens(inp, exp) + + +def test_not_really_or_pre_post(): + inp = "![foo-or-bar]" + exp = [ + ('BANG_LBRACKET', '![', 0), + ('NAME', 'foo', 2), + ('MINUS', '-', 5), + ('NAME', 'or', 6), + ('MINUS', '-', 8), + ('NAME', 'bar', 9), + ('RBRACKET', ']', 12), + ] + assert check_tokens(inp, exp) + + def test_atdollar(): assert check_token('@$', ['ATDOLLAR', '@$', 0]) diff --git a/xonsh/__init__.py b/xonsh/__init__.py index f6e327a20..39af68808 100644 --- a/xonsh/__init__.py +++ b/xonsh/__init__.py @@ -50,6 +50,8 @@ else: _sys.modules['xonsh.foreign_shells'] = __amalgam__ jobs = __amalgam__ _sys.modules['xonsh.jobs'] = __amalgam__ + jsonutils = __amalgam__ + _sys.modules['xonsh.jsonutils'] = __amalgam__ lexer = __amalgam__ _sys.modules['xonsh.lexer'] = __amalgam__ openpy = __amalgam__ diff --git a/xonsh/environ.py b/xonsh/environ.py index 9356ee24f..b0bca48bf 100644 --- a/xonsh/environ.py +++ b/xonsh/environ.py @@ -22,7 +22,7 @@ from xonsh.foreign_shells import load_foreign_envs, load_foreign_aliases from xonsh.xontribs import update_context, prompt_xontrib_install from xonsh.platform import ( BASH_COMPLETIONS_DEFAULT, DEFAULT_ENCODING, PATH_DEFAULT, - ON_WINDOWS, ON_LINUX + ON_WINDOWS, ON_LINUX, os_environ ) from xonsh.tools import ( @@ -240,7 +240,7 @@ def default_xonshrc(env): """Creates a new instance of the default xonshrc tuple.""" if ON_WINDOWS: dxrc = (xonshconfig(env), - os.path.join(os.environ['ALLUSERSPROFILE'], + os.path.join(os_environ['ALLUSERSPROFILE'], 'xonsh', 'xonshrc'), os.path.expanduser('~/.xonshrc')) else: @@ -599,7 +599,7 @@ def DEFAULT_DOCS(): 'the possibilities. This currently only affects the prompt-toolkit shell.' ), 'UPDATE_OS_ENVIRON': VarDocs( - "If True ``os.environ`` will always be updated " + "If True ``os_environ`` will always be updated " "when the xonsh environment changes. The environment can be reset to " "the default value by calling ``__xonsh_env__.undo_replace_env()``"), 'UPDATE_PROMPT_ON_KEYPRESS': VarDocs( @@ -775,7 +775,7 @@ class Env(cabc.MutableMapping): _arg_regex = None def __init__(self, *args, **kwargs): - """If no initial environment is given, os.environ is used.""" + """If no initial environment is given, os_environ is used.""" self._d = {} # sentinel value for non existing envvars self._no_value = object() @@ -784,7 +784,7 @@ class Env(cabc.MutableMapping): self._defaults = DEFAULT_VALUES self._docs = DEFAULT_DOCS if len(args) == 0 and len(kwargs) == 0: - args = (os.environ,) + args = (os_environ,) for key, val in dict(*args, **kwargs).items(): self[key] = val if 'PATH' not in self._d: @@ -819,21 +819,21 @@ class Env(cabc.MutableMapping): return ctx def replace_env(self): - """Replaces the contents of os.environ with a detyped version + """Replaces the contents of os_environ with a detyped version of the xonsh environement. """ if self._orig_env is None: - self._orig_env = dict(os.environ) - os.environ.clear() - os.environ.update(self.detype()) + self._orig_env = dict(os_environ) + os_environ.clear() + os_environ.update(self.detype()) def undo_replace_env(self): - """Replaces the contents of os.environ with a detyped version + """Replaces the contents of os_environ with a detyped version of the xonsh environement. """ if self._orig_env is not None: - os.environ.clear() - os.environ.update(self._orig_env) + os_environ.clear() + os_environ.update(self._orig_env) self._orig_env = None def get_ensurer(self, key, @@ -952,7 +952,7 @@ class Env(cabc.MutableMapping): if self._orig_env is None: self.replace_env() else: - os.environ[key] = ensurer.detype(val) + os_environ[key] = ensurer.detype(val) if old_value is self._no_value: events.on_envvar_new.fire(name=key, value=val) elif old_value != val: @@ -964,8 +964,8 @@ class Env(cabc.MutableMapping): val = self._d.pop(key) if self.detypeable(val): self._detyped = None - if self.get('UPDATE_OS_ENVIRON') and key in os.environ: - del os.environ[key] + if self.get('UPDATE_OS_ENVIRON') and key in os_environ: + del os_environ[key] def get(self, key, default=None): """The environment will look up default values from its own defaults if a @@ -1047,10 +1047,10 @@ def load_static_config(ctx, config=None): config = env['XONSHCONFIG'] = xonshconfig(env) if os.path.isfile(config): # Note that an Env instance at __xonsh_env__ has not been started yet, - # per se, so we have to use os.environ - encoding = os.environ.get('XONSH_ENCODING', + # per se, so we have to use os_environ + encoding = os_environ.get('XONSH_ENCODING', DEFAULT_VALUES.get('XONSH_ENCODING', 'utf8')) - errors = os.environ.get('XONSH_ENCODING_ERRORS', + errors = os_environ.get('XONSH_ENCODING_ERRORS', DEFAULT_VALUES.get('XONSH_ENCODING_ERRORS', 'surrogateescape')) with open(config, 'r', encoding=encoding, errors=errors) as f: @@ -1109,8 +1109,8 @@ def windows_foreign_env_fixes(ctx): # /c/Windows/System32 syntax instead of C:\\Windows\\System32 # which messes up these environment variables for xonsh. for ev in ['PATH', 'TEMP', 'TMP']: - if ev in os.environ: - ctx[ev] = os.environ[ev] + if ev in os_environ: + ctx[ev] = os_environ[ev] elif ev in ctx: del ctx[ev] ctx['PWD'] = _get_cwd() or '' @@ -1181,7 +1181,7 @@ def default_env(env=None): """Constructs a default xonsh environment.""" # in order of increasing precedence ctx = dict(BASE_ENV) - ctx.update(os.environ) + ctx.update(os_environ) ctx['PWD'] = _get_cwd() or '' # other shells' PROMPT definitions generally don't work in XONSH: try: diff --git a/xonsh/foreign_shells.py b/xonsh/foreign_shells.py index 2386eef77..d00d3f7d0 100644 --- a/xonsh/foreign_shells.py +++ b/xonsh/foreign_shells.py @@ -14,7 +14,7 @@ import collections.abc as cabc from xonsh.lazyasd import lazyobject from xonsh.tools import to_bool, ensure_string -from xonsh.platform import ON_WINDOWS, ON_CYGWIN +from xonsh.platform import ON_WINDOWS, ON_CYGWIN, os_environ COMMAND = """{seterrprevcmd} {prevcmd} @@ -539,7 +539,7 @@ def _get_shells(shells=None, config=None, issue_warning=True): elif shells is not None: pass else: - env = getattr(builtins, '__xonsh_env__', os.environ) + env = getattr(builtins, '__xonsh_env__', os_environ) if env.get('LOADED_CONFIG', False): conf = builtins.__xonsh_config__ else: diff --git a/xonsh/jsonutils.py b/xonsh/jsonutils.py new file mode 100644 index 000000000..0805d9641 --- /dev/null +++ b/xonsh/jsonutils.py @@ -0,0 +1,19 @@ +"""Custom tools for managing JSON serialization / deserialization of xonsh +objects. +""" +import functools + +from xonsh.tools import EnvPath + + +@functools.singledispatch +def serialize_xonsh_json(val): + """JSON serializer for xonsh custom data structures. This is only + called when another normal JSON types are not found. + """ + return str(val) + + +@serialize_xonsh_json.register(EnvPath) +def _serialize_xonsh_json_env_path(val): + return val.paths diff --git a/xonsh/lexer.py b/xonsh/lexer.py index f9902e360..d923d0e26 100644 --- a/xonsh/lexer.py +++ b/xonsh/lexer.py @@ -69,15 +69,18 @@ def token_map(): def handle_name(state, token): """Function for handling name tokens""" typ = 'NAME' - state['last'] = token if state['pymode'][-1][0]: if token.string in kwmod.kwlist: typ = token.string.upper() + state['last'] = token yield _new_token(typ, token.string, token.start) else: - if token.string == 'and': + prev = state['last'] + state['last'] = token + has_whitespace = prev.end != token.start + if token.string == 'and' and has_whitespace: yield _new_token('AND', token.string, token.start) - elif token.string == 'or': + elif token.string == 'or' and has_whitespace: yield _new_token('OR', token.string, token.start) else: yield _new_token('NAME', token.string, token.start) diff --git a/xonsh/platform.py b/xonsh/platform.py index b9d051472..7e38666e4 100644 --- a/xonsh/platform.py +++ b/xonsh/platform.py @@ -359,6 +359,20 @@ def windows_bash_command(): # +@lazyobject +def os_environ(): + """This dispatches to the correct, case-senstive version of os.envrion. + This is mainly a problem for Windows. See #2024 for more details. + This can probably go away once support for Python v3.5 or v3.6 is + dropped. + """ + if ON_WINDOWS: + import nt + return nt.environ + else: + return os.environ + + @functools.lru_cache(1) def bash_command(): """Determines the command for Bash on the current plaform.""" diff --git a/xonsh/prompt/base.py b/xonsh/prompt/base.py index aab01d8ee..87814f548 100644 --- a/xonsh/prompt/base.py +++ b/xonsh/prompt/base.py @@ -92,7 +92,7 @@ class PromptFormatter: @xl.lazyobject def PROMPT_FIELDS(): return dict( - user=os.environ.get('USERNAME' if xp.ON_WINDOWS else 'USER', ''), + user=xp.os_environ.get('USERNAME' if xp.ON_WINDOWS else 'USER', ''), prompt_end='#' if xt.is_superuser() else '$', hostname=socket.gethostname().split('.', 1)[0], cwd=_dynamically_collapsed_pwd, diff --git a/xonsh/ptk/completer.py b/xonsh/ptk/completer.py index b0ee304de..885a8928c 100644 --- a/xonsh/ptk/completer.py +++ b/xonsh/ptk/completer.py @@ -48,6 +48,7 @@ class PromptToolkitCompleter(Completer): endidx + expand_offset, self.ctx) # completions from auto suggest + sug_comp = None if env.get('AUTO_SUGGEST'): sug_comp = self.suggestion_completion(document, line) if sug_comp is None: @@ -55,7 +56,9 @@ class PromptToolkitCompleter(Completer): elif len(completions) == 0: completions = (sug_comp,) else: - completions = (sug_comp,) + completions + completions = set(completions) + completions.discard(sug_comp) + completions = (sug_comp,) + tuple(sorted(completions)) # reserve space, if needed. if len(completions) <= 1: pass @@ -69,9 +72,13 @@ class PromptToolkitCompleter(Completer): break c_prefix = c_prefix[:-1] # yield completions + if sug_comp is None: + pre = min(document.cursor_position_col - begidx, len(c_prefix)) + else: + pre = len(c_prefix) for comp in completions: # do not display quote - disp = comp.strip('\'"')[len(c_prefix):] + disp = comp[pre:].strip('\'"') yield Completion(comp, -l, display=disp) def suggestion_completion(self, document, line): diff --git a/xonsh/pyghooks.py b/xonsh/pyghooks.py index e5b4eebd6..93a1edf34 100644 --- a/xonsh/pyghooks.py +++ b/xonsh/pyghooks.py @@ -28,6 +28,7 @@ from xonsh.color_tools import (RE_BACKGROUND, BASE_XONSH_COLORS, make_pallete, find_closest_color) from xonsh.style_tools import norm_name from xonsh.lazyimps import terminal256 +from xonsh.platform import os_environ load_module_in_background('pkg_resources', debug='XONSH_DEBUG', replacements={'pygments.plugin': 'pkg_resources'}) @@ -86,7 +87,7 @@ class XonshLexer(PythonLexer): if not hasattr(builtins, '__xonsh_env__'): setattr(builtins, '__xonsh_env__', {}) if ON_WINDOWS: - pathext = os.environ.get('PATHEXT', ['.EXE', '.BAT', '.CMD']) + pathext = os_environ.get('PATHEXT', ['.EXE', '.BAT', '.CMD']) builtins.__xonsh_env__['PATHEXT'] = pathext.split(os.pathsep) if not hasattr(builtins, '__xonsh_commands_cache__'): setattr(builtins, '__xonsh_commands_cache__', CommandsCache()) @@ -735,7 +736,7 @@ def _default_style(): Color.WHITE: '#ansilightgray', Color.YELLOW: '#ansibrown', } - elif ON_WINDOWS and 'CONEMUANSI' not in os.environ: + elif ON_WINDOWS and 'CONEMUANSI' not in os_environ: # These colors must match the color specification # in prompt_toolkit, so the colors are converted # correctly when using cmd.exe diff --git a/xonsh/readline_shell.py b/xonsh/readline_shell.py index 19c35c27d..d6c05062f 100644 --- a/xonsh/readline_shell.py +++ b/xonsh/readline_shell.py @@ -28,7 +28,7 @@ from xonsh.ansi_colors import (ansi_partial_color_format, ansi_color_style_names from xonsh.prompt.base import multiline_prompt from xonsh.tools import (print_exception, check_for_partial_string, to_bool, columnize, carriage_return) -from xonsh.platform import ON_WINDOWS, ON_CYGWIN, ON_DARWIN, ON_POSIX +from xonsh.platform import ON_WINDOWS, ON_CYGWIN, ON_DARWIN, ON_POSIX, os_environ from xonsh.lazyimps import pygments, pyghooks, winutils from xonsh.events import events @@ -117,7 +117,7 @@ def setup_readline(): else: readline.parse_and_bind("tab: complete") # try to load custom user settings - inputrc_name = os.environ.get('INPUTRC') + inputrc_name = os_environ.get('INPUTRC') if inputrc_name is None: if uses_libedit: inputrc_name = '.editrc' diff --git a/xonsh/tools.py b/xonsh/tools.py index 3daa7aa19..3a324cddf 100644 --- a/xonsh/tools.py +++ b/xonsh/tools.py @@ -34,13 +34,14 @@ import sys import threading import traceback import warnings +import operator # adding imports from further xonsh modules is discouraged to avoid circular # dependencies from xonsh.lazyasd import LazyObject, LazyDict, lazyobject from xonsh.platform import (has_prompt_toolkit, scandir, DEFAULT_ENCODING, ON_LINUX, ON_WINDOWS, PYTHON_VERSION_INFO, - expanduser) + expanduser, os_environ) @functools.lru_cache(1) @@ -85,7 +86,7 @@ class XonshCalledProcessError(XonshError, subprocess.CalledProcessError): def expand_path(s, expand_user=True): """Takes a string path and expands ~ to home if expand_user is set and environment vars if EXPAND_ENV_VARS is set.""" - env = getattr(builtins, '__xonsh_env__', os.environ) + env = getattr(builtins, '__xonsh_env__', os_environ) if env.get('EXPAND_ENV_VARS', False): s = expandvars(s) if expand_user: @@ -106,7 +107,7 @@ def _expandpath(path): """Performs environment variable / user expansion on a given path if EXPAND_ENV_VARS is set. """ - env = getattr(builtins, '__xonsh_env__', os.environ) + env = getattr(builtins, '__xonsh_env__', os_environ) expand_user = env.get('EXPAND_ENV_VARS', False) return expand_path(path, expand_user=expand_user) @@ -115,7 +116,7 @@ def decode_bytes(b): """Tries to decode the bytes using XONSH_ENCODING if available, otherwise using sys.getdefaultencoding(). """ - env = getattr(builtins, '__xonsh_env__', os.environ) + env = getattr(builtins, '__xonsh_env__', os_environ) enc = env.get('XONSH_ENCODING') or DEFAULT_ENCODING err = env.get('XONSH_ENCODING_ERRORS') or 'strict' return b.decode(encoding=enc, errors=err) @@ -198,6 +199,11 @@ class EnvPath(collections.MutableSequence): def __repr__(self): return repr(self._l) + def __eq__(self, other): + if len(self) != len(other): + return False + return all(map(operator.eq, self, other)) + class DefaultNotGivenType(object): """Singleton for representing when no default value is given.""" @@ -745,7 +751,7 @@ def print_exception(msg=None): env = getattr(builtins, '__xonsh_env__', None) # flags indicating whether the traceback options have been manually set if env is None: - env = os.environ + env = os_environ manually_set_trace = 'XONSH_SHOW_TRACEBACK' in env manually_set_logfile = 'XONSH_TRACEBACK_LOGFILE' in env else: @@ -1570,7 +1576,8 @@ def intensify_colors_on_win_setter(enable): """ enable = to_bool(enable) if hasattr(builtins, '__xonsh_shell__'): - delattr(builtins.__xonsh_shell__.shell.styler, 'style_name') + if hasattr(builtins.__xonsh_shell__.shell.styler, 'style_name'): + delattr(builtins.__xonsh_shell__.shell.styler, 'style_name') return enable diff --git a/xonsh/wizard.py b/xonsh/wizard.py index 8cfae3bda..d722652f2 100644 --- a/xonsh/wizard.py +++ b/xonsh/wizard.py @@ -8,6 +8,7 @@ import builtins import textwrap from xonsh.tools import to_bool, to_bool_or_break, backup_file, print_color +from xonsh.jsonutils import serialize_xonsh_json # @@ -610,7 +611,8 @@ class PromptVisitor(StateVisitor): return rtns def visit_save(self, node): - jstate = json.dumps(self.state, indent=1, sort_keys=True) + jstate = json.dumps(self.state, indent=1, sort_keys=True, + default=serialize_xonsh_json) if node.check: msg = 'The current state is:\n\n{0}\n' print(msg.format(textwrap.indent(jstate, ' '))) diff --git a/xonsh/xoreutils/which.py b/xonsh/xoreutils/which.py index 0b7072a4f..e3c7a1255 100644 --- a/xonsh/xoreutils/which.py +++ b/xonsh/xoreutils/which.py @@ -124,15 +124,15 @@ def which(args, stdin=None, stdout=None, stderr=None, spec=None): # which.whichgen gives the nicest 'verbose' output if PATH is taken # from os.environ so we temporarily override it with # __xosnh_env__['PATH'] - original_os_path = os.environ['PATH'] - os.environ['PATH'] = builtins.__xonsh_env__.detype()['PATH'] + original_os_path = xp.os_environ['PATH'] + xp.os_environ['PATH'] = builtins.__xonsh_env__.detype()['PATH'] matches = _which.whichgen(arg, exts=exts, verbose=verbose) for abs_name, from_where in matches: print_path(abs_name, from_where, stdout, verbose, captured) nmatches += 1 if not pargs.all: break - os.environ['PATH'] = original_os_path + xp.os_environ['PATH'] = original_os_path if not nmatches: failures.append(arg) if len(failures) == 0: