mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 16:34:47 +01:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
8e28a2e8af
48 changed files with 580 additions and 281 deletions
|
@ -1,4 +1,4 @@
|
|||
version: 0.8.3.{build}
|
||||
version: 0.8.5.{build}
|
||||
os: Windows Server 2012 R2
|
||||
environment:
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@ IF "%XONSH_TEST_ENV%" == "MSYS2" (
|
|||
%MSYS2_PATH%\usr\bin\pacman.exe -Syu --noconfirm
|
||||
%MSYS2_PATH%\usr\bin\pacman.exe -S --noconfirm python3 python3-pip
|
||||
%MSYS2_PATH%\usr\bin\bash.exe -c "/usr/bin/pip install -r requirements-tests.txt"
|
||||
%MSYS2_PATH%\usr\bin\bash.exe -c "/usr/bin/python setup.py install"
|
||||
) ELSE (
|
||||
echo "Windows Environment"
|
||||
%PYTHON%\Scripts\pip install -r requirements-tests.txt --upgrade --upgrade-strategy eager
|
||||
%PYTHON%\python.exe --version
|
||||
%PYTHON%\python.exe setup.py install
|
||||
)
|
||||
|
|
|
@ -5,8 +5,10 @@ IF "%XONSH_TEST_ENV%" == "MSYS2" (
|
|||
REM We monkey path `py._path.local.PosixPath` here such that it does not
|
||||
REM allow to create symlinks which are not supported by MSYS2 anyway. As a
|
||||
REM result the other pytest code uses a workaround.
|
||||
call %MSYS2_PATH%\usr\bin\bash.exe -c "/usr/bin/python -u -c 'import py._path.local; del py._path.local.PosixPath.mksymlinkto; import pytest; raise SystemExit(pytest.main())'" || EXIT 1
|
||||
SET "PATH=%MSYS2_PATH%\usr\bin;%PATH%"
|
||||
call bash.exe -c "/usr/bin/xonsh run-tests.xsh" || EXIT 1
|
||||
) ELSE (
|
||||
echo "Windows Environment"
|
||||
call %PYTHON%\Scripts\py.test || EXIT 1
|
||||
SET "PATH=%PYTHON%\Scripts;%PATH%"
|
||||
call xonsh run-tests.xsh || EXIT 1
|
||||
)
|
||||
|
|
|
@ -43,7 +43,9 @@ jobs:
|
|||
pip install . --no-deps
|
||||
- run:
|
||||
command: |
|
||||
/home/circleci/miniconda/envs/py34-xonsh-test/bin/pytest --timeout=10
|
||||
export PATH="$HOME/miniconda/bin:$PATH"
|
||||
source activate ${ENV_NAME}
|
||||
xonsh run-tests.xsh --timeout=10
|
||||
build_35:
|
||||
machine: true
|
||||
environment:
|
||||
|
@ -87,7 +89,9 @@ jobs:
|
|||
pip install . --no-deps
|
||||
- run:
|
||||
command: |
|
||||
/home/circleci/miniconda/envs/py35-xonsh-test/bin/pytest --timeout=10
|
||||
export PATH="$HOME/miniconda/bin:$PATH"
|
||||
source activate ${ENV_NAME}
|
||||
xonsh run-tests.xsh --timeout=10
|
||||
build_36:
|
||||
machine: true
|
||||
environment:
|
||||
|
@ -131,7 +135,9 @@ jobs:
|
|||
pip install . --no-deps
|
||||
- run:
|
||||
command: |
|
||||
/home/circleci/miniconda/envs/py36-xonsh-test/bin/pytest --timeout=10 --flake8 --cov=./xonsh
|
||||
export PATH="$HOME/miniconda/bin:$PATH"
|
||||
source activate ${ENV_NAME}
|
||||
xonsh run-tests.xsh --timeout=10 --flake8 --cov=./xonsh
|
||||
build_37:
|
||||
machine: true
|
||||
environment:
|
||||
|
@ -175,7 +181,9 @@ jobs:
|
|||
pip install . --no-deps
|
||||
- run:
|
||||
command: |
|
||||
/home/circleci/miniconda/envs/py37-xonsh-test/bin/pytest --timeout=10 --flake8 --cov=./xonsh
|
||||
export PATH="$HOME/miniconda/bin:$PATH"
|
||||
source activate ${ENV_NAME}
|
||||
xonsh run-tests.xsh --timeout=10 --flake8 --cov=./xonsh
|
||||
build_black:
|
||||
machine: true
|
||||
steps:
|
||||
|
|
|
@ -51,6 +51,7 @@ install:
|
|||
python setup.py install;
|
||||
else
|
||||
pip install --upgrade -r requirements-tests.txt;
|
||||
python setup.py install;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
|
@ -71,6 +72,6 @@ script:
|
|||
cd ..;
|
||||
doctr deploy --deploy-repo xonsh/xonsh-docs .;
|
||||
else
|
||||
py.test --timeout=10;
|
||||
xonsh run-tests.xsh --timeout=10;
|
||||
fi
|
||||
|
||||
|
|
119
CHANGELOG.rst
119
CHANGELOG.rst
|
@ -4,6 +4,125 @@ Xonsh Change Log
|
|||
|
||||
.. current developments
|
||||
|
||||
v0.8.5
|
||||
====================
|
||||
|
||||
**Added:**
|
||||
|
||||
* Add alias to `base16 shell <https://github.com/chriskempson/base16-shell>`_
|
||||
|
||||
* Installation / Usage
|
||||
1. To install use pip
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 -m pip install xontrib-base16-shell
|
||||
|
||||
2. Add on ``~/.xonshrc``
|
||||
|
||||
.. code:: python
|
||||
:number-lines:
|
||||
|
||||
$BASE16_SHELL = $HOME + "/.config/base16-shell/"
|
||||
xontrib load base16_shell
|
||||
|
||||
|
||||
3. See image
|
||||
|
||||
.. image:: https://raw.githubusercontent.com/ErickTucto/xontrib-base16-shell/master/docs/terminal.png
|
||||
:width: 600px
|
||||
:alt: terminal.png
|
||||
* New ``DumbShell`` class that kicks in whenever ``$TERM == "dumb"``.
|
||||
This usually happens in emacs. Currently, this class inherits from
|
||||
the ``ReadlineShell`` but adds some light customization to make
|
||||
sure that xonsh looks good in the resultant terminal emulator.
|
||||
* Aliases from foreign shells (e.g. Bash) that are more than single expressions,
|
||||
or contain sub-shell executions, are now evaluated and run in the foreign shell.
|
||||
Previously, xonsh would attempt to translate the alias from sh-lang into
|
||||
xonsh. These restrictions have been removed. For example, the following now
|
||||
works:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ source-bash 'alias eee="echo aaa \$(echo b)"'
|
||||
$ eee
|
||||
aaa b
|
||||
|
||||
* New ``ForeignShellBaseAlias``, ``ForeignShellFunctionAlias``, and
|
||||
``ForeignShellExecAlias`` classes have been added which manage foreign shell
|
||||
alias execution.
|
||||
|
||||
|
||||
**Changed:**
|
||||
|
||||
* String aliases will now first be checked to see if they contain sub-expressions
|
||||
that require evaluations, such as ``@(expr)``, ``$[cmd]``, etc. If they do,
|
||||
then an ``ExecAlias`` will be constructed, rather than a simple list-of-strs
|
||||
substitutiuon alias being used. For example:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ aliases['uuu'] = "echo ccc $(echo ddd)"
|
||||
$ aliases['uuu']
|
||||
ExecAlias('echo ccc $(echo ddd)\n', filename='<exec-alias:uuu>')
|
||||
$ uuu
|
||||
ccc ddd
|
||||
|
||||
* The ``parse_aliases()`` function now requires the shell name.
|
||||
* ``ForeignShellFunctionAlias`` now inherits from ``ForeignShellBaseAlias``
|
||||
rather than ``object``.
|
||||
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* Fixed issues where the prompt-toolkit v2 shell would print an extra newline
|
||||
after Python evaluations in interactive mode.
|
||||
|
||||
|
||||
|
||||
|
||||
v0.8.4
|
||||
====================
|
||||
|
||||
**Added:**
|
||||
|
||||
* Added the possibility of arbitrary paths to the help strings in ``vox activate`` and
|
||||
``vox remove``; also updated the documentation accordingly.
|
||||
* New ``xonsh.aliases.ExecAlias`` class enables multi-statement aliases.
|
||||
* New ``xonsh.ast.isexpression()`` function will return a boolean of whether
|
||||
code is a simple xonsh expression or not.
|
||||
* Added top-level ``run-tests.xsh`` script for safely running the test suite.
|
||||
|
||||
|
||||
**Changed:**
|
||||
|
||||
* String aliases are no longer split with ``shlex.split()``, but instead use
|
||||
``xonsh.lexer.Lexer.split()``.
|
||||
* Update xonsh/prompt/cwd.py _collapsed_pwd to print 2 chars if a directory begins with "."
|
||||
* test which determines whether a directory is a virtualenv
|
||||
|
||||
previously it used to check the existence of 'pyvenv.cfg'
|
||||
now it checks if 'bin/python' is executable
|
||||
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* Fixed issue with ``and`` & ``or`` being incorrectly tokenized in implicit
|
||||
subprocesses. Auto-wrapping of certain subprocesses will now correctly work.
|
||||
For example::
|
||||
|
||||
$ echo x-and-y
|
||||
x-and-y
|
||||
* Fix EOFError when press `control+d`
|
||||
* fix no candidates if no permission files in PATH
|
||||
* Fixed interpretation of color names with PTK2 and Pygments 2.3.
|
||||
* Several ResourceWarnings: unclosed file in tests
|
||||
* AttributeError crash when using --timings flag
|
||||
* issue #2929
|
||||
|
||||
|
||||
|
||||
|
||||
v0.8.3
|
||||
====================
|
||||
|
||||
|
|
|
@ -20,15 +20,16 @@ jobs:
|
|||
architecture: 'x64'
|
||||
|
||||
# Conda Environment
|
||||
# Create and activate a Conda environment.
|
||||
# Create and activate a Conda environment.
|
||||
- task: CondaEnvironment@1
|
||||
inputs:
|
||||
packageSpecs: 'python=$(python.version) pygments prompt_toolkit ply pytest pytest-timeout numpy psutil matplotlib flake8 coverage pyflakes pytest-cov pytest-flake8 codecov'
|
||||
installOptions: '-c conda-forge'
|
||||
updateConda: false
|
||||
- script: |
|
||||
pytest --timeout=10 --junitxml=junit/test-results.xml
|
||||
displayName: 'pytest'
|
||||
pip install .
|
||||
xonsh run-tests.xsh --timeout=10 --junitxml=junit/test-results.xml
|
||||
displayName: 'Tests'
|
||||
|
||||
# Publish build results
|
||||
- task: PublishTestResults@2
|
||||
|
|
10
docs/api/dumb_shell.rst
Normal file
10
docs/api/dumb_shell.rst
Normal file
|
@ -0,0 +1,10 @@
|
|||
.. _xonsh_dumb_shell:
|
||||
|
||||
******************************************************
|
||||
Dumb Shell (``xonsh.dumb_shell``)
|
||||
******************************************************
|
||||
|
||||
.. automodule:: xonsh.dumb_shell
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
|
@ -74,6 +74,7 @@ For those of you who want the gritty details.
|
|||
pyghooks
|
||||
jupyter_kernel
|
||||
jupyter_shell
|
||||
dumb_shell
|
||||
wizard
|
||||
xonfig
|
||||
codecache
|
||||
|
|
|
@ -1122,7 +1122,14 @@ matching only occurs for the first element of a subprocess command.
|
|||
The keys of ``aliases`` are strings that act as commands in subprocess-mode.
|
||||
The values are lists of strings, where the first element is the command, and
|
||||
the rest are the arguments. You can also set the value to a string, in which
|
||||
case it will be converted to a list automatically with ``shlex.split``.
|
||||
one of two things will happen:
|
||||
|
||||
1. If the string is a xonsh expression, it will be converted to a list
|
||||
automatically with xonsh's ``Lexer.split()`` method.
|
||||
2. If the string is more complex (representing a block of xonsh code),
|
||||
the alias will be registered as an ``ExecAlias``, which is a callable
|
||||
alias. This block of code will then be executed whenever the alias is
|
||||
run.
|
||||
|
||||
For example, the following creates several aliases for the ``git``
|
||||
version control software. Both styles (list of strings and single
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
**Added:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* Fixed issue with ``and`` & ``or`` being incoreectly tokenized in implicit
|
||||
subprocesses. Auto-wrapping of certain subprocesses will now correctly work.
|
||||
For example::
|
||||
|
||||
$ echo x-and-y
|
||||
x-and-y
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -1,24 +0,0 @@
|
|||
**Added:**
|
||||
|
||||
* Added the possibility of arbitrary paths to the help strings in ``vox activate`` and
|
||||
``vox remove``; also updated the documentation accordingly.
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -1,23 +0,0 @@
|
|||
**Added:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* fix no candidates if no permission files in PATH
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -1,23 +0,0 @@
|
|||
**Added:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* Several ResourceWarnings: unclosed file in tests
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -1,23 +0,0 @@
|
|||
**Added:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* AttributeError crash when using --timings flag
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -1,23 +0,0 @@
|
|||
**Added:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Changed:**
|
||||
|
||||
* Update xonsh/prompt/cwd.py _collapsed_pwd to print 2 chars if a directory begins with "."
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -1,26 +0,0 @@
|
|||
**Added:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Changed:**
|
||||
|
||||
* test which determines whether a directory is a virtualenv
|
||||
|
||||
previously it used to check the existence of 'pyvenv.cfg'
|
||||
now it checks if 'bin/python' is executable
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* issue #2929
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -12,6 +12,8 @@ $VERSION_BUMP_PATTERNS = [
|
|||
$CHANGELOG_FILENAME = 'CHANGELOG.rst'
|
||||
$CHANGELOG_TEMPLATE = 'TEMPLATE.rst'
|
||||
|
||||
$PYTEST_COMMAND = "./run-tests.xsh"
|
||||
|
||||
$TAG_REMOTE = 'git@github.com:xonsh/xonsh.git'
|
||||
$TAG_TARGET = 'master'
|
||||
|
||||
|
|
10
run-tests.xsh
Executable file
10
run-tests.xsh
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env xonsh
|
||||
$RAISE_SUBPROC_ERROR = True
|
||||
|
||||
run_separately = [
|
||||
'tests/test_ptk_highlight.py',
|
||||
]
|
||||
|
||||
![pytest @($ARGS[1:]) --ignore @(run_separately)]
|
||||
for fname in run_separately:
|
||||
![pytest @($ARGS[1:]) @(fname)]
|
|
@ -33,8 +33,24 @@ def source_path():
|
|||
return os.path.dirname(pwd)
|
||||
|
||||
|
||||
def ensure_attached_session(session):
|
||||
def ensure_attached_session(monkeypatch, session):
|
||||
for i in range(1, 11):
|
||||
|
||||
# next try to monkey patch with raising.
|
||||
try:
|
||||
monkeypatch.setattr(builtins, "__xonsh__", session, raising=True)
|
||||
except AttributeError:
|
||||
pass
|
||||
if hasattr(builtins, "__xonsh__"):
|
||||
break
|
||||
# first try to monkey patch without raising.
|
||||
try:
|
||||
monkeypatch.setattr(builtins, "__xonsh__", session, raising=False)
|
||||
except AttributeError:
|
||||
pass
|
||||
if hasattr(builtins, "__xonsh__"):
|
||||
break
|
||||
# now just try to apply it
|
||||
builtins.__xonsh__ = session
|
||||
if hasattr(builtins, "__xonsh__"):
|
||||
break
|
||||
|
@ -56,11 +72,15 @@ def xonsh_execer(monkeypatch):
|
|||
"xonsh.built_ins.load_builtins.__code__",
|
||||
(lambda *args, **kwargs: None).__code__,
|
||||
)
|
||||
added_session = False
|
||||
if not hasattr(builtins, "__xonsh__"):
|
||||
ensure_attached_session(XonshSession())
|
||||
added_session = True
|
||||
ensure_attached_session(monkeypatch, XonshSession())
|
||||
execer = Execer(unload=False)
|
||||
builtins.__xonsh__.execer = execer
|
||||
return execer
|
||||
yield execer
|
||||
if added_session:
|
||||
monkeypatch.delattr(builtins, "__xonsh__", raising=False)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -71,7 +91,7 @@ def monkeypatch_stderr(monkeypatch):
|
|||
yield
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
@pytest.fixture
|
||||
def xonsh_events():
|
||||
yield events
|
||||
for name, oldevent in vars(events).items():
|
||||
|
@ -81,13 +101,13 @@ def xonsh_events():
|
|||
setattr(events, name, newevent)
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
def xonsh_builtins(xonsh_events):
|
||||
@pytest.fixture
|
||||
def xonsh_builtins(monkeypatch, xonsh_events):
|
||||
"""Mock out most of the builtins xonsh attributes."""
|
||||
old_builtins = set(dir(builtins))
|
||||
execer = getattr(getattr(builtins, "__xonsh__", None), "execer", None)
|
||||
session = XonshSession(execer=execer, ctx={})
|
||||
ensure_attached_session(session)
|
||||
ensure_attached_session(monkeypatch, session)
|
||||
builtins.__xonsh__.env = DummyEnv()
|
||||
if ON_WINDOWS:
|
||||
builtins.__xonsh__.env["PATHEXT"] = [".EXE", ".BAT", ".CMD"]
|
||||
|
@ -131,6 +151,7 @@ def xonsh_builtins(xonsh_events):
|
|||
# be firing events on the global instance.
|
||||
builtins.events = xonsh_events
|
||||
yield builtins
|
||||
monkeypatch.delattr(builtins, "__xonsh__", raising=False)
|
||||
for attr in set(dir(builtins)) - old_builtins:
|
||||
if hasattr(builtins, attr):
|
||||
delattr(builtins, attr)
|
||||
|
|
|
@ -17,17 +17,19 @@ def cd(args, stdin=None):
|
|||
return args
|
||||
|
||||
|
||||
ALIASES = Aliases(
|
||||
{"o": ["omg", "lala"]},
|
||||
color_ls=["ls", "--color=true"],
|
||||
ls="ls '- -'",
|
||||
cd=cd,
|
||||
indirect_cd="cd ..",
|
||||
)
|
||||
RAW = ALIASES._raw
|
||||
def make_aliases():
|
||||
ales = Aliases(
|
||||
{"o": ["omg", "lala"]},
|
||||
color_ls=["ls", "--color=true"],
|
||||
ls="ls '- -'",
|
||||
cd=cd,
|
||||
indirect_cd="cd ..",
|
||||
)
|
||||
return ales
|
||||
|
||||
|
||||
def test_imports():
|
||||
def test_imports(xonsh_execer, xonsh_builtins):
|
||||
ales = make_aliases()
|
||||
expected = {
|
||||
"o": ["omg", "lala"],
|
||||
"ls": ["ls", "- -"],
|
||||
|
@ -35,22 +37,27 @@ def test_imports():
|
|||
"cd": cd,
|
||||
"indirect_cd": ["cd", ".."],
|
||||
}
|
||||
assert RAW == expected
|
||||
raw = ales._raw
|
||||
assert raw == expected
|
||||
|
||||
|
||||
def test_eval_normal(xonsh_builtins):
|
||||
assert ALIASES.get("o") == ["omg", "lala"]
|
||||
def test_eval_normal(xonsh_execer, xonsh_builtins):
|
||||
ales = make_aliases()
|
||||
assert ales.get("o") == ["omg", "lala"]
|
||||
|
||||
|
||||
def test_eval_self_reference(xonsh_builtins):
|
||||
assert ALIASES.get("ls") == ["ls", "- -"]
|
||||
def test_eval_self_reference(xonsh_execer, xonsh_builtins):
|
||||
ales = make_aliases()
|
||||
assert ales.get("ls") == ["ls", "- -"]
|
||||
|
||||
|
||||
def test_eval_recursive(xonsh_builtins):
|
||||
assert ALIASES.get("color_ls") == ["ls", "- -", "--color=true"]
|
||||
def test_eval_recursive(xonsh_execer, xonsh_builtins):
|
||||
ales = make_aliases()
|
||||
assert ales.get("color_ls") == ["ls", "- -", "--color=true"]
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_eval_recursive_callable_partial(xonsh_builtins):
|
||||
def test_eval_recursive_callable_partial(xonsh_execer, xonsh_builtins):
|
||||
ales = make_aliases()
|
||||
xonsh_builtins.__xonsh__.env = Env(HOME=os.path.expanduser("~"))
|
||||
assert ALIASES.get("indirect_cd")(["arg2", "arg3"]) == ["..", "arg2", "arg3"]
|
||||
assert ales.get("indirect_cd")(["arg2", "arg3"]) == ["..", "arg2", "arg3"]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import ast as pyast
|
||||
|
||||
from xonsh import ast
|
||||
from xonsh.ast import Tuple, Name, Store, min_line, Call, BinOp, pdump
|
||||
from xonsh.ast import Tuple, Name, Store, min_line, Call, BinOp, pdump, isexpression
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -49,7 +49,7 @@ def test_gather_load_store_names_tuple():
|
|||
"l = 1",
|
||||
],
|
||||
)
|
||||
def test_multilline_num(line1):
|
||||
def test_multilline_num(xonsh_execer, line1):
|
||||
code = line1 + "\nls -l\n"
|
||||
tree = check_parse(code)
|
||||
lsnode = tree.body[1]
|
||||
|
@ -115,10 +115,28 @@ def test_unmodified(inp):
|
|||
|
||||
assert nodes_equal(exp, obs)
|
||||
|
||||
@pytest.mark.parametrize("test_input", [
|
||||
"echo; echo && echo\n",
|
||||
"echo; echo && echo a\n",
|
||||
"true && false && true\n",
|
||||
])
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_input",
|
||||
["echo; echo && echo\n", "echo; echo && echo a\n", "true && false && true\n"],
|
||||
)
|
||||
def test_whitespace_subproc(test_input):
|
||||
assert check_parse(test_input)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"inp,exp",
|
||||
[
|
||||
("1+1", True),
|
||||
("1+1;", True),
|
||||
("1+1\n", True),
|
||||
("1+1; 2+2", False),
|
||||
("1+1; 2+2;", False),
|
||||
("1+1; 2+2\n", False),
|
||||
("1+1; 2+2;\n", False),
|
||||
("x = 42", False),
|
||||
],
|
||||
)
|
||||
def test_isexpression(xonsh_execer, inp, exp):
|
||||
obs = isexpression(inp)
|
||||
assert exp is obs
|
||||
|
|
|
@ -37,7 +37,7 @@ pytestmark = pytest.mark.skipif(
|
|||
)
|
||||
|
||||
|
||||
@pytest.yield_fixture(scope="module")
|
||||
@pytest.fixture(scope="module")
|
||||
def shares_setup(tmpdir_factory):
|
||||
"""create some shares to play with on current machine.
|
||||
|
||||
|
@ -218,8 +218,8 @@ def test_uncpushd_push_base_push_rempath(xonsh_builtins):
|
|||
pass
|
||||
|
||||
|
||||
# really? Need to cut-and-paste 2 flavors of this? yield_fixture requires yield in defined function body, not callee
|
||||
@pytest.yield_fixture()
|
||||
# really? Need to cut-and-paste 2 flavors of this? fixture requires yield in defined function body, not callee
|
||||
@pytest.fixture()
|
||||
def with_unc_check_enabled():
|
||||
if not ON_WINDOWS:
|
||||
return
|
||||
|
@ -251,7 +251,7 @@ def with_unc_check_enabled():
|
|||
winreg.CloseKey(key)
|
||||
|
||||
|
||||
@pytest.yield_fixture()
|
||||
@pytest.fixture()
|
||||
def with_unc_check_disabled(): # just like the above, but value is 1 to *disable* unc check
|
||||
if not ON_WINDOWS:
|
||||
return
|
||||
|
|
|
@ -64,7 +64,7 @@ def test_parse_aliases():
|
|||
"__XONSH_ALIAS_END__\n"
|
||||
"more filth"
|
||||
)
|
||||
obs = parse_aliases(s)
|
||||
obs = parse_aliases(s, 'bash')
|
||||
assert exp == obs
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ from xonsh.history.main import history_main, _xh_parse_args, construct_history
|
|||
CMDS = ["ls", "cat hello kitty", "abc", "def", "touch me", "grep from me"]
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
@pytest.fixture
|
||||
def hist():
|
||||
h = JsonHistory(
|
||||
filename="xonsh-HISTORY-TEST.json", here="yup", sessionid="SESSIONID", gc=False
|
||||
|
|
|
@ -10,7 +10,7 @@ from xonsh.history.main import history_main
|
|||
import pytest
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
@pytest.fixture
|
||||
def hist():
|
||||
h = SqliteHistory(
|
||||
filename="xonsh-HISTORY-TEST.sqlite", sessionid="SESSIONID", gc=False
|
||||
|
|
|
@ -13,8 +13,8 @@ from xonsh.built_ins import unload_builtins
|
|||
imphooks.install_import_hooks()
|
||||
|
||||
|
||||
@pytest.yield_fixture(autouse=True)
|
||||
def imp_env():
|
||||
@pytest.fixture(autouse=True)
|
||||
def imp_env(xonsh_builtins):
|
||||
execer = Execer(unload=False)
|
||||
builtins.__xonsh__.env = Env({"PATH": [], "PATHEXT": []})
|
||||
yield
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import os
|
||||
import tempfile
|
||||
|
||||
from xonsh.lib.os import indir, rmtree
|
||||
|
||||
import pytest
|
||||
|
||||
from tools import ON_WINDOWS
|
||||
|
||||
|
||||
def test_indir():
|
||||
if ON_WINDOWS:
|
||||
pytest.skip("On Windows")
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
assert ![pwd].output.strip() != tmpdir
|
||||
with indir(tmpdir):
|
||||
|
|
|
@ -4,10 +4,14 @@ import tempfile
|
|||
from xonsh.lib.os import indir
|
||||
from xonsh.lib.subprocess import run, check_call, check_output, CalledProcessError
|
||||
|
||||
from tools import skip_if_on_windows
|
||||
import pytest
|
||||
|
||||
from tools import ON_WINDOWS
|
||||
|
||||
|
||||
def test_run():
|
||||
if ON_WINDOWS:
|
||||
pytest.skip("On Windows")
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with indir(tmpdir):
|
||||
run(['touch', 'hello.txt'])
|
||||
|
@ -19,6 +23,8 @@ def test_run():
|
|||
|
||||
|
||||
def test_check_call():
|
||||
if ON_WINDOWS:
|
||||
pytest.skip("On Windows")
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with indir(tmpdir):
|
||||
check_call(['touch', 'hello.txt'])
|
||||
|
@ -29,8 +35,9 @@ def test_check_call():
|
|||
assert 'tst_dir/hello.txt' in g`tst_dir/*.txt`
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_check_call_raises():
|
||||
if ON_WINDOWS:
|
||||
pytest.skip("On Windows")
|
||||
try:
|
||||
check_call('false')
|
||||
got_raise = False
|
||||
|
@ -40,6 +47,8 @@ def test_check_call_raises():
|
|||
|
||||
|
||||
def test_check_output():
|
||||
if ON_WINDOWS:
|
||||
pytest.skip("On Windows")
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with indir(tmpdir):
|
||||
check_call(['touch', 'hello.txt'])
|
||||
|
@ -49,4 +58,3 @@ def test_check_output():
|
|||
p = check_output(['touch', 'hello.txt'], cwd='tst_dir')
|
||||
assert p.decode('utf-8') == ''
|
||||
assert 'tst_dir/hello.txt' in g`tst_dir/*.txt`
|
||||
|
||||
|
|
|
@ -19,11 +19,12 @@ from tools import skip_if_on_windows
|
|||
|
||||
from xonsh.platform import ON_WINDOWS
|
||||
from xonsh.built_ins import load_builtins, unload_builtins
|
||||
from xonsh.execer import Execer
|
||||
from xonsh.pyghooks import XonshLexer
|
||||
|
||||
|
||||
@pytest.yield_fixture(autouse=True)
|
||||
def load_command_cache():
|
||||
@pytest.fixture(autouse=True)
|
||||
def load_command_cache(xonsh_builtins):
|
||||
load_builtins()
|
||||
if ON_WINDOWS:
|
||||
for key in ("cd", "bash"):
|
||||
|
@ -58,10 +59,12 @@ def test_bin_ls():
|
|||
check_token("/bin/ls -al", [(Name.Builtin, "/bin/ls")])
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_py_print():
|
||||
check_token('print("hello")', [(Keyword, "print"), (String.Double, "hello")])
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_invalid_cmd():
|
||||
check_token("non-existance-cmd -al", [(Name, "non")]) # parse as python
|
||||
check_token(
|
||||
|
@ -71,6 +74,7 @@ def test_invalid_cmd():
|
|||
check_token("(1, )", [(Punctuation, "("), (Number.Integer, "1")])
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_multi_cmd():
|
||||
check_token(
|
||||
"cd && cd", [(Name.Builtin, "cd"), (Operator, "&&"), (Name.Builtin, "cd")]
|
||||
|
@ -81,6 +85,7 @@ def test_multi_cmd():
|
|||
)
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_nested():
|
||||
check_token(
|
||||
'echo @("hello")',
|
||||
|
@ -117,6 +122,7 @@ def test_nested():
|
|||
)
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_path(tmpdir):
|
||||
test_dir = str(tmpdir.mkdir("xonsh-test-highlight-path"))
|
||||
check_token(
|
||||
|
@ -132,10 +138,12 @@ def test_path(tmpdir):
|
|||
check_token(test_dir, [(Name.Constant, test_dir)])
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_subproc_args():
|
||||
check_token("cd 192.168.0.1", [(Text, "192.168.0.1")])
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_backtick():
|
||||
check_token(
|
||||
r"echo g`.*\w+`",
|
||||
|
@ -149,6 +157,7 @@ def test_backtick():
|
|||
)
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_macro():
|
||||
check_token(
|
||||
r"g!(42, *, 65)",
|
||||
|
|
|
@ -18,7 +18,7 @@ from tools import DummyEnv, skip_if_lt_ptk2
|
|||
Context = namedtuple("Context", ["indent", "buffer", "accept", "cli", "cr"])
|
||||
|
||||
|
||||
@pytest.yield_fixture(scope="module")
|
||||
@pytest.fixture(scope="module")
|
||||
def ctx():
|
||||
"""Context in which the ptk multiline functionality will be tested."""
|
||||
builtins.__xonsh__ = XonshSession()
|
||||
|
|
|
@ -15,7 +15,7 @@ from tools import skip_if_on_darwin
|
|||
HISTDIR = os.path.join(os.path.dirname(__file__), "histories")
|
||||
|
||||
|
||||
@pytest.yield_fixture(scope="module", autouse=True)
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def ctx():
|
||||
"""Create a global Shell instance to use in all the test."""
|
||||
ctx = {"PATH": []}
|
||||
|
|
|
@ -64,6 +64,7 @@ from xonsh.tools import (
|
|||
is_balanced,
|
||||
subexpr_before_unbalanced,
|
||||
swap_values,
|
||||
get_line_continuation,
|
||||
get_logical_line,
|
||||
replace_logical_line,
|
||||
check_quotes,
|
||||
|
@ -521,7 +522,8 @@ def test_replace_logical_line(src, idx, exp_line, exp_n):
|
|||
idx -= 1
|
||||
replace_logical_line(lines, logical, idx, exp_n)
|
||||
exp = src.replace("\\\n", "").strip()
|
||||
obs = "\n".join(lines).replace("\\\n", "").strip()
|
||||
lc = get_line_continuation() + "\n"
|
||||
obs = "\n".join(lines).replace(lc, "").strip()
|
||||
assert exp == obs
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ def test_load_xontrib_metadata():
|
|||
xontrib_metadata()
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
@pytest.fixture
|
||||
def tmpmod(tmpdir):
|
||||
"""
|
||||
Same as tmpdir but also adds/removes it to the front of sys.path.
|
||||
|
|
|
@ -30,6 +30,9 @@ ON_CONDA = True in [
|
|||
conda in pytest.__file__.lower() for conda in ["conda", "anaconda", "miniconda"]
|
||||
]
|
||||
ON_TRAVIS = "TRAVIS" in os.environ and "CI" in os.environ
|
||||
ON_AZURE_PIPELINES = os.environ.get("TF_BUILD", "") == "True"
|
||||
print("ON_AZURE_PIPELINES", repr(ON_AZURE_PIPELINES))
|
||||
print("os.environ['TF_BUILD']", repr(os.environ.get("TF_BUILD", "")))
|
||||
TEST_DIR = os.path.dirname(__file__)
|
||||
|
||||
# pytest skip decorators
|
||||
|
@ -48,6 +51,8 @@ skip_if_on_msys = pytest.mark.skipif(
|
|||
|
||||
skip_if_on_windows = pytest.mark.skipif(ON_WINDOWS, reason="Unix stuff")
|
||||
|
||||
skip_if_on_azure_pipelines = pytest.mark.skipif(ON_AZURE_PIPELINES, reason="not suitable for azure")
|
||||
|
||||
skip_if_on_unix = pytest.mark.skipif(not ON_WINDOWS, reason="Windows stuff")
|
||||
|
||||
skip_if_on_darwin = pytest.mark.skipif(ON_DARWIN, reason="not Mac friendly")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
__version__ = "0.8.3"
|
||||
__version__ = "0.8.5"
|
||||
|
||||
|
||||
# amalgamate exclude jupyter_kernel parser_table parser_test_table pyghooks
|
||||
|
@ -86,6 +86,8 @@ else:
|
|||
_sys.modules["xonsh.tracer"] = __amalgam__
|
||||
aliases = __amalgam__
|
||||
_sys.modules["xonsh.aliases"] = __amalgam__
|
||||
dumb_shell = __amalgam__
|
||||
_sys.modules["xonsh.dumb_shell"] = __amalgam__
|
||||
built_ins = __amalgam__
|
||||
_sys.modules["xonsh.built_ins"] = __amalgam__
|
||||
execer = __amalgam__
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Aliases for the xonsh shell."""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import shlex
|
||||
import inspect
|
||||
import argparse
|
||||
import builtins
|
||||
|
@ -24,14 +24,26 @@ from xonsh.platform import (
|
|||
from xonsh.tools import unthreadable, print_color
|
||||
from xonsh.replay import replay_main
|
||||
from xonsh.timings import timeit_alias
|
||||
from xonsh.tools import argvquote, escape_windows_cmd_string, to_bool, swap_values
|
||||
from xonsh.tools import (
|
||||
argvquote,
|
||||
escape_windows_cmd_string,
|
||||
to_bool,
|
||||
swap_values,
|
||||
strip_simple_quotes,
|
||||
)
|
||||
from xonsh.xontribs import xontribs_main
|
||||
from xonsh.ast import isexpression
|
||||
|
||||
import xonsh.completers._aliases as xca
|
||||
import xonsh.history.main as xhm
|
||||
import xonsh.xoreutils.which as xxw
|
||||
|
||||
|
||||
@lazyobject
|
||||
def SUB_EXEC_ALIAS_RE():
|
||||
return re.compile(r"@\(|\$\(|!\(|\$\[|!\[")
|
||||
|
||||
|
||||
class Aliases(cabc.MutableMapping):
|
||||
"""Represents a location to hold and look up aliases."""
|
||||
|
||||
|
@ -115,7 +127,17 @@ class Aliases(cabc.MutableMapping):
|
|||
|
||||
def __setitem__(self, key, val):
|
||||
if isinstance(val, str):
|
||||
self._raw[key] = shlex.split(val)
|
||||
f = "<exec-alias:" + key + ">"
|
||||
if SUB_EXEC_ALIAS_RE.search(val) is not None:
|
||||
# We have a sub-command, e.g. $(cmd), to evaluate
|
||||
self._raw[key] = ExecAlias(val, filename=f)
|
||||
elif isexpression(val):
|
||||
# expansion substitution
|
||||
lexer = builtins.__xonsh__.execer.parser.lexer
|
||||
self._raw[key] = list(map(strip_simple_quotes, lexer.split(val)))
|
||||
else:
|
||||
# need to exec alias
|
||||
self._raw[key] = ExecAlias(val, filename=f)
|
||||
else:
|
||||
self._raw[key] = val
|
||||
|
||||
|
@ -150,6 +172,37 @@ class Aliases(cabc.MutableMapping):
|
|||
p.pretty(dict(self))
|
||||
|
||||
|
||||
class ExecAlias:
|
||||
"""Provides a callable alias for xonsh source code."""
|
||||
|
||||
def __init__(self, src, filename="<exec-alias>"):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
src : str
|
||||
Source code that will be
|
||||
"""
|
||||
self.src = src if src.endswith("\n") else src + "\n"
|
||||
self.filename = filename
|
||||
|
||||
def __call__(
|
||||
self, args, stdin=None, stdout=None, stderr=None, spec=None, stack=None
|
||||
):
|
||||
execer = builtins.__xonsh__.execer
|
||||
frame = stack[0][0] # execute as though we are at the call site
|
||||
execer.exec(
|
||||
self.src, glbs=frame.f_globals, locs=frame.f_locals, filename=self.filename
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "ExecAlias({0!r}, filename={1!r})".format(self.src, self.filename)
|
||||
|
||||
|
||||
#
|
||||
# Actual aliases below
|
||||
#
|
||||
|
||||
|
||||
def xonsh_exit(args, stdin=None):
|
||||
"""Sends signal to exit shell."""
|
||||
if not clean_jobs():
|
||||
|
|
21
xonsh/ast.py
21
xonsh/ast.py
|
@ -3,6 +3,7 @@
|
|||
# These are imported into our module namespace for the benefit of parser.py.
|
||||
# pylint: disable=unused-import
|
||||
import sys
|
||||
import builtins
|
||||
from ast import (
|
||||
Module,
|
||||
Num,
|
||||
|
@ -302,6 +303,26 @@ def isdescendable(node):
|
|||
return isinstance(node, (UnaryOp, BoolOp))
|
||||
|
||||
|
||||
def isexpression(node, ctx=None, *args, **kwargs):
|
||||
"""Determines whether a node (or code string) is an expression, and
|
||||
does not contain any statements. The execution context (ctx) and
|
||||
other args and kwargs are passed down to the parser, as needed.
|
||||
"""
|
||||
# parse string to AST
|
||||
if isinstance(node, str):
|
||||
node = node if node.endswith("\n") else node + "\n"
|
||||
ctx = builtins.__xonsh__.ctx if ctx is None else ctx
|
||||
node = builtins.__xonsh__.execer.parse(node, ctx, *args, **kwargs)
|
||||
# determin if expresission-like enough
|
||||
if isinstance(node, (Expr, Expression)):
|
||||
isexpr = True
|
||||
elif isinstance(node, Module) and len(node.body) == 1:
|
||||
isexpr = isinstance(node.body[0], (Expr, Expression))
|
||||
else:
|
||||
isexpr = False
|
||||
return isexpr
|
||||
|
||||
|
||||
class CtxAwareTransformer(NodeTransformer):
|
||||
"""Transforms a xonsh AST based to use subprocess calls when
|
||||
the first name in an expression statement is not known in the context.
|
||||
|
|
12
xonsh/dumb_shell.py
Normal file
12
xonsh/dumb_shell.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
"""A dumb shell for when $TERM == 'dumb', which usually happens in emacs."""
|
||||
import builtins
|
||||
|
||||
from xonsh.readline_shell import ReadlineShell
|
||||
|
||||
|
||||
class DumbShell(ReadlineShell):
|
||||
"""A dumb shell for when $TERM == 'dumb', which usually happens in emacs."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
builtins.__xonsh__.env["XONSH_COLOR_STYLE"] = "emacs"
|
||||
super().__init__(*args, **kwargs)
|
|
@ -298,7 +298,7 @@ def foreign_shell_data(
|
|||
if use_tmpfile:
|
||||
os.remove(tmpfile.name)
|
||||
env = parse_env(s)
|
||||
aliases = parse_aliases(s)
|
||||
aliases = parse_aliases(s, shell=shell, sourcer=sourcer, extra_args=extra_args)
|
||||
funcs = parse_funcs(s, shell=shell, sourcer=sourcer, extra_args=extra_args)
|
||||
aliases.update(funcs)
|
||||
return env, aliases
|
||||
|
@ -332,7 +332,12 @@ def ALIAS_RE():
|
|||
)
|
||||
|
||||
|
||||
def parse_aliases(s):
|
||||
@lazyobject
|
||||
def FS_EXEC_ALIAS_RE():
|
||||
return re.compile(r";|`|\$\(")
|
||||
|
||||
|
||||
def parse_aliases(s, shell, sourcer=None, extra_args=()):
|
||||
"""Parses the aliases portion of string into a dict."""
|
||||
m = ALIAS_RE.search(s)
|
||||
if m is None:
|
||||
|
@ -352,7 +357,20 @@ def parse_aliases(s):
|
|||
# strip one single quote at the start and end of value
|
||||
if value[0] == "'" and value[-1] == "'":
|
||||
value = value[1:-1]
|
||||
value = shlex.split(value)
|
||||
# now compute actual alias
|
||||
if FS_EXEC_ALIAS_RE.search(value) is None:
|
||||
# simple list of args alias
|
||||
value = shlex.split(value)
|
||||
else:
|
||||
# alias is more complex, use ExecAlias, but via shell
|
||||
filename = "<foreign-shell-exec-alias:" + key + ">"
|
||||
value = ForeignShellExecAlias(
|
||||
src=value,
|
||||
shell=shell,
|
||||
filename=filename,
|
||||
sourcer=sourcer,
|
||||
extra_args=extra_args,
|
||||
)
|
||||
except ValueError as exc:
|
||||
warnings.warn(
|
||||
'could not parse alias "{0}": {1!r}'.format(key, exc), RuntimeWarning
|
||||
|
@ -400,7 +418,7 @@ def parse_funcs(s, shell, sourcer=None, extra_args=()):
|
|||
if not os.path.isabs(filename):
|
||||
filename = os.path.abspath(filename)
|
||||
wrapper = ForeignShellFunctionAlias(
|
||||
name=funcname,
|
||||
funcname=funcname,
|
||||
shell=shell,
|
||||
sourcer=sourcer,
|
||||
filename=filename,
|
||||
|
@ -410,19 +428,17 @@ def parse_funcs(s, shell, sourcer=None, extra_args=()):
|
|||
return funcs
|
||||
|
||||
|
||||
class ForeignShellFunctionAlias(object):
|
||||
class ForeignShellBaseAlias(object):
|
||||
"""This class is responsible for calling foreign shell functions as if
|
||||
they were aliases. This does not currently support taking stdin.
|
||||
"""
|
||||
|
||||
INPUT = '{sourcer} "{filename}"\n' "{funcname} {args}\n"
|
||||
INPUT = "echo ForeignShellBaseAlias {shell} {filename} {args}\n"
|
||||
|
||||
def __init__(self, name, shell, filename, sourcer=None, extra_args=()):
|
||||
def __init__(self, shell, filename, sourcer=None, extra_args=()):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
function name
|
||||
shell : str
|
||||
Name or path to shell
|
||||
filename : str
|
||||
|
@ -433,36 +449,29 @@ class ForeignShellFunctionAlias(object):
|
|||
Additional command line options to pass into the shell.
|
||||
"""
|
||||
sourcer = DEFAULT_SOURCERS.get(shell, "source") if sourcer is None else sourcer
|
||||
self.name = name
|
||||
self.shell = shell
|
||||
self.filename = filename
|
||||
self.sourcer = sourcer
|
||||
self.extra_args = extra_args
|
||||
|
||||
def __eq__(self, other):
|
||||
if (
|
||||
not hasattr(other, "name")
|
||||
or not hasattr(other, "filename")
|
||||
or not hasattr(other, "sourcer")
|
||||
or not hasattr(other, "exta_args")
|
||||
):
|
||||
return NotImplemented
|
||||
return (
|
||||
(self.name == other.name)
|
||||
and (self.shell == other.shell)
|
||||
and (self.filename == other.filename)
|
||||
and (self.sourcer == other.sourcer)
|
||||
and (self.extra_args == other.extra_args)
|
||||
)
|
||||
def _input_kwargs(self):
|
||||
return {
|
||||
"shell": self.shell,
|
||||
"filename": self.filename,
|
||||
"sourcer": self.sourcer,
|
||||
"extra_args": self.extra_args,
|
||||
}
|
||||
|
||||
def __call__(self, args, stdin=None):
|
||||
def __eq__(self, other):
|
||||
if not hasattr(other, "_input_kwargs") or not callable(other._input_kwargs):
|
||||
return NotImplemented
|
||||
return self._input_kwargs() == other._input_kwargs()
|
||||
|
||||
def __call__(
|
||||
self, args, stdin=None, stdout=None, stderr=None, spec=None, stack=None
|
||||
):
|
||||
args, streaming = self._is_streaming(args)
|
||||
input = self.INPUT.format(
|
||||
sourcer=self.sourcer,
|
||||
filename=self.filename,
|
||||
funcname=self.name,
|
||||
args=" ".join(args),
|
||||
)
|
||||
input = self.INPUT.format(args=" ".join(args), **self._input_kwargs())
|
||||
cmd = [self.shell] + list(self.extra_args) + ["-c", input]
|
||||
env = builtins.__xonsh__.env
|
||||
denv = env.detype()
|
||||
|
@ -478,7 +487,21 @@ class ForeignShellFunctionAlias(object):
|
|||
out = out.replace("\r\n", "\n")
|
||||
return out
|
||||
|
||||
def _is_streaming(self, args):
|
||||
def __repr__(self):
|
||||
return (
|
||||
self.__class__.__name__
|
||||
+ "("
|
||||
+ ", ".join(
|
||||
[
|
||||
"{k}={v!r}".format(k=k, v=v)
|
||||
for k, v in sorted(self._input_kwargs().items())
|
||||
]
|
||||
)
|
||||
+ ")"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_streaming(args):
|
||||
"""Test and modify args if --xonsh-stream is present."""
|
||||
if "--xonsh-stream" not in args:
|
||||
return args, False
|
||||
|
@ -487,6 +510,77 @@ class ForeignShellFunctionAlias(object):
|
|||
return args, True
|
||||
|
||||
|
||||
class ForeignShellFunctionAlias(ForeignShellBaseAlias):
|
||||
"""This class is responsible for calling foreign shell functions as if
|
||||
they were aliases. This does not currently support taking stdin.
|
||||
"""
|
||||
|
||||
INPUT = '{sourcer} "{filename}"\n' "{funcname} {args}\n"
|
||||
|
||||
def __init__(self, funcname, shell, filename, sourcer=None, extra_args=()):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
funcname : str
|
||||
function name
|
||||
shell : str
|
||||
Name or path to shell
|
||||
filename : str
|
||||
Where the function is defined, path to source.
|
||||
sourcer : str or None, optional
|
||||
Command to source foreign files with.
|
||||
extra_args : tuple of str, optional
|
||||
Additional command line options to pass into the shell.
|
||||
"""
|
||||
super().__init__(
|
||||
shell=shell, filename=filename, sourcer=sourcer, extra_args=extra_args
|
||||
)
|
||||
self.funcname = funcname
|
||||
|
||||
def _input_kwargs(self):
|
||||
inp = super()._input_kwargs()
|
||||
inp["funcname"] = self.funcname
|
||||
return inp
|
||||
|
||||
|
||||
class ForeignShellExecAlias(ForeignShellBaseAlias):
|
||||
"""Provides a callable alias for source code in a foreign shell."""
|
||||
|
||||
INPUT = "{src} {args}\n"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
src,
|
||||
shell,
|
||||
filename="<foreign-shell-exec-alias>",
|
||||
sourcer=None,
|
||||
extra_args=(),
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
src : str
|
||||
Source code in the shell language
|
||||
shell : str
|
||||
Name or path to shell
|
||||
filename : str
|
||||
Where the function is defined, path to source.
|
||||
sourcer : str or None, optional
|
||||
Command to source foreign files with.
|
||||
extra_args : tuple of str, optional
|
||||
Additional command line options to pass into the shell.
|
||||
"""
|
||||
super().__init__(
|
||||
shell=shell, filename=filename, sourcer=sourcer, extra_args=extra_args
|
||||
)
|
||||
self.src = src.strip()
|
||||
|
||||
def _input_kwargs(self):
|
||||
inp = super()._input_kwargs()
|
||||
inp["src"] = self.src
|
||||
return inp
|
||||
|
||||
|
||||
@lazyobject
|
||||
def VALID_SHELL_PARAMS():
|
||||
return frozenset(
|
||||
|
|
|
@ -257,7 +257,8 @@ def _pprint_displayhook(value):
|
|||
printed_val = repr(value)
|
||||
if HAS_PYGMENTS and env.get("COLOR_RESULTS"):
|
||||
tokens = list(pygments.lex(printed_val, lexer=pyghooks.XonshLexer()))
|
||||
print_color(tokens)
|
||||
end = "" if env.get("SHELL_TYPE") == "prompt_toolkit2" else "\n"
|
||||
print_color(tokens, end=end)
|
||||
else:
|
||||
print(printed_val) # black & white case
|
||||
builtins._ = value
|
||||
|
|
|
@ -194,7 +194,9 @@ def ptk_below_max_supported():
|
|||
|
||||
@functools.lru_cache(1)
|
||||
def best_shell_type():
|
||||
if ON_WINDOWS or has_prompt_toolkit():
|
||||
if builtins.__xonsh__.env.get("TERM", "") == "dumb":
|
||||
return "dumb"
|
||||
elif ON_WINDOWS or has_prompt_toolkit():
|
||||
return "prompt_toolkit"
|
||||
else:
|
||||
return "readline"
|
||||
|
|
|
@ -148,7 +148,7 @@ def ctrl_d_condition():
|
|||
empty.
|
||||
"""
|
||||
if builtins.__xonsh__.env.get("IGNOREEOF"):
|
||||
raise EOFError
|
||||
return False
|
||||
else:
|
||||
app = get_app()
|
||||
buffer_name = app.current_buffer.name
|
||||
|
|
|
@ -1431,7 +1431,7 @@ def XonshTerminal256Formatter():
|
|||
ptk_version_info()
|
||||
and ptk_version_info() > (2, 0)
|
||||
and pygments_version_info()
|
||||
and (2, 2) <= pygments_version_info() < (2, 3)
|
||||
and (2, 2, 0) <= pygments_version_info() <= (2, 3, 0)
|
||||
):
|
||||
# Monky patch pygments' dict of console codes
|
||||
# with the new color names used by PTK2
|
||||
|
|
|
@ -119,6 +119,8 @@ class Shell(object):
|
|||
shell_type_aliases = {
|
||||
"b": "best",
|
||||
"best": "best",
|
||||
"d": "dumb",
|
||||
"dumb": "dumb",
|
||||
"ptk": "prompt_toolkit",
|
||||
"ptk1": "prompt_toolkit1",
|
||||
"ptk2": "prompt_toolkit2",
|
||||
|
@ -166,6 +168,8 @@ class Shell(object):
|
|||
shell_type = self.shell_type_aliases.get(shell_type, shell_type)
|
||||
if shell_type == "best" or shell_type is None:
|
||||
shell_type = best_shell_type()
|
||||
elif env.get("TERM", "") == "dumb":
|
||||
shell_type = "dumb"
|
||||
elif shell_type == "random":
|
||||
shell_type = random.choice(("readline", "prompt_toolkit"))
|
||||
if shell_type == "prompt_toolkit":
|
||||
|
@ -195,6 +199,8 @@ class Shell(object):
|
|||
from xonsh.readline_shell import ReadlineShell as shell_class
|
||||
elif shell_type == "jupyter":
|
||||
from xonsh.jupyter_shell import JupyterShell as shell_class
|
||||
elif shell_type == "dumb":
|
||||
from xonsh.dumb_shell import DumbShell as shell_class
|
||||
else:
|
||||
raise XonshError("{} is not recognized as a shell type".format(shell_type))
|
||||
self.shell = shell_class(execer=self.execer, ctx=self.ctx, **kwargs)
|
||||
|
|
|
@ -1979,6 +1979,38 @@ def RE_COMPLETE_STRING():
|
|||
return re.compile(ptrn, re.DOTALL)
|
||||
|
||||
|
||||
def strip_simple_quotes(s):
|
||||
"""Gets rid of single quotes, double quotes, single triple quotes, and
|
||||
single double quotes from a string, if present front and back of a string.
|
||||
Otherwiswe, does nothing.
|
||||
"""
|
||||
starts_single = s.startswith("'")
|
||||
starts_double = s.startswith('"')
|
||||
if not starts_single and not starts_double:
|
||||
return s
|
||||
elif starts_single:
|
||||
ends_single = s.endswith("'")
|
||||
if not ends_single:
|
||||
return s
|
||||
elif s.startswith("'''") and s.endswith("'''") and len(s) >= 6:
|
||||
return s[3:-3]
|
||||
elif len(s) >= 2:
|
||||
return s[1:-1]
|
||||
else:
|
||||
return s
|
||||
else:
|
||||
# starts double
|
||||
ends_double = s.endswith('"')
|
||||
if not ends_double:
|
||||
return s
|
||||
elif s.startswith('"""') and s.endswith('"""') and len(s) >= 6:
|
||||
return s[3:-3]
|
||||
elif len(s) >= 2:
|
||||
return s[1:-1]
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
def check_for_partial_string(x):
|
||||
"""Returns the starting index (inclusive), ending index (exclusive), and
|
||||
starting quote string of the most recent Python string found in the input.
|
||||
|
|
|
@ -28,6 +28,11 @@
|
|||
"its behavior. To see the modifications as they are applied (in unified diff",
|
||||
"format), please set ``$XONSH_DEBUG`` to ``2`` or higher."]
|
||||
},
|
||||
{"name": "base16_shell",
|
||||
"package": "xontrib-base16-shell",
|
||||
"url": "https://github.com/ErickTucto/xontrib-base16-shell",
|
||||
"description": ["Change base16 shell themes"]
|
||||
},
|
||||
{"name": "coreutils",
|
||||
"package": "xonsh",
|
||||
"url": "http://xon.sh",
|
||||
|
|
|
@ -50,18 +50,18 @@ class VoxHandler:
|
|||
help='Activate virtual environment'
|
||||
)
|
||||
activate.add_argument('name', metavar='ENV',
|
||||
help='The environment to activate. ENV can be '+
|
||||
'either a name from the venvs shown by '+
|
||||
'vox list or the path to an arbitrary venv')
|
||||
help=('The environment to activate. ENV can be '
|
||||
'either a name from the venvs shown by vox'
|
||||
'list or the path to an arbitrary venv'))
|
||||
subparsers.add_parser('deactivate', aliases=['exit'], help='Deactivate current virtual environment')
|
||||
subparsers.add_parser('list', aliases=['ls'],
|
||||
help='List environments available in '+
|
||||
'$VIRTUALENV_HOME')
|
||||
help=('List environments available in '
|
||||
'$VIRTUALENV_HOME'))
|
||||
remove = subparsers.add_parser('remove', aliases=['rm', 'delete', 'del'], help='Remove virtual environment')
|
||||
remove.add_argument('names', metavar='ENV', nargs='+',
|
||||
help='The environments to remove. ENV can be '+
|
||||
'either a name from the venvs shown by '+
|
||||
'vox list or the path to an arbitrary venv')
|
||||
help=('The environments to remove. ENV can be '
|
||||
'either a name from the venvs shown by vox'
|
||||
'list or the path to an arbitrary venv'))
|
||||
subparsers.add_parser('help', help='Show this help message')
|
||||
return parser
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue