Merge branch 'master' into update_docs

This commit is contained in:
Gil Forsyth 2016-04-06 18:36:27 -04:00
commit da13eb858d
41 changed files with 1042 additions and 357 deletions

View file

@ -1,4 +1,4 @@
version: 0.2.6.{build}
version: 0.2.7.{build}
os: Windows Server 2012 R2
install:
- C:\Python34\Scripts\pip install ply pyreadline nose pygments prompt_toolkit

4
.gitattributes vendored Normal file
View file

@ -0,0 +1,4 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
tests/histories/*.json text eol=lf

3
.gitignore vendored
View file

@ -33,3 +33,6 @@ include/
# Mac
.DS_Store
# Editor project files
*.komodo*

View file

@ -3,6 +3,6 @@ python:
- 3.4
- 3.5
install:
- pip install ply nose pygments prompt_toolkit
- pip install -r requirements-tests.txt
script:
- nosetests -q

View file

@ -6,6 +6,48 @@ Current Developments
====================
**Added:**
* Added a new shell type ``'none'``, used to avoid importing ``readline`` or
``prompt_toolkit`` when running scripts or running a single command.
* New: `sudo` functionality on Windows through an alias
* Automatically enhance colors for readability in the default terminal (cmd.exe)
on Windows. This functionality can be enabled/disabled with the
$INTENSIFY_COLORS_ON_WIN environment variable.
* Added ``Ellipsis`` lookup to ``__xonsh_env__`` to allow environment variable checks, e.g. ``'HOME' in ${...}``
* Added an option to update ``os.environ`` every time the xonsh environment changes.
This disabled by default, but can be enabled by setting ``$UPDATE_OS_ENVIRON`` to
True.
**Changed:**
* Running scripts through xonsh (or running a single command with ``-c``) no
longer runs the user's rc file, unless the ``--login`` option is specified.
Also avoids loading aliases and environments from foreign shells, as well as
loading bash completions.
* rc files are now compiled and cached, to avoid re-parsing when they haven't
changed.
* Left and Right arrows in the ``prompt_toolkit`` shell now wrap in multiline
environments
* Regexpath matching with backticks, now returns an empty list in python mode.
**Deprecated:** None
**Removed:** None
**Fixed:**
* Fixed bug with loading prompt-toolkit shell < v0.57.
* Fixed bug with prompt-toolkit completion when the cursor is not at the end of the line
**Security:** None
v0.2.7
====================
**Added:**
* Added new valid ``$SHELL_TYPE`` called ``'best'``. This selects the best value
for the concrete shell type based on the availability on the user's machine.
* New environment variable ``$XONSH_COLOR_STYLE`` will set the color mapping
@ -47,7 +89,7 @@ Current Developments
``re.match``, and the result of an empty regex glob does not cause the
argument to be deleted.
**Deprecated:** None
**Removed:**
@ -58,11 +100,12 @@ Current Developments
**Fixed:**
* Multidimensional slicing, as in numpy, no longer throws SyntaxErrors.
* Some minor zsh fixes for more platforms and setups.
* The ``BaseShell.settitle`` method no longer has its commands captured by
``$(...)``
**Security:** None
v0.2.6
====================

View file

@ -178,6 +178,14 @@ The following aliases on Windows are expanded to ``['cmd', '/c', alias]``:
On Windows, ``which`` is aliased to ``['where']``.
``sudo`` on Windows
====================
On Windows, if no executables named ``sudo`` are found, Xonsh adds a ``sudo`` alias
that poly fills the "run as Admin" behavior with the help of ``ShellExecuteEx`` and
``ctypes``. It doesn't support any actual ``sudo`` parameters and just takes the
command to run.
``ls``
====================
The ``ls`` command is aliased to ``['ls', '--color=auto', '-v']`` normally. On Mac OSX

View file

@ -74,10 +74,34 @@ If you want to lint the entire code base run::
How to Test
================
First, install nose: http://nose.readthedocs.org/en/latest/. Second, ensure
your cwd is the root directory of the project (i.e., the one containing the
Ensure your cwd is the root directory of the project (i.e., the one containing the
.git directory).
----------------------------------
Dependencies
----------------------------------
Prep your environment for running the tests::
$ pip install requirements-tests.txt
----------------------------------
Running the Tests - Basic
----------------------------------
Run all the tests using Nose::
$ nosetests -q
Use "-q" to keep nose from outputing a dot for every test. There are A LOT of tests
and you will waste time waiting for all the dots to get pushed through stdout.
----------------------------------
Running the Tests - Advanced
----------------------------------
To perform all unit tests::
$ scripts/run_tests.xsh all

View file

@ -1,11 +1,11 @@
the xonsh shell
===============
.. raw:: html
.. raw:: html
<p style="text-align:center;">
<span style="font-family:Times;font-size:28px;font-style:normal;font-weight:normal;text-decoration:none;text-transform:none;font-variant:small-caps;color:000000;">
~
~
<script>
var taglines = [
"Exofrills in the shell",
@ -23,7 +23,12 @@ the xonsh shell
"It is pronounced <i>quanxh</i>",
"It is pronounced <i>zonsch</i>",
"It is pronounced <i>jeaunsch</i>",
"It is pronounced <i>🐚</i>",
"It is pronounced <i>măjˈĭk</i>",
"It is pronounced <i>gif</i>",
"It is pronounced <i>Honshu</i>",
"It is pronounced <i>soonish</i>",
"It is pronounced <i>vixenish</i>",
"The shell, bourne again",
"Snailed it",
"Starfish loves you",
@ -51,16 +56,16 @@ the xonsh shell
Xonsh is a Python-ish, BASHwards-looking shell language and command prompt.
The language is a superset of Python 3.4+ with additional shell primitives
that you are used to from Bash and IPython. It works on all major systems including
Linux, Mac OSX, and Windows. Xonsh is meant for the daily use of experts and novices
Linux, Mac OSX, and Windows. Xonsh is meant for the daily use of experts and novices
alike.
**At a glance**
.. raw:: html
.. raw:: html
<p style="text-align:center;"><script type="text/javascript"
src="https://asciinema.org/a/9kp21a67ahhng25wtzoep3pyx.js"
id="asciicast-9kp21a67ahhng25wtzoep3pyx" async
<p style="text-align:center;"><script type="text/javascript"
src="https://asciinema.org/a/9kp21a67ahhng25wtzoep3pyx.js"
id="asciicast-9kp21a67ahhng25wtzoep3pyx" async
data-size="medium" data-speed="2"></script>
</p>
@ -111,19 +116,18 @@ Contents
faq
todo
==========
Comparison
==========
Xonsh is significantly different from most other shells or shell tools. The following
table lists features and capabilities that various tools may or may not share.
.. list-table::
.. list-table::
:widths: 3 1 1 1 1 1 1
:header-rows: 1
:stub-columns: 1
* -
* -
- Bash
- zsh
- plumbum
@ -131,8 +135,8 @@ table lists features and capabilities that various tools may or may not share.
- IPython
- xonsh
* - Sane language
-
-
-
-
- ✓
- ✓
- ✓
@ -142,11 +146,11 @@ table lists features and capabilities that various tools may or may not share.
- ✓
- ✓
- ✓
-
-
- ✓
* - Native cross-platform support
-
-
-
-
- ✓
- ✓
- ✓
@ -154,58 +158,58 @@ table lists features and capabilities that various tools may or may not share.
* - Meant as a shell
- ✓
- ✓
-
-
- ✓
-
-
- ✓
* - Tab completion
- ✓
- ✓
-
-
- ✓
- ✓
- ✓
* - Man-page completion
-
-
-
-
-
-
- ✓
-
-
- ✓
* - Large standard library
-
-
- ✓
-
-
-
-
- ✓
- ✓
* - Typed variables
-
-
-
-
- ✓
- ✓
- ✓
- ✓
* - Syntax highlighting
-
-
-
-
-
-
- ✓
- in notebook
- w/ prompt-toolkit
* - Pun in name
- ✓
-
-
- ✓
-
-
-
-
- ✓
* - Rich history
-
-
-
-
-
-
-
-
-
-
- ✓
@ -232,17 +236,17 @@ Xonsh currently has the following external dependencies,
============
Contributing
============
We highly encourage contributions to xonsh! If you would like to contribute,
it is as easy as forking the repository on GitHub, making your changes, and
issuing a pull request. If you have any questions about this process don't
hesitate to ask the mailing list (xonsh@googlegroups.com).
We highly encourage contributions to xonsh! If you would like to contribute,
it is as easy as forking the repository on GitHub, making your changes, and
issuing a pull request. If you have any questions about this process don't
hesitate to ask the mailing list (xonsh@googlegroups.com).
==========
Contact Us
==========
If you have questions or comments, please send them to the mailing list
xonsh@googlegroups.com, page us on IRC, contact the author directly, or
open an issue on GitHub.
open an issue on GitHub.
`Join the mailing list here! <https://groups.google.com/forum/#!forum/xonsh>`_
=============

View file

@ -134,9 +134,31 @@ variable in Python. The same is true for deleting them too.
Become the Lord of the Files
>>> del $GOAL
Very nice. All environment variables live in the built-in
``__xonsh_env__`` mapping. You can access this mapping directly, but in most
situations, you shouldn't need to.
Very nice.
__xonsh_env__
--------------
All environment variables live in the built-in ``__xonsh_env__`` mapping. You can access this
mapping directly, but in most situations, you shouldn't need to.
One helpful method on the __xonsh_env__ is :func:`~xonsh.environ.Env.swap`. It can be used to temporarily set an
environment variable:
.. code-block:: xonshcon
>>> with __xonsh_env__.swap(SOMEVAR='foo'):
... echo $SOMEVAR
...
...
foo
>>> echo $SOMEVAR
>>>
Environment Types
-----------------
Like other variables in Python, environment variables have a type. Sometimes
this type is imposed based on the variable name. The current rules are pretty
@ -197,6 +219,14 @@ examples in action:
Not bad, xonsh, not bad.
If you want to check if an environment variable is present in your current
session (say, in your awesome new ``xonsh`` script) you can pass an Ellipsis to
the ``${}`` operator:
.. code-block:: xonshcon
>>> 'HOME' in ${...}
True
Running Commands
==============================

View file

@ -1,123 +1,124 @@
==========================
Windows Guide
==========================
Installation
================
The easy way
----------------
The easiest way to install xonsh on windows is through the `Anaconda Python Distribution`_ and the conda package manager.
.. note:: Be sure to install the version with Python3.4 or later. Xonsh is not supported on legacy Python (2.7).
Install xonsh with the following command:
.. code-block:: bat
> conda install xonsh --channel xonsh
This will install xonsh and all the recommended dependencies. Next, run xonsh:
.. code-block:: bat
> xonsh
snail@home ~ $
Install from source
-------------------
To install xonsh from source on Windows, first install `Python v3.4+`_ from
http://python.org. Remember to select "Add python to PATH" during installation.
Next, install the prompt_toolkit dependency via ``pip``:
.. code-block:: bat
> pip install prompt-toolkit
While prompt-toolkit is considered an optional dependency, it is the
recommended alternative to pyreadline for Windows users. For Windows,
it is recommended to use a replacement console emulator. Good choices are `cmder`_ or `conemu`_.
Download the latest `xonsh-master.zip`_ from github and unzip it
to ``xonsh-master``.
Now install xonsh:
.. code-block:: bat
> cd xonsh-master
> python setup.py install
Next, run xonsh:
.. code-block:: bat
> xonsh
snail@home ~ $
.. _Python v3.4+: https://www.python.org/downloads/windows/
.. _xonsh-master.zip: https://github.com/scopatz/xonsh/archive/master.zip
.. _cmder: http://cmder.net/
.. _conemu: https://conemu.github.io/
.. _Anaconda Python Distribution: https://www.continuum.io/downloads#_windows
.. include:: dependencies.rst
Usage
================
Name space conflicts
--------------------
Due to ambiguity with the Python ``dir`` builtin, to list the current
directory via the ``cmd.exe`` builtin you must explicitly request
the ``.``, like this:
.. code-block:: xonshcon
>>> dir .
Volume in drive C is Windows
Volume Serial Number is 30E8-8B86
Directory of C:\Users\snail\xonsh
2015-05-12 03:04 <DIR> .
2015-05-12 03:04 <DIR> ..
2015-05-01 01:31 <DIR> xonsh
0 File(s) 0 bytes
3 Dir(s) 11,008,000,000 bytes free
Many people create a ``d`` alias for the ``dir`` command to save
typing and avoid the ambiguity altogether:
.. code-block:: xonshcon
>>> aliases['d'] = ['cmd', '/c', 'dir']
You can add aliases to your ``~/.xonshrc`` to have it always
available when xonsh starts.
Unicode support for Windows
----------------------------
Python's utf-8 unicode is not compatible with the default shell 'cmd.exe' on Windows. The package ``win_unicode_console`` fixes this. Xonsh will use ``win_unicode_console`` if it is installed. This can be disabled/enabled with the ``$WIN_UNICODE_CONSOLE``` environment variable.
.. note:: Even with unicode support enabled the symbols available will depend on the font used in cmd.exe.
The packages ``win_unicode_console`` can be installed using pip or conda.
.. code-block:: bat
> pip install win_unicode_console
.. code-block:: bat
> conda install --channel xonsh win_unicode_console
==========================
Windows Guide
==========================
Installation
================
The easy way
----------------
The easiest way to install xonsh on windows is through the Anaconda Python
Distribution and the conda package manager.
.. note::
Be sure to install the version with Python3.4 or later.
Xonsh is not yet supported on legacy Python (2.7).
Install xonsh with the following command:
.. code-block:: bat
> conda install xonsh --channel xonsh
This will install xonsh and all the recommended dependencies. Next, run xonsh:
.. code-block:: bat
> xonsh
snail@home ~ $
Install from source
-------------------
To install xonsh from source on Windows, first install `Python v3.4+`_ from
http://python.org. Remember to select "Add python to PATH" during installation.
Next, install the prompt_toolkit dependency via ``pip``:
.. code-block:: bat
> pip install prompt-toolkit
While prompt-toolkit is considered an optional dependency, it is the
recommended alternative to pyreadline for Windows users. For Windows,
it is recommended to use a replacement console emulator. Good choices are `cmder`_ or `conemu`_.
Download the latest `xonsh-master.zip`_ from github and unzip it
to ``xonsh-master``.
Now install xonsh:
.. code-block:: bat
> cd xonsh-master
> python setup.py install
Next, run xonsh:
.. code-block:: bat
> xonsh
snail@home ~ $
.. _Python v3.4+: https://www.python.org/downloads/windows/
.. _xonsh-master.zip: https://github.com/scopatz/xonsh/archive/master.zip
.. _cmder: http://cmder.net/
.. _conemu: https://conemu.github.io/
Usage
================
Name space conflicts
--------------------
Due to ambiguity with the Python ``dir`` builtin, to list the current
directory via the ``cmd.exe`` builtin you must explicitly request
the ``.``, like this:
.. code-block:: xonshcon
>>> dir .
Volume in drive C is Windows
Volume Serial Number is 30E8-8B86
Directory of C:\Users\snail\xonsh
2015-05-12 03:04 <DIR> .
2015-05-12 03:04 <DIR> ..
2015-05-01 01:31 <DIR> xonsh
0 File(s) 0 bytes
3 Dir(s) 11,008,000,000 bytes free
Many people create a ``d`` alias for the ``dir`` command to save
typing and avoid the ambiguity altogether:
.. code-block:: xonshcon
>>> aliases['d'] = ['cmd', '/c', 'dir']
You can add aliases to your ``~/.xonshrc`` to have it always
available when xonsh starts.
Unicode support for Windows
----------------------------
Python's utf-8 unicode is not compatible with the default shell 'cmd.exe' on Windows. The package ``win_unicode_console`` fixes this. Xonsh will use ``win_unicode_console`` if it is installed. This can be disabled/enabled with the ``$WIN_UNICODE_CONSOLE``` environment variable.
.. note:: Even with unicode support enabled the symbols available will depend on the font used in cmd.exe.
The packages ``win_unicode_console`` can be installed using pip or conda.
.. code-block:: bat
> pip install win_unicode_console
.. code-block:: bat
> conda install --channel xonsh win_unicode_console

View file

@ -1,10 +1,10 @@
Run Control File
=========================
Xonsh allows you to have run control files to customize your shell behavior. These are called ``xonshrc`` files.
Xonsh allows you to have run control files to customize your shell behavior. These are called ``xonshrc`` files.
The system-wide ``xonshrc`` file controls options that are applied to all users of Xonsh on a given system. You can create this file in ``/etc/xonshrc`` for Linux and OSX and in ``%ALLUSERSPROFILE%\xonsh\xonshrc`` on Windows.
The system-wide ``xonshrc`` file controls options that are applied to all users of Xonsh on a given system. You can create this file in ``/etc/xonshrc`` for Linux and OSX and in ``%ALLUSERSPROFILE%\xonsh\xonshrc`` on Windows.
Xonsh also allows you to have a run control file in your home directory called ``~/.xonshrc``. The options set in the local ``xonshrc`` only apply to the current user and will override any conflicting settings set in the system-wide control file.
Xonsh also allows you to have a run control file in your home directory called ``~/.xonshrc``. The options set in the local ``xonshrc`` only apply to the current user and will override any conflicting settings set in the system-wide control file.
These files are written in the xonsh language, of course. They are executed exactly once
at startup. The following is a real-world example of such a file.
@ -18,16 +18,16 @@ at startup. The following is a real-world example of such a file.
Snippets for xonshrc
=========================
The following are usefull snippets and code that tweaks and adjust xonsh in various ways.
If you have any useful tricks, feel free to share them.
If you have any useful tricks, feel free to share them.
Adjust how git branch label behaves
---------------------------------
Xonsh adds a colored branch name to the prompt when working with git or hg repositories.
This behavior can be controlled with the ``$PROMPT`` environment variable. See how to `customize the prompt`_ .
The branch name changes color if the work dir is dirty or not. This is controlled by the ``{branch_color}`` formatter string.
-------------------------------------------
Xonsh adds a colored branch name to the prompt when working with git or hg repositories.
This behavior can be controlled with the ``$PROMPT`` environment variable. See how to `customize the prompt`_ .
The branch name changes color if the work dir is dirty or not. This is controlled by the ``{branch_color}`` formatter string.
The following snippet reimplements the formatter also to include untracked files when considering if a git directory is dirty.
The following snippet reimplements the formatter also to include untracked files when considering if a git directory is dirty.
.. code-block:: xonshcon
@ -36,13 +36,13 @@ The following snippet reimplements the formatter also to include untracked files
if git_dirty_working_directory(include_untracked=True)
else '{BOLD_INTENSE_GREEN}')
.. _customize the prompt: http://xon.sh/tutorial.html#customizing-the-prompt
Get better colors from the ``ls`` command
----------------------------------------------
The colors of the ``ls`` command may be hard to read in a dark terminal. If so, this is an excellent addition to the xonshrc file.
The colors of the ``ls`` command may be hard to read in a dark terminal. If so, this is an excellent addition to the xonshrc file.
.. code-block:: xonshcon

4
requirements-tests.txt Normal file
View file

@ -0,0 +1,4 @@
ply
nose
prompt-toolkit
pygments

View file

@ -72,5 +72,35 @@ def test_HISTCONTROL():
assert_true('ignoreerr' in env['HISTCONTROL'])
assert_true('ignoredups' in env['HISTCONTROL'])
def test_swap():
env = Env(VAR='wakka')
assert_equal(env['VAR'], 'wakka')
# positional arg
with env.swap({'VAR': 'foo'}):
assert_equal(env['VAR'], 'foo')
# make sure the environment goes back outside the context manager
assert_equal(env['VAR'], 'wakka')
# kwargs only
with env.swap(VAR1='foo', VAR2='bar'):
assert_equal(env['VAR1'], 'foo')
assert_equal(env['VAR2'], 'bar')
# positional and kwargs
with env.swap({'VAR3': 'baz'}, VAR1='foo', VAR2='bar'):
assert_equal(env['VAR1'], 'foo')
assert_equal(env['VAR2'], 'bar')
assert_equal(env['VAR3'], 'baz')
# make sure the environment goes back outside the context manager
assert_equal(env['VAR'], 'wakka')
assert 'VAR1' not in env
assert 'VAR2' not in env
assert 'VAR3' not in env
if __name__ == '__main__':
nose.runmodule()

View file

@ -19,6 +19,14 @@ def test_login_shell():
with patch('xonsh.main.Shell', Shell), mock_xonsh_env({}):
xonsh.main.premain([])
assert_true(builtins.__xonsh_env__.get('XONSH_LOGIN'))
with patch('xonsh.main.Shell', Shell), mock_xonsh_env({}):
xonsh.main.premain(['-l', '-c', 'echo "hi"'])
assert_true(builtins.__xonsh_env__.get('XONSH_LOGIN'))
with patch('xonsh.main.Shell', Shell), mock_xonsh_env({}):
xonsh.main.premain(['-c', 'echo "hi"'])
assert_false(builtins.__xonsh_env__.get('XONSH_LOGIN'))
with patch('xonsh.main.Shell', Shell), mock_xonsh_env({}):

View file

@ -58,7 +58,7 @@ def assert_nodes_equal(x, y, include_attributes=True):
print(pdump(x, include_attributes=include_attributes), '\n')
print('y:\n==')
print(pdump(y, include_attributes=include_attributes), '\n')
assert_equal(pdump(x, include_attributes=include_attributes),
assert_equal(pdump(x, include_attributes=include_attributes),
pdump(y, include_attributes=include_attributes))
def check_ast(inp, run=True, mode='eval'):
@ -310,6 +310,81 @@ def test_str_slice_lower_other():
def test_str_slice_upper_other():
yield check_ast, '"hello"[3::2]'
def test_str_2slice():
yield check_ast, '"hello"[0:3,0:3]', False
def test_str_2step():
yield check_ast, '"hello"[0:3:1,0:4:2]', False
def test_str_2slice_all():
yield check_ast, '"hello"[:,:]', False
def test_str_2slice_upper():
yield check_ast, '"hello"[5:,5:]', False
def test_str_2slice_lower():
yield check_ast, '"hello"[:3,:3]', False
def test_str_2slice_lowerupper():
yield check_ast, '"hello"[5:,:3]', False
def test_str_2slice_other():
yield check_ast, '"hello"[::2,::2]', False
def test_str_2slice_lower_other():
yield check_ast, '"hello"[:3:2,:3:2]', False
def test_str_2slice_upper_other():
yield check_ast, '"hello"[3::2,3::2]', False
def test_str_3slice():
yield check_ast, '"hello"[0:3,0:3,0:3]', False
def test_str_3step():
yield check_ast, '"hello"[0:3:1,0:4:2,1:3:2]', False
def test_str_3slice_all():
yield check_ast, '"hello"[:,:,:]', False
def test_str_3slice_upper():
yield check_ast, '"hello"[5:,5:,5:]', False
def test_str_3slice_lower():
yield check_ast, '"hello"[:3,:3,:3]', False
def test_str_3slice_lowerlowerupper():
yield check_ast, '"hello"[:3,:3,:3]', False
def test_str_3slice_lowerupperlower():
yield check_ast, '"hello"[:3,5:,:3]', False
def test_str_3slice_lowerupperupper():
yield check_ast, '"hello"[:3,5:,5:]', False
def test_str_3slice_upperlowerlower():
yield check_ast, '"hello"[5:,5:,:3]', False
def test_str_3slice_upperlowerupper():
yield check_ast, '"hello"[5:,:3,5:]', False
def test_str_3slice_upperupperlower():
yield check_ast, '"hello"[5:,5:,:3]', False
def test_str_3slice_other():
yield check_ast, '"hello"[::2,::2,::2]', False
def test_str_3slice_lower_other():
yield check_ast, '"hello"[:3:2,:3:2,:3:2]', False
def test_str_3slice_upper_other():
yield check_ast, '"hello"[3::2,3::2,3::2]', False
def test_str_slice_true():
yield check_ast, '"hello"[0:3,True]', False
def test_str_true_slice():
yield check_ast, '"hello"[True,0:3]', False
def test_list_empty():
yield check_ast, '[]'
@ -1499,6 +1574,36 @@ def test_ls_envvar_strval():
def test_ls_envvar_listval():
yield check_xonsh_ast, {'WAKKA': ['.', '.']}, '$(ls $WAKKA)', False
def test_bang_sub():
yield check_xonsh_ast, {}, '!(ls)', False
def test_bang_sub_space():
yield check_xonsh_ast, {}, '!(ls )', False
def test_bang_ls_dot():
yield check_xonsh_ast, {}, '!(ls .)', False
def test_bang_ls_dot_nesting():
yield check_xonsh_ast, {}, '!(ls @(None or "."))', False
def test_bang_ls_dot_nesting_var():
yield check_xonsh, {}, 'x = "."; !(ls @(None or x))', False
def test_bang_ls_dot_str():
yield check_xonsh_ast, {}, '!(ls ".")', False
def test_bang_ls_nest_ls():
yield check_xonsh_ast, {}, '!(ls $(ls))', False
def test_bang_ls_nest_ls_dashl():
yield check_xonsh_ast, {}, '!(ls $(ls) -l)', False
def test_bang_ls_envvar_strval():
yield check_xonsh_ast, {'WAKKA': '.'}, '!(ls $WAKKA)', False
def test_bang_ls_envvar_listval():
yield check_xonsh_ast, {'WAKKA': ['.', '.']}, '!(ls $WAKKA)', False
def test_question():
yield check_xonsh_ast, {}, 'range?'
@ -1517,6 +1622,50 @@ def test_backtick():
def test_uncaptured_sub():
yield check_xonsh_ast, {}, '$[ls]', False
def test_hiddenobj_sub():
yield check_xonsh_ast, {}, '![ls]', False
def test_bang_two_cmds_one_pipe():
yield check_xonsh_ast, {}, '!(ls | grep wakka)', False
def test_bang_three_cmds_two_pipes():
yield check_xonsh_ast, {}, '!(ls | grep wakka | grep jawaka)', False
def test_bang_one_cmd_write():
yield check_xonsh_ast, {}, '!(ls > x.py)', False
def test_bang_one_cmd_append():
yield check_xonsh_ast, {}, '!(ls >> x.py)', False
def test_bang_two_cmds_write():
yield check_xonsh_ast, {}, '!(ls | grep wakka > x.py)', False
def test_bang_two_cmds_append():
yield check_xonsh_ast, {}, '!(ls | grep wakka >> x.py)', False
def test_bang_cmd_background():
yield check_xonsh_ast, {}, '!(emacs ugggh &)', False
def test_bang_cmd_background_nospace():
yield check_xonsh_ast, {}, '!(emacs ugggh&)', False
def test_bang_git_quotes_no_space():
yield check_xonsh_ast, {}, '![git commit -am "wakka"]', False
def test_bang_git_quotes_space():
yield check_xonsh_ast, {}, '![git commit -am "wakka jawaka"]', False
def test_bang_git_two_quotes_space():
yield check_xonsh, {}, ('![git commit -am "wakka jawaka"]\n'
'![git commit -am "flock jawaka"]\n'), False
def test_bang_git_two_quotes_space_space():
yield check_xonsh, {}, ('![git commit -am "wakka jawaka" ]\n'
'![git commit -am "flock jawaka milwaka" ]\n'), False
def test_bang_ls_quotes_3_space():
yield check_xonsh_ast, {}, '![ls "wakka jawaka baraka"]', False
def test_two_cmds_one_pipe():
yield check_xonsh_ast, {}, '$(ls | grep wakka)', False

View file

@ -1 +1 @@
__version__ = '0.2.6'
__version__ = '0.2.7'

View file

@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
"""Aliases for the xonsh shell."""
import os
import shlex
import builtins
import subprocess
from warnings import warn
import os
from argparse import ArgumentParser
from xonsh.dirstack import cd, pushd, popd, dirs
from xonsh.dirstack import cd, pushd, popd, dirs, _get_cwd
from xonsh.jobs import jobs, fg, bg, kill_all_jobs
from xonsh.proc import foreground
from xonsh.timings import timeit_alias
@ -223,7 +221,7 @@ DEFAULT_ALIASES = {
'timeit': timeit_alias,
'xonfig': xonfig,
'scp-resume': ['rsync', '--partial', '-h', '--progress', '--rsh=ssh'],
'ipynb': ['ipython', 'notebook', '--no-browser'],
'ipynb': ['jupyter', 'notebook', '--no-browser'],
'vox': vox,
}
@ -253,6 +251,25 @@ if ON_WINDOWS:
DEFAULT_ALIASES['which'] = ['where']
if not locate_binary('sudo'):
import xonsh.winutils as winutils
def sudo(args, sdin=None):
if len(args) < 1:
print('You need to provide an executable to run as Administrator.')
return
cmd = args[0]
if locate_binary(cmd):
return winutils.sudo(cmd, args[1:])
elif cmd.lower() in WINDOWS_CMD_ALIASES:
return winutils.sudo('cmd', ['/D', '/C', 'CD', _get_cwd(), '&&'] + args)
else:
print('Cannot find the path for executable "{0}".'.format(cmd))
DEFAULT_ALIASES['sudo'] = sudo
elif ON_MAC:
DEFAULT_ALIASES['ls'] = ['ls', '-G']
else:

View file

@ -5,7 +5,7 @@
from ast import Module, Num, Expr, Str, Bytes, UnaryOp, UAdd, USub, Invert, \
BinOp, Add, Sub, Mult, Div, FloorDiv, Mod, Pow, Compare, Lt, Gt, \
LtE, GtE, Eq, NotEq, In, NotIn, Is, IsNot, Not, BoolOp, Or, And, \
Subscript, Load, Slice, List, Tuple, Set, Dict, AST, NameConstant, \
Subscript, Load, Slice, ExtSlice, List, Tuple, Set, Dict, AST, NameConstant, \
Name, GeneratorExp, Store, comprehension, ListComp, SetComp, DictComp, \
Assign, AugAssign, BitXor, BitAnd, BitOr, LShift, RShift, Assert, Delete, \
Del, Pass, Raise, Import, alias, ImportFrom, Continue, Break, Yield, \

View file

@ -108,10 +108,11 @@ class BaseShell(object):
"""The xonsh shell."""
def __init__(self, execer, ctx, **kwargs):
super().__init__(**kwargs)
super().__init__()
self.execer = execer
self.ctx = ctx
self.completer = Completer()
if kwargs.get('completer', True):
self.completer = Completer()
self.buffer = []
self.need_more_lines = False
self.mlprompt = None

View file

@ -25,7 +25,6 @@ from xonsh.tools import (
)
from xonsh.inspectors import Inspector
from xonsh.environ import Env, default_env, locate_binary
from xonsh.aliases import DEFAULT_ALIASES
from xonsh.jobs import add_job, wait_for_active_job
from xonsh.proc import (ProcProxy, SimpleProcProxy, ForegroundProcProxy,
SimpleForegroundProcProxy, TeePTYProc,
@ -267,13 +266,14 @@ def reglob(path, parts=None, i=None):
return paths
def regexpath(s):
def regexpath(s, pymode=False):
"""Takes a regular expression string and returns a list of file
paths that match the regex.
"""
s = expand_path(s)
o = reglob(s)
return o if len(o) != 0 else [s]
no_match = [] if pymode else [s]
return o if len(o) != 0 else no_match
def globpath(s, ignore_case=False):
@ -497,6 +497,7 @@ def run_subproc(cmds, captured=False):
prev_proc = None
_capture_streams = captured in {'stdout', 'object'}
for ix, cmd in enumerate(cmds):
starttime = time.time()
procinfo['args'] = list(cmd)
stdin = None
stderr = None
@ -674,6 +675,7 @@ def run_subproc(cmds, captured=False):
procinfo['stderr'] = errout
if (not prev_is_proxy and
hist.last_cmd_rtn is not None and
hist.last_cmd_rtn > 0 and
ENV.get('RAISE_SUBPROC_ERROR')):
raise CalledProcessError(hist.last_cmd_rtn, aliased_cmd, output=output)
@ -682,6 +684,7 @@ def run_subproc(cmds, captured=False):
elif captured is not False:
procinfo['pid'] = prev_proc.pid
procinfo['returncode'] = prev_proc.returncode
procinfo['timestamp'] = (starttime, time.time())
if captured == 'object':
procinfo['stdout'] = output
if _stdin_file is not None:
@ -734,13 +737,13 @@ def ensure_list_of_strs(x):
return rtn
def load_builtins(execer=None, config=None):
def load_builtins(execer=None, config=None, login=False):
"""Loads the xonsh builtins into the Python builtins. Sets the
BUILTINS_LOADED variable to True.
"""
global BUILTINS_LOADED, ENV
# private built-ins
builtins.__xonsh_env__ = ENV = Env(default_env(config=config))
builtins.__xonsh_env__ = ENV = Env(default_env(config=config, login=login))
builtins.__xonsh_ctx__ = {}
builtins.__xonsh_help__ = helper
builtins.__xonsh_superhelp__ = superhelper
@ -769,8 +772,12 @@ def load_builtins(execer=None, config=None):
builtins.evalx = None if execer is None else execer.eval
builtins.execx = None if execer is None else execer.exec
builtins.compilex = None if execer is None else execer.compile
# Need this inline/lazy import here since we use locate_binary that relies on __xonsh_env__ in default aliases
from xonsh.aliases import DEFAULT_ALIASES
builtins.default_aliases = builtins.aliases = Aliases(DEFAULT_ALIASES)
builtins.aliases.update(load_foreign_aliases(issue_warning=False))
if login:
builtins.aliases.update(load_foreign_aliases(issue_warning=False))
# history needs to be started after env and aliases
# would be nice to actually include non-detyped versions.
builtins.__xonsh_history__ = History(env=ENV.detype(),

View file

@ -5,7 +5,7 @@ from itertools import zip_longest
from difflib import SequenceMatcher
from xonsh import lazyjson
from xonsh.tools import print_color, format_color
from xonsh.tools import print_color
NO_COLOR = '{NO_COLOR}'
RED = '{RED}'

View file

@ -2,10 +2,12 @@
"""Environment for the xonsh shell."""
import os
import re
import sys
import json
import socket
import string
import locale
import marshal
import builtins
import subprocess
from itertools import chain
@ -23,7 +25,8 @@ from xonsh.tools import (
history_tuple_to_str, is_float, string_types, is_string, DEFAULT_ENCODING,
is_completions_display_value, to_completions_display_value, is_string_set,
csv_to_set, set_to_csv, get_sep, is_int, is_bool_seq, csv_to_bool_seq,
bool_seq_to_csv, DefaultNotGiven, setup_win_unicode_console
bool_seq_to_csv, DefaultNotGiven, setup_win_unicode_console,
intensify_colors_on_win_setter
)
from xonsh.dirstack import _get_cwd
from xonsh.foreign_shells import DEFAULT_SHELLS, load_foreign_envs
@ -67,6 +70,7 @@ DEFAULT_ENSURERS = {
'FORCE_POSIX_PATHS': (is_bool, to_bool, bool_to_str),
'HISTCONTROL': (is_string_set, csv_to_set, set_to_csv),
'IGNOREEOF': (is_bool, to_bool, bool_to_str),
'INTENSIFY_COLORS_ON_WIN':(always_false, intensify_colors_on_win_setter, bool_to_str),
'LC_COLLATE': (always_false, locale_convert('LC_COLLATE'), ensure_string),
'LC_CTYPE': (always_false, locale_convert('LC_CTYPE'), ensure_string),
'LC_MESSAGES': (always_false, locale_convert('LC_MESSAGES'), ensure_string),
@ -81,6 +85,7 @@ DEFAULT_ENSURERS = {
'RAISE_SUBPROC_ERROR': (is_bool, to_bool, bool_to_str),
'RIGHT_PROMPT': (is_string, ensure_string, ensure_string),
'TEEPTY_PIPE_DELAY': (is_float, float, str),
'UPDATE_OS_ENVIRON': (is_bool, to_bool, bool_to_str),
'XONSHRC': (is_env_path, str_to_env_path, env_path_to_str),
'XONSH_COLOR_STYLE': (is_string, ensure_string, ensure_string),
'XONSH_ENCODING': (is_string, ensure_string, ensure_string),
@ -167,6 +172,7 @@ DEFAULT_VALUES = {
'HISTCONTROL': set(),
'IGNOREEOF': False,
'INDENT': ' ',
'INTENSIFY_COLORS_ON_WIN': True,
'LC_CTYPE': locale.setlocale(locale.LC_CTYPE),
'LC_COLLATE': locale.setlocale(locale.LC_COLLATE),
'LC_TIME': locale.setlocale(locale.LC_TIME),
@ -179,8 +185,6 @@ DEFAULT_VALUES = {
'PATH': (),
'PATHEXT': (),
'PROMPT': DEFAULT_PROMPT,
'PROMPT_TOOLKIT_COLORS': {},
'PROMPT_TOOLKIT_STYLES': None,
'PUSHD_MINUS': False,
'PUSHD_SILENT': False,
'RAISE_SUBPROC_ERROR': False,
@ -191,6 +195,7 @@ DEFAULT_VALUES = {
'SUGGEST_THRESHOLD': 3,
'TEEPTY_PIPE_DELAY': 0.01,
'TITLE': DEFAULT_TITLE,
'UPDATE_OS_ENVIRON': False,
'VI_MODE': False,
'WIN_UNICODE_CONSOLE': True,
'XDG_CONFIG_HOME': os.path.expanduser(os.path.join('~', '.config')),
@ -307,6 +312,10 @@ DEFAULT_DOCS = {
"exit status) to not be added to the history list."),
'IGNOREEOF': VarDocs('Prevents Ctrl-D from exiting the shell.'),
'INDENT': VarDocs('Indentation string for multiline input'),
'INTENSIFY_COLORS_ON_WIN': VarDocs('Enhance style colors for readability '
'when using the default terminal (cmd.exe) on winodws. Blue colors, '
'which are hard to read, are replaced with cyan. Other colors are '
'generally replaced by their bright counter parts.'),
'LOADED_CONFIG': VarDocs('Whether or not the xonsh config file was loaded',
configurable=False),
'LOADED_RC_FILES': VarDocs(
@ -333,17 +342,6 @@ DEFAULT_DOCS = {
"auto-formatted, see 'Customizing the Prompt' at "
'http://xon.sh/tutorial.html#customizing-the-prompt.',
default='xonsh.environ.DEFAULT_PROMPT'),
'PROMPT_TOOLKIT_COLORS': VarDocs(
'This is a mapping of from color names to HTML color codes. Whenever '
'prompt-toolkit would color a word a particular color (in the prompt, '
'or in syntax highlighting), it will use the value specified here to '
'represent that color, instead of its default. If a color is not '
'specified here, prompt-toolkit uses the colors from '
"'xonsh.tools._PT_COLORS'.", configurable=False),
'PROMPT_TOOLKIT_STYLES': VarDocs(
'This is a mapping of pygments tokens to user-specified styles for '
'prompt-toolkit. See the prompt-toolkit and pygments documentation '
'for more details. If None, this is skipped.', configurable=False),
'PUSHD_MINUS': VarDocs(
'Flag for directory pushing functionality. False is the normal '
'behavior.'),
@ -398,6 +396,9 @@ DEFAULT_DOCS = {
"in the same manner as $PROMPT, see 'Customizing the Prompt' "
'http://xon.sh/tutorial.html#customizing-the-prompt.',
default='xonsh.environ.DEFAULT_TITLE'),
'UPDATE_OS_ENVIRON': VarDocs("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()'"),
'VI_MODE': VarDocs(
"Flag to enable 'vi_mode' in the 'prompt_toolkit' shell."),
'VIRTUAL_ENV': VarDocs(
@ -495,6 +496,7 @@ class Env(MutableMapping):
def __init__(self, *args, **kwargs):
"""If no initial environment is given, os.environ is used."""
self._d = {}
self._orig_env = None
self.ensurers = {k: Ensurer(*v) for k, v in DEFAULT_ENSURERS.items()}
self.defaults = DEFAULT_VALUES
self.docs = DEFAULT_DOCS
@ -503,14 +505,17 @@ class Env(MutableMapping):
for key, val in dict(*args, **kwargs).items():
self[key] = val
self._detyped = None
self._orig_env = None
@staticmethod
def detypeable(val):
return not (callable(val) or isinstance(val, MutableMapping))
def detype(self):
if self._detyped is not None:
return self._detyped
ctx = {}
for key, val in self._d.items():
if callable(val) or isinstance(val, MutableMapping):
if not self.detypeable(val):
continue
if not isinstance(key, string_types):
key = str(key)
@ -567,16 +572,27 @@ class Env(MutableMapping):
return vd
@contextmanager
def swap(self, other):
def swap(self, other=None, **kwargs):
"""Provides a context manager for temporarily swapping out certain
environment variables with other values. On exit from the context
manager, the original values are restored.
"""
old = {}
for k, v in other.items():
# single positional argument should be a dict-like object
if other is not None:
for k, v in other.items():
old[k] = self.get(k, NotImplemented)
self[k] = v
# kwargs could also have been sent in
for k, v in kwargs.items():
old[k] = self.get(k, NotImplemented)
self[k] = v
yield self
# restore the values
for k, v in old.items():
if v is NotImplemented:
del self[k]
@ -589,6 +605,8 @@ class Env(MutableMapping):
#
def __getitem__(self, key):
if key is Ellipsis:
return self
m = self._arg_regex.match(key)
if (m is not None) and (key not in self._d) and ('ARGS' in self._d):
args = self._d['ARGS']
@ -615,12 +633,23 @@ class Env(MutableMapping):
if not ensurer.validate(val):
val = ensurer.convert(val)
self._d[key] = val
self._detyped = None
if self.detypeable(val):
self._detyped = None
if self.get('UPDATE_OS_ENVIRON'):
if self._orig_env is None:
self.replace_env()
else:
dval = ensurer.detype(val)
os.environ[key] = dval
def __delitem__(self, key):
del self._d[key]
self._detyped = None
val = self._d.pop(key)
if self.detypeable(val):
self._detyped = None
if self.get('UPDATE_OS_ENVIRON'):
if 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
default is not given here.
@ -685,10 +714,15 @@ def _is_executable_file(path):
def yield_executables_windows(directory, name):
normalized_name = os.path.normcase(name)
extensions = builtins.__xonsh_env__.get('PATHEXT')
for a_file in os.listdir(directory):
base_name, ext = os.path.splitext(a_file)
if name == base_name and ext.upper() in extensions:
normalized_file_name = os.path.normcase(a_file)
base_name, ext = os.path.splitext(normalized_file_name)
if (
normalized_name == base_name or normalized_name == normalized_file_name
) and ext.upper() in extensions:
yield os.path.join(directory, a_file)
@ -706,9 +740,15 @@ def locate_binary(name):
if os.path.isfile(name) and name != os.path.basename(name):
return name
directories = builtins.__xonsh_env__.get('PATH')
# Windows users expect t obe able to execute files in the same directory without `./`
if ON_WINDOWS:
directories = [_get_cwd()] + directories
try:
return next(chain.from_iterable(yield_executables(directory, name) for
directory in builtins.__xonsh_env__.get('PATH') if os.path.isdir(directory)))
directory in directories if os.path.isdir(directory)))
except StopIteration:
return None
@ -1135,9 +1175,36 @@ def load_static_config(ctx, config=None):
return conf
def _splitpath(path, sofar=[]):
folder, path = os.path.split(path)
if path == "":
return sofar[::-1]
elif folder == "":
return (sofar + [path])[::-1]
else:
return _splitpath(folder, sofar + [path])
_CHARACTER_MAP = {chr(o): '_%s' % chr(o+32) for o in range(65, 91)}
_CHARACTER_MAP.update({'.': '_.', '_': '__'})
def _cache_renamer(path):
path = os.path.abspath(path)
o = [''.join(_CHARACTER_MAP.get(i, i) for i in w) for w in _splitpath(path)]
o[-1] = "{}.{}".format(o[-1], sys.implementation.cache_tag)
return o
def _make_if_not_exists(dirname):
if not os.path.isdir(dirname):
os.makedirs(dirname)
def xonshrc_context(rcfiles=None, execer=None):
"""Attempts to read in xonshrc file, and return the contents."""
loaded = builtins.__xonsh_env__['LOADED_RC_FILES'] = []
datadir = builtins.__xonsh_env__['XONSH_DATA_DIR']
cachedir = os.path.join(datadir, 'xonshrc_cache')
if (rcfiles is None or execer is None):
return {}
env = {}
@ -1145,21 +1212,38 @@ def xonshrc_context(rcfiles=None, execer=None):
if not os.path.isfile(rcfile):
loaded.append(False)
continue
with open(rcfile, 'r') as f:
rc = f.read()
if not rc.endswith('\n'):
rc += '\n'
fname = execer.filename
try:
execer.filename = rcfile
execer.exec(rc, glbs=env)
loaded.append(True)
except SyntaxError as err:
loaded.append(False)
msg = 'syntax error in xonsh run control file {0!r}: {1!s}'
warn(msg.format(rcfile, err), RuntimeWarning)
finally:
execer.filename = fname
use_cached = False
cachefname = os.path.join(cachedir, *_cache_renamer(rcfile))
if os.path.isfile(cachefname):
if os.stat(cachefname).st_mtime >= os.stat(rcfile).st_mtime:
# use the compiled version and leave
with open(cachefname, 'rb') as cfile:
ccode = marshal.load(cfile)
use_cached = True
loaded.append(True)
if not use_cached:
with open(rcfile, 'r') as f:
rc = f.read()
if not rc.endswith('\n'):
rc += '\n'
fname = execer.filename
try:
execer.filename = rcfile
ccode = execer.compile(rc, glbs=env)
_make_if_not_exists(os.path.dirname(cachefname))
with open(cachefname, 'wb') as cfile:
marshal.dump(ccode, cfile)
loaded.append(True)
except SyntaxError as err:
loaded.append(False)
msg = 'syntax error in xonsh run control file {0!r}: {1!s}'
warn(msg.format(rcfile, err), RuntimeWarning)
continue
finally:
execer.filename = fname
if ccode is None:
return env
exec(ccode, env, None)
return env
@ -1182,15 +1266,18 @@ def windows_env_fixes(ctx):
ctx['PWD'] = _get_cwd()
def default_env(env=None, config=None):
def default_env(env=None, config=None, login=True):
"""Constructs a default xonsh environment."""
# in order of increasing precedence
ctx = dict(BASE_ENV)
ctx.update(os.environ)
conf = load_static_config(ctx, config=config)
ctx.update(conf.get('env', ()))
ctx.update(load_foreign_envs(shells=conf.get('foreign_shells', DEFAULT_SHELLS),
issue_warning=False))
if login:
ctx = dict(BASE_ENV)
ctx.update(os.environ)
conf = load_static_config(ctx, config=config)
ctx.update(conf.get('env', ()))
ctx.update(load_foreign_envs(shells=conf.get('foreign_shells', DEFAULT_SHELLS),
issue_warning=False))
else:
ctx = {}
if ON_WINDOWS:
windows_env_fixes(ctx)
# finalize env

View file

@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
"""Implements the xonsh executer."""
import re
import os
import types
import inspect
import builtins
from collections import Iterable, Sequence, Mapping
from collections import Mapping
from xonsh import ast
from xonsh.parser import Parser
@ -17,7 +15,7 @@ class Execer(object):
"""Executes xonsh code in a context."""
def __init__(self, filename='<xonsh-code>', debug_level=0, parser_args=None,
unload=True, config=None):
unload=True, config=None, login=True):
"""Parameters
----------
filename : str, optional
@ -37,7 +35,7 @@ class Execer(object):
self.debug_level = debug_level
self.unload = unload
self.ctxtransformer = ast.CtxAwareTransformer(self.parser)
load_builtins(execer=self, config=config)
load_builtins(execer=self, config=config, login=login)
def __del__(self):
if self.unload:

View file

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
"""Hooks for Jupyter Xonsh Kernel."""
import io
import sys
import builtins
from pprint import pformat
from tempfile import SpooledTemporaryFile

View file

@ -2,6 +2,7 @@
"""The main xonsh script."""
import os
import sys
import enum
import builtins
from argparse import ArgumentParser, ArgumentTypeError
from contextlib import contextmanager
@ -138,6 +139,11 @@ def _pprint_displayhook(value):
pprint(value) # black & white case
builtins._ = value
class XonshMode(enum.Enum):
single_command = 0
script_from_file = 1
script_from_stdin = 2
interactive = 3
def premain(argv=None):
"""Setup for main xonsh entry point, returns parsed arguments."""
@ -156,19 +162,35 @@ def premain(argv=None):
version = '/'.join(('xonsh', __version__)),
print(version)
exit()
shell_kwargs = {'shell_type': args.shell_type}
shell_kwargs = {'shell_type': args.shell_type,
'completer': False,
'login': False}
if args.login:
shell_kwargs['login'] = True
if args.config_path is None:
shell_kwargs['config'] = args.config_path
if args.norc:
shell_kwargs['rc'] = ()
setattr(sys, 'displayhook', _pprint_displayhook)
if args.command is not None:
args.mode = XonshMode.single_command
shell_kwargs['shell_type'] = 'none'
elif args.file is not None:
args.mode = XonshMode.script_from_file
shell_kwargs['shell_type'] = 'none'
elif not sys.stdin.isatty() and not args.force_interactive:
args.mode = XonshMode.script_from_stdin
shell_kwargs['shell_type'] = 'none'
else:
args.mode = XonshMode.interactive
shell_kwargs['completer'] = True
shell_kwargs['login'] = True
shell = builtins.__xonsh_shell__ = Shell(**shell_kwargs)
from xonsh import imphooks
env = builtins.__xonsh_env__
env['XONSH_LOGIN'] = shell_kwargs['login']
if args.defines is not None:
env.update([x.split('=', 1) for x in args.defines])
if args.login:
env['XONSH_LOGIN'] = True
env['XONSH_INTERACTIVE'] = False
if ON_WINDOWS:
setup_win_unicode_console(env.get('WIN_UNICODE_CONSOLE', True))
@ -180,10 +202,10 @@ def main(argv=None):
args = premain(argv)
env = builtins.__xonsh_env__
shell = builtins.__xonsh_shell__
if args.command is not None:
if args.mode == XonshMode.single_command:
# run a single command and exit
shell.default(args.command)
elif args.file is not None:
elif args.mode == XonshMode.script_from_file:
# run a script contained in a file
if os.path.isfile(args.file):
with open(args.file) as f:
@ -196,7 +218,7 @@ def main(argv=None):
shell.execer.exec(code, mode='exec', glbs=shell.ctx)
else:
print('xonsh: {0}: No such file or directory.'.format(args.file))
elif not sys.stdin.isatty() and not args.force_interactive:
elif args.mode == XonshMode.script_from_stdin:
# run a script given on stdin
code = sys.stdin.read()
code = code if code.endswith('\n') else code + '\n'

View file

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
"""Implements the base xonsh parser."""
import os
import sys
from collections import Iterable, Sequence, Mapping
from ply import yacc
@ -128,9 +126,12 @@ def xonsh_superhelp(x, lineno=None, col=None):
return xonsh_call('__xonsh_superhelp__', [x], lineno=lineno, col=col)
def xonsh_regexpath(x, lineno=None, col=None):
"""Creates the AST node for calling the __xonsh_regexpath__() function."""
return xonsh_call('__xonsh_regexpath__', [x], lineno=lineno, col=col)
def xonsh_regexpath(x, pymode=False, lineno=None, col=None):
"""Creates the AST node for calling the __xonsh_regexpath__() function.
The pymode argument indicate if it is called from subproc or python mode"""
pymode = ast.NameConstant(value=pymode, lineno=lineno, col_offset=col)
return xonsh_call('__xonsh_regexpath__', args=[x, pymode], lineno=lineno,
col=col)
def load_ctx(x):
@ -1593,7 +1594,7 @@ class BaseParser(object):
return leader
p0 = leader
for trailer in trailers:
if isinstance(trailer, (ast.Index, ast.Slice)):
if isinstance(trailer, (ast.Index, ast.Slice, ast.ExtSlice)):
p0 = ast.Subscript(value=leader,
slice=trailer,
ctx=ast.Load(),
@ -1722,7 +1723,8 @@ class BaseParser(object):
"""atom : REGEXPATH"""
p1 = ast.Str(s=p[1].strip('`'), lineno=self.lineno,
col_offset=self.col)
p[0] = xonsh_regexpath(p1, lineno=self.lineno, col=self.col)
p[0] = xonsh_regexpath(p1, pymode=True, lineno=self.lineno,
col=self.col)
def p_atom_dname(self, p):
"""atom : DOLLAR_NAME"""
@ -1798,7 +1800,12 @@ class BaseParser(object):
def p_subscriptlist(self, p):
"""subscriptlist : subscript comma_subscript_list_opt comma_opt"""
p1, p2 = p[1], p[2]
if p2 is not None:
if p2 is None:
pass
elif isinstance(p1, ast.Slice) or \
any([isinstance(x, ast.Slice) for x in p2]):
p1 = ast.ExtSlice(dims=[p1]+p2)
else:
p1.value = ast.Tuple(elts=[p1.value] + [x.value for x in p2],
ctx=ast.Load(), lineno=p1.lineno,
col_offset=p1.col_offset)
@ -2167,22 +2174,6 @@ class BaseParser(object):
p0._cliarg_action = 'splitlines'
p[0] = p0
def p_subproc_atom_captured_object(self, p):
"""subproc_atom : bang_lparen_tok subproc RPAREN"""
p1 = p[1]
p0 = xonsh_call('__xonsh_subproc_captured_object__', args=p[2],
lineno=p1.lineno, col=p1.lexpos)
p0._cliarg_action = 'splitlines'
p[0] = p0
def p_subproc_atom_captured_hiddenobject(self, p):
"""subproc_atom : bang_lbracket_tok subproc RBRACKET"""
p1 = p[1]
p0 = xonsh_call('__xonsh_subproc_captured_hiddenobject__', args=p[2],
lineno=p1.lineno, col=p1.lexpos)
p0._cliarg_action = 'splitlines'
p[0] = p0
def p_subproc_atom_pyenv_lookup(self, p):
"""subproc_atom : dollar_lbrace_tok test RBRACE"""
p1 = p[1]
@ -2227,7 +2218,8 @@ class BaseParser(object):
"""subproc_atom : REGEXPATH"""
p1 = ast.Str(s=p[1].strip('`'), lineno=self.lineno,
col_offset=self.col)
p0 = xonsh_regexpath(p1, lineno=self.lineno, col=self.col)
p0 = xonsh_regexpath(p1, pymode=False, lineno=self.lineno,
col=self.col)
p0._cliarg_action = 'extend'
p[0] = p0

View file

@ -1,12 +1,7 @@
# -*- coding: utf-8 -*-
"""Implements the xonsh parser for Python v3.4."""
import os
import sys
from collections import Iterable, Sequence, Mapping
from xonsh import ast
from xonsh.lexer import LexToken
from xonsh.parsers.base import BaseParser, xonsh_help, xonsh_superhelp
from xonsh.parsers.base import BaseParser
class Parser(BaseParser):

View file

@ -1,12 +1,7 @@
# -*- coding: utf-8 -*-
"""Implements the xonsh parser for Python v3.5."""
import os
import sys
from collections import Iterable, Sequence, Mapping
from xonsh import ast
from xonsh.lexer import LexToken
from xonsh.parsers.base import BaseParser, xonsh_help, xonsh_superhelp
from xonsh.parsers.base import BaseParser
class Parser(BaseParser):

View file

@ -539,7 +539,8 @@ _CCTuple = namedtuple("_CCTuple", ["stdin",
"alias",
"stdin_redirect",
"stdout_redirect",
"stderr_redirect"])
"stderr_redirect",
"timestamp"])
class CompletedCommand(_CCTuple):
"""Represents a completed subprocess-mode command."""

View file

@ -25,7 +25,7 @@ class PromptToolkitCompleter(Completer):
if complete_event.completion_requested:
line = document.current_line.lstrip()
endidx = document.cursor_position_col
begidx = line.rfind(' ') + 1 if line.rfind(' ') >= 0 else 0
begidx = line[:endidx].rfind(' ') + 1 if line[:endidx].rfind(' ') >= 0 else 0
prefix = line[begidx:endidx]
completions, l = self.completer.complete(prefix,
line,
@ -48,14 +48,15 @@ class PromptToolkitCompleter(Completer):
except AttributeError:
#new layout to become default
window = cli.application.layout.children[1].content
h = window.render_info.content_height
r = builtins.__xonsh_env__.get('COMPLETIONS_MENU_ROWS')
size = h + r
def comp_height(cli):
# If there is an autocompletion menu to be shown, make sure that o
# layout has at least a minimal height in order to display it.
if not cli.is_done:
return LayoutDimension(min=size)
else:
return LayoutDimension()
window._height = comp_height
if window and window.render_info:
h = window.render_info.content_height
r = builtins.__xonsh_env__.get('COMPLETIONS_MENU_ROWS')
size = h + r
def comp_height(cli):
# If there is an autocompletion menu to be shown, make sure that o
# layout has at least a minimal height in order to display it.
if not cli.is_done:
return LayoutDimension(min=size)
else:
return LayoutDimension()
window._height = comp_height

View file

@ -1,13 +1,10 @@
# -*- coding: utf-8 -*-
"""History object for use with prompt_toolkit."""
import os
import time
import builtins
from threading import Thread
import prompt_toolkit.history
from prompt_toolkit.buffer import Buffer
from xonsh import lazyjson

View file

@ -70,6 +70,28 @@ class TabShouldInsertIndentFilter(Filter):
return bool(before_cursor.isspace())
class BeginningOfLine(Filter):
"""
Check if cursor is at beginning of a line other than the first line
in a multiline document
"""
def __call__(self, cli):
before_cursor = cli.current_buffer.document.current_line_before_cursor
return bool(len(before_cursor) == 0
and not cli.current_buffer.document.on_first_line)
class EndOfLine(Filter):
"""
Check if cursor is at the end of a line other than the last line
in a multiline document
"""
def __call__(self, cli):
d = cli.current_buffer.document
at_end = d.is_cursor_at_the_end_of_line
last_line = d.is_cursor_at_the_end
return bool(at_end and not last_line)
def can_compile(src):
"""Returns whether the code can be compiled, i.e. it is valid xonsh."""
@ -109,6 +131,21 @@ def load_xonsh_bindings(key_bindings_manager):
b = event.cli.current_buffer
carriage_return(b, event.cli)
@handle(Keys.Left, filter=BeginningOfLine())
def wrap_cursor_back(event):
"""Move cursor to end of previous line unless at beginning of document"""
b = event.cli.current_buffer
b.cursor_up(count=1)
relative_end_index = b.document.get_end_of_line_position()
b.cursor_right(count=relative_end_index)
@handle(Keys.Right, filter=EndOfLine())
def wrap_cursor_forward(event):
"""Move cursor to beginning of next line unless at end of document"""
b = event.cli.current_buffer
relative_begin_index = b.document.get_start_of_line_position()
b.cursor_left(count=abs(relative_begin_index))
b.cursor_down(count=1)
def _is_blank(l):
return len(l.strip()) == 0

View file

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
"""The prompt_toolkit based xonsh shell."""
import builtins
from warnings import warn
from prompt_toolkit.key_binding.manager import KeyBindingManager
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
@ -15,7 +14,7 @@ from pygments.token import (Keyword, Name, Comment, String, Error, Number,
Operator, Generic, Whitespace, Token)
from xonsh.base_shell import BaseShell
from xonsh.tools import print_exception, format_color
from xonsh.tools import print_exception
from xonsh.environ import partial_format_prompt
from xonsh.pyghooks import XonshLexer, XonshStyle, partial_color_tokenize, \
xonsh_style_proxy

View file

@ -1,7 +1,7 @@
"""A prompt-toolkit inspired shortcut collection."""
from prompt_toolkit.interface import CommandLineInterface
from prompt_toolkit.utils import DummyContext
from prompt_toolkit.shortcuts import (create_prompt_application,
from prompt_toolkit.shortcuts import (create_prompt_application,
create_eventloop, create_asyncio_eventloop, create_output)
from xonsh.shell import prompt_toolkit_version_info
@ -9,7 +9,7 @@ from xonsh.shell import prompt_toolkit_version_info
class Prompter(object):
def __init__(self, cli=None, *args, **kwargs):
"""Implements a prompt that statefully holds a command-line
"""Implements a prompt that statefully holds a command-line
interface. When used as a context manager, it will return itself
on entry and reset itself on exit.
@ -33,18 +33,18 @@ class Prompter(object):
def prompt(self, message='', **kwargs):
"""Get input from the user and return it.
This is a wrapper around a lot of prompt_toolkit functionality and
can be a replacement for raw_input. (or GNU readline.) If you want
to keep your history across several calls, create one
`~prompt_toolkit.history.History instance and pass it every
time. This function accepts many keyword arguments. Except for the
following. they are a proxy to the arguments of
This is a wrapper around a lot of prompt_toolkit functionality and
can be a replacement for raw_input. (or GNU readline.) If you want
to keep your history across several calls, create one
`~prompt_toolkit.history.History instance and pass it every
time. This function accepts many keyword arguments. Except for the
following. they are a proxy to the arguments of
create_prompt_application().
Parameters
----------
patch_stdout : file-like, optional
Replace ``sys.stdout`` by a proxy that ensures that print
Replace ``sys.stdout`` by a proxy that ensures that print
statements from other threads won't destroy the prompt. (They
will be printed above the prompt instead.)
return_asyncio_coroutine : bool, optional
@ -64,6 +64,8 @@ class Prompter(object):
# Create CommandLineInterface.
if self.cli is None:
if self.major_minor < (0, 57):
kwargs.pop('reserve_space_for_menu', None)
if self.major_minor <= (0, 57):
kwargs.pop('get_rprompt_tokens', None)
kwargs.pop('get_continuation_tokens', None)
@ -93,9 +95,9 @@ class Prompter(object):
'''), exec_context)
return exec_context['prompt_coro']()
else:
# Note: We pass `reset_current_buffer=False`, because that way
# it's easy to give DEFAULT_BUFFER a default value, without it
# getting erased. We don't have to reset anyway, because this is
# Note: We pass `reset_current_buffer=False`, because that way
# it's easy to give DEFAULT_BUFFER a default value, without it
# getting erased. We don't have to reset anyway, because this is
# the first and only time that this CommandLineInterface will run.
try:
with patch_context:
@ -140,7 +142,7 @@ except ImportError:
This method was forked from the mainline prompt-toolkit repo.
Copyright (c) 2014, Jonathan Slenders, All rights reserved.
This is deprecated and slated for removal after a prompt-toolkit
v0.57+ release.
v0.57+ release.
"""
stdout = stdout or sys.__stdout__
true_color = to_simple_filter(true_color)
@ -179,7 +181,7 @@ except ImportError:
This method was forked from the mainline prompt-toolkit repo.
Copyright (c) 2014, Jonathan Slenders, All rights reserved.
This is deprecated and slated for removal after a prompt-toolkit
v0.57+ release.
v0.57+ release.
"""
assert isinstance(style, Style)
output = create_output(true_color=true_color)

View file

@ -16,6 +16,8 @@ from pygments.style import Style
from pygments.styles import get_style_by_name
import pygments.util
from xonsh.tools import (ON_WINDOWS, intensify_colors_for_cmd_exe,
expand_gray_colors_for_cmd_exe)
class XonshSubprocLexer(BashLexer):
"""Lexer for xonsh subproc mode."""
@ -28,8 +30,8 @@ class XonshSubprocLexer(BashLexer):
ROOT_TOKENS = [(r'\?', Keyword),
(r'\$\w+', Name.Variable),
(r'\$\{', Keyword, ('pymode', )),
(r'\$\(', Keyword, ('subproc', )),
(r'\$\[', Keyword, ('subproc', )),
(r'[\!\$]\(', Keyword, ('subproc', )),
(r'[\!\$]\[', Keyword, ('subproc', )),
(r'@\(', Keyword, ('pymode', )),
inherit, ]
@ -281,7 +283,8 @@ class XonshStyle(Style):
style_name : str, optional
The style name to initialize with.
"""
self.trap = {} # for custom colors
self.trap = {} # for traping custom colors set by user
self._smap = {}
self._style_name = ''
self.style_name = style_name
super().__init__()
@ -301,17 +304,34 @@ class XonshStyle(Style):
RuntimeWarning)
cmap = DEFAULT_STYLE
try:
smap = get_style_by_name(value)().styles
self._smap = get_style_by_name(value)().styles.copy()
except (ImportError, pygments.util.ClassNotFound):
smap = XONSH_BASE_STYLE
compound = CompoundColorMap(ChainMap(self.trap, cmap, PTK_STYLE, smap))
self.styles = ChainMap(self.trap, cmap, PTK_STYLE, smap, compound)
self._smap = XONSH_BASE_STYLE.copy()
compound = CompoundColorMap(ChainMap(self.trap, cmap, PTK_STYLE, self._smap))
self.styles = ChainMap(self.trap, cmap, PTK_STYLE, self._smap, compound)
self._style_name = value
if ON_WINDOWS:
self.enhance_colors_for_cmd_exe()
@style_name.deleter
def style_name(self):
self._style_name = ''
def enhance_colors_for_cmd_exe(self):
""" Enhance colors when using cmd.exe on windows.
When using the default style all blue and dark red colors
are changed to CYAN and intence red.
"""
env = builtins.__xonsh_env__
# Ensure we are not using ConEmu
if 'CONEMUANSI' not in env:
# Auto suggest needs to be a darker shade to be distinguishable
# from the default color
self.styles[Token.AutoSuggestion] = '#444444'
if env.get('INTENSIFY_COLORS_ON_WIN', False):
self._smap.update(expand_gray_colors_for_cmd_exe(self._smap))
self._smap.update(intensify_colors_for_cmd_exe(self._smap))
def xonsh_style_proxy(styler):
"""Factory for a proxy class to a xonsh style."""

View file

@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
"""The readline based xonsh shell."""
import os
import sys
import time
import select
import builtins
from cmd import Cmd
from warnings import warn
from threading import Thread, Lock
from threading import Thread
from collections import deque
from xonsh import lazyjson

View file

@ -73,6 +73,8 @@ class Shell(object):
rc : list of str, optional
Sequence of paths to run control files.
"""
self.login = kwargs.get('login', True)
self.stype = shell_type
self._init_environ(ctx, config, rc)
env = builtins.__xonsh_env__
# pick a valid shell
@ -88,23 +90,23 @@ class Shell(object):
warn('prompt_toolkit is not available, using readline instead.')
shell_type = env['SHELL_TYPE'] = 'readline'
# actually make the shell
if shell_type == 'prompt_toolkit':
if shell_type == 'none':
from xonsh.base_shell import BaseShell as shell_class
elif shell_type == 'prompt_toolkit':
vptk = prompt_toolkit_version()
minor = int(vptk.split('.')[1])
if minor < 57 or vptk == '<0.57': # TODO: remove in future
msg = ('prompt-toolkit version < v0.57 and may not work as '
'expected. Please update.')
warn(msg, RuntimeWarning)
from xonsh.ptk.shell import PromptToolkitShell
self.shell = PromptToolkitShell(execer=self.execer,
ctx=self.ctx, **kwargs)
from xonsh.ptk.shell import PromptToolkitShell as shell_class
elif shell_type == 'readline':
from xonsh.readline_shell import ReadlineShell
self.shell = ReadlineShell(execer=self.execer,
ctx=self.ctx, **kwargs)
from xonsh.readline_shell import ReadlineShell 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)
# allows history garbace colector to start running
builtins.__xonsh_history__.gc.wait_for_shell = False
@ -113,11 +115,14 @@ class Shell(object):
return getattr(self.shell, attr)
def _init_environ(self, ctx, config, rc):
self.execer = Execer(config=config)
self.execer = Execer(config=config, login=self.login)
env = builtins.__xonsh_env__
if ctx is None:
rc = env.get('XONSHRC') if rc is None else rc
self.ctx = xonshrc_context(rcfiles=rc, execer=self.execer)
if self.stype == 'none' and not self.login:
self.ctx = {}
else:
rc = env.get('XONSHRC') if rc is None else rc
self.ctx = xonshrc_context(rcfiles=rc, execer=self.execer)
else:
self.ctx = ctx
builtins.__xonsh_ctx__ = self.ctx

View file

@ -733,6 +733,78 @@ def color_style():
return builtins.__xonsh_shell__.shell.color_style()
try:
import prompt_toolkit
except ImportError:
prompt_toolkit = None
def _get_color_indexes(style_map):
""" Generates the color and windows color index for a style """
table = prompt_toolkit.terminal.win32_output.ColorLookupTable()
pt_style = prompt_toolkit.styles.style_from_dict(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:
rgb = (int(attr.color[0:2], 16),
int(attr.color[2:4], 16),
int(attr.color[4:6], 16))
except:
rgb = None
yield token, index, rgb
def intensify_colors_for_cmd_exe(style_map, replace_colors=None):
"""Returns a modified style to where colors that maps to dark
colors are replaced with brighter versions. Also expands the
range used by the gray colors
"""
modified_style = {}
if not ON_WINDOWS or prompt_toolkit is None:
return modified_style
if replace_colors is None:
replace_colors = {1: '#44ffff', # subst blue with bright cyan
2: '#44ff44', # subst green with bright green
4: '#ff4444', # subst red with bright red
5: '#ff44ff', # subst magenta with bright magenta
6: '#ffff44', # subst yellow with bright yellow
9: '#00aaaa', # subst intense blue (hard to read)
# with dark cyan (which is readable)
}
for token, idx, _ in _get_color_indexes(style_map):
if idx in replace_colors:
modified_style[token] = replace_colors[idx]
return modified_style
def expand_gray_colors_for_cmd_exe(style_map):
""" Expand the style's gray scale color range.
All gray scale colors has a tendency to map to the same default GRAY
in cmd.exe.
"""
modified_style = {}
if not ON_WINDOWS or prompt_toolkit is None:
return modified_style
for token, idx, rgb in _get_color_indexes(style_map):
if idx == 7 and rgb:
if sum(rgb) <= 306:
# Equal and below '#666666 is reset to dark gray
modified_style[token] = '#444444'
elif sum(rgb) >= 408:
# Equal and above 0x888888 is reset to white
modified_style[token] = '#ffffff'
return modified_style
def intensify_colors_on_win_setter(enable):
""" Resets the style when setting the INTENSIFY_COLORS_ON_WIN
environment variable. """
enable = to_bool(enable)
delattr(builtins.__xonsh_shell__.shell.styler, 'style_name')
return enable
_RE_STRING_START = "[bBrRuU]*"
_RE_STRING_TRIPLE_DOUBLE = '"""'
@ -917,6 +989,8 @@ def expandvars(path):
var = eval(var, builtins.__xonsh_ctx__)
if _is_in_env(var):
value = _get_env_string(var)
elif var is Ellipsis:
value = dollar + brace + '...' + rbrace
else:
value = dollar + brace + var + rbrace
except:

130
xonsh/winutils.py Normal file
View file

@ -0,0 +1,130 @@
"""
This file is based on the code from https://github.com/JustAMan/pyWinClobber/blob/master/win32elevate.py
Copyright (c) 2013 by JustAMan at GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import sys
import subprocess
import ctypes
from ctypes.wintypes import HANDLE, BOOL, DWORD, HWND, HINSTANCE, HKEY
from ctypes import c_ulong, c_char_p, c_int, c_void_p
P_HANDLE = ctypes.POINTER(HANDLE)
P_WORD = ctypes.POINTER(DWORD)
CloseHandle = ctypes.windll.kernel32.CloseHandle
CloseHandle.argtypes = (HANDLE, )
CloseHandle.restype = BOOL
GetActiveWindow = ctypes.windll.user32.GetActiveWindow
GetActiveWindow.argtypes = ()
GetActiveWindow.restype = HANDLE
TOKEN_READ = 0x20008
class ShellExecuteInfo(ctypes.Structure):
_fields_ = [
('cbSize', DWORD),
('fMask', c_ulong),
('hwnd', HWND),
('lpVerb', c_char_p),
('lpFile', c_char_p),
('lpParameters', c_char_p),
('lpDirectory', c_char_p),
('nShow', c_int),
('hInstApp', HINSTANCE),
('lpIDList', c_void_p),
('lpClass', c_char_p),
('hKeyClass', HKEY),
('dwHotKey', DWORD),
('hIcon', HANDLE),
('hProcess', HANDLE)
]
def __init__(self, **kw):
ctypes.Structure.__init__(self)
self.cbSize = ctypes.sizeof(self)
for field_name, field_value in kw.items():
setattr(self, field_name, field_value)
PShellExecuteInfo = ctypes.POINTER(ShellExecuteInfo)
ShellExecuteEx = ctypes.windll.Shell32.ShellExecuteExA
ShellExecuteEx.argtypes = (PShellExecuteInfo, )
ShellExecuteEx.restype = BOOL
WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
WaitForSingleObject.argtypes = (HANDLE, DWORD)
WaitForSingleObject.restype = DWORD
# SW_HIDE = 0
SW_SHOW = 5
SEE_MASK_NOCLOSEPROCESS = 0x00000040
SEE_MASK_NO_CONSOLE = 0x00008000
INFINITE = -1
def wait_and_close_handle(process_handle):
"""
Waits till spawned process finishes and closes the handle for it
:param process_handle: The Windows handle for the process
:type process_handle: HANDLE
"""
WaitForSingleObject(process_handle, INFINITE)
CloseHandle(process_handle)
def sudo(executable, args=None):
"""
This will re-run current Python script requesting to elevate administrative rights.
:param executable: The path/name of the executable
:type executable: str
:param args: The arguments to be passed to the executable
:type args: list
"""
if not args:
args = []
execute_info = ShellExecuteInfo(
fMask=SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE,
hwnd=GetActiveWindow(),
lpVerb=b'runas',
lpFile=executable.encode('utf-8'),
lpParameters=subprocess.list2cmdline(args).encode('utf-8'),
lpDirectory=None,
nShow=SW_SHOW
)
if not all(stream.isatty() for stream in (sys.stdin, sys.stdout, sys.stderr)):
# TODO: Some streams were redirected, we need to manually work them
raise NotImplementedError("Redirection is not supported")
if not ShellExecuteEx(ctypes.byref(execute_info)):
raise ctypes.WinError()
wait_and_close_handle(execute_info.hProcess)
__all__ = ('sudo', )

View file

@ -6,7 +6,7 @@ import json
import builtins
import textwrap
from pprint import pformat
from collections.abc import MutableSequence, Mapping, Sequence
from collections.abc import Mapping, Sequence
from xonsh.tools import to_bool, to_bool_or_break, backup_file, print_color

View file

@ -1,5 +1,4 @@
"""The xonsh configuration (xonfig) utility."""
import os
import ast
import json
import shutil
@ -324,6 +323,8 @@ def _tok_colors(cmap, cols):
def _colors(ns):
cols, _ = shutil.get_terminal_size()
if tools.ON_WINDOWS:
cols -= 1
cmap = tools.color_style()
akey = next(iter(cmap))
if isinstance(akey, str):