Merge branch 'main' into fix_callias_double_open

This commit is contained in:
Andy Kipp 2024-12-16 16:28:30 +06:00 committed by GitHub
commit 1850e14061
Failed to generate hash of commit
43 changed files with 774 additions and 212 deletions

View file

@ -64,7 +64,7 @@
- Gilbert.Forsyth@capitalone.com - Gilbert.Forsyth@capitalone.com
- gforsyth@gwu.edu - gforsyth@gwu.edu
- gil@forsyth.dev - gil@forsyth.dev
num_commits: 665 num_commits: 680
first_commit: 2015-10-19 16:04:32 first_commit: 2015-10-19 16:04:32
github: gforsyth github: gforsyth
- name: Morten Enemark Lund - name: Morten Enemark Lund
@ -1136,7 +1136,7 @@
github: marciomazza github: marciomazza
- name: Noortheen Raja - name: Noortheen Raja
email: jnoortheen@gmail.com email: jnoortheen@gmail.com
num_commits: 237 num_commits: 239
first_commit: 2020-03-15 10:13:56 first_commit: 2020-03-15 10:13:56
github: jnoortheen github: jnoortheen
- name: Samuel Lotz - name: Samuel Lotz
@ -1283,7 +1283,7 @@
first_commit: 2021-02-08 10:50:51 first_commit: 2021-02-08 10:50:51
- name: Evgeny - name: Evgeny
email: eugenesvk@users.noreply.github.com email: eugenesvk@users.noreply.github.com
num_commits: 11 num_commits: 12
first_commit: 2021-02-22 09:32:34 first_commit: 2021-02-22 09:32:34
- name: Adam Schwalm - name: Adam Schwalm
email: adamschwalm@gmail.com email: adamschwalm@gmail.com
@ -1301,7 +1301,7 @@
email: 46109467+yaxollum@users.noreply.github.com email: 46109467+yaxollum@users.noreply.github.com
alternate_emails: alternate_emails:
- yaxollum@gmail.com - yaxollum@gmail.com
num_commits: 34 num_commits: 36
first_commit: 2021-06-11 14:11:19 first_commit: 2021-06-11 14:11:19
- name: Walter A. Boring IV - name: Walter A. Boring IV
email: waboring@hemna.com email: waboring@hemna.com
@ -1431,7 +1431,7 @@
first_commit: 2021-11-18 18:42:29 first_commit: 2021-11-18 18:42:29
- name: doronz88 - name: doronz88
email: doron88@gmail.com email: doron88@gmail.com
num_commits: 6 num_commits: 7
first_commit: 2022-04-27 12:41:13 first_commit: 2022-04-27 12:41:13
- name: Stefano Rivera - name: Stefano Rivera
email: github@rivera.za.net email: github@rivera.za.net
@ -1465,7 +1465,7 @@
first_commit: 2022-06-27 22:21:34 first_commit: 2022-06-27 22:21:34
- name: pre-commit-ci[bot] - name: pre-commit-ci[bot]
email: 66853113+pre-commit-ci[bot]@users.noreply.github.com email: 66853113+pre-commit-ci[bot]@users.noreply.github.com
num_commits: 66 num_commits: 78
first_commit: 2022-07-11 14:26:34 first_commit: 2022-07-11 14:26:34
- name: jgart - name: jgart
email: 47760695+jgarte@users.noreply.github.com email: 47760695+jgarte@users.noreply.github.com
@ -1571,7 +1571,7 @@
first_commit: 2024-01-15 05:25:23 first_commit: 2024-01-15 05:25:23
- name: JamesParrott - name: JamesParrott
email: 80779630+JamesParrott@users.noreply.github.com email: 80779630+JamesParrott@users.noreply.github.com
num_commits: 1 num_commits: 2
first_commit: 2024-01-16 08:56:07 first_commit: 2024-01-16 08:56:07
- name: Airat Makhmutov - name: Airat Makhmutov
email: 108220148+rautyrauty@users.noreply.github.com email: 108220148+rautyrauty@users.noreply.github.com
@ -1613,3 +1613,31 @@
email: kulkarniniraj14@gmail.com email: kulkarniniraj14@gmail.com
num_commits: 1 num_commits: 1
first_commit: 2024-07-04 04:37:55 first_commit: 2024-07-04 04:37:55
- name: Aidan Courtney
email: aidanfc97@gmail.com
num_commits: 1
first_commit: 2024-07-27 11:33:35
- name: Max Nordlund
email: max.nordlund@gmail.com
num_commits: 1
first_commit: 2024-10-30 09:11:04
- name: Shawn Wallace
email: yungwallace@live.com
num_commits: 1
first_commit: 2024-10-31 05:33:45
- name: Faidon Liambotis
email: paravoid@debian.org
num_commits: 1
first_commit: 2024-09-17 06:47:59
- name: Jueun Lee
email: seirios0107@gmail.com
num_commits: 1
first_commit: 2024-11-18 18:04:24
- name: Simon Billinge
email: sbillinge@users.noreply.github.com
num_commits: 1
first_commit: 2024-12-08 03:28:31
- name: Bala
email: kumaran.4353@gmail.com
num_commits: 1
first_commit: 2024-12-02 07:01:09

View file

@ -22,6 +22,7 @@ jobs:
- "3.10" - "3.10"
- "3.11" - "3.11"
- "3.12" - "3.12"
- "3.13"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: setup-python - name: setup-python

View file

@ -38,7 +38,7 @@ jobs:
- "3.10" - "3.10"
- "3.11" - "3.11"
- "3.12" - "3.12"
# - "3.13-dev" - "3.13"
name: Test Python ${{ matrix.python-version }} ${{ matrix.os }} name: Test Python ${{ matrix.python-version }} ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -76,11 +76,4 @@ jobs:
run: | run: |
python -m pip install -e . --no-deps python -m pip install -e . --no-deps
python -m xonsh run-tests.xsh test --report-coverage -- --timeout=240 python -m xonsh run-tests.xsh test --report-coverage -- --timeout=240
- name: Upload coverage to Codecov
if: ${{ startsWith(matrix.python-version, env.DEFAULT_PYTHON_VERSION) }}
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
verbose: true
flags: ${{ matrix.os }}

View file

@ -36,8 +36,8 @@ anki-code <anki-code@users.noreply.github.com> a <anki-code>
anki-code <anki-code@users.noreply.github.com> a <aaa@aaa.aaa> anki-code <anki-code@users.noreply.github.com> a <aaa@aaa.aaa>
anki-code <anki-code@users.noreply.github.com> a <1@1.1> anki-code <anki-code@users.noreply.github.com> a <1@1.1>
BlahGeek <i@BlahGeek.com> BlahGeek <i@BlahGeek.com>
Daniel Shimon <daniel.shimon22@gmail.com>
pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Daniel Shimon <daniel.shimon22@gmail.com>
Gyuri Horak <dyuri@horak.hu> Gyuri Horak <dyuri@horak.hu>
Jean-Benoist Leger <jb@leger.tf> Jean-Benoist Leger <jbleger@gertrude> Jean-Benoist Leger <jb@leger.tf> Jean-Benoist Leger <jbleger@gertrude>
Jean-Benoist Leger <jb@leger.tf> Jean-Benoist Leger <jbleger@hds.utc.fr> Jean-Benoist Leger <jb@leger.tf> Jean-Benoist Leger <jbleger@hds.utc.fr>
@ -47,9 +47,9 @@ Leonardo Santagada <santagada@gmail.com>
Burak Yiğit Kaya <ben@byk.im> Burak Yigit Kaya <ben@byk.im> Burak Yiğit Kaya <ben@byk.im> Burak Yigit Kaya <ben@byk.im>
Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias Bussonnier <bussonniermatthias@gmail.com>
Aaron Griffin <aig787@gmail.com> Aaron Griffin <aig787@gmail.com>
Peter Ye <46109467+yaxollum@users.noreply.github.com> Peter Ye <yaxollum@gmail.com>
Rob Brewer <rwb123@gmail.com> Robert W. Brewer <rwb123@gmail.com> Rob Brewer <rwb123@gmail.com> Robert W. Brewer <rwb123@gmail.com>
virus <virusbb001a@gmail.com> virus <virusbb001a@gmail.com>
Peter Ye <46109467+yaxollum@users.noreply.github.com> Peter Ye <yaxollum@gmail.com>
Sagar Tewari <sagartewari01@gmail.com> Sagar Tewari <sagartewariym@yahoo.com> Sagar Tewari <sagartewari01@gmail.com> Sagar Tewari <sagartewariym@yahoo.com>
Gordon Ball <gordon@chronitis.net> Gordon Ball <gordon@chronitis.net>
Eadaen1 <eadaen@protonmail.com> Eadaen1 <eadaen@protonmail.com>
@ -95,6 +95,7 @@ Alessio Bogon <youtux@gmail.com> Alessio Bogon <youtux@users.noreply.github.com>
Yohei Tamura <tamuhey@gmail.com> Yohei Tamura <tamuhey@gmail.com>
Maximilian Köhl <mail@koehlma.de> Maximilian Köhl <mail@koehlma.de>
Alexander Sosedkin <monk@unboiled.info> Alexander Sosedkin <monk@unboiled.info>
doronz88 <doron88@gmail.com>
Cody Scott <cody.j.b.scott@gmail.com> Cody Scott <cody.j.b.scott@gmail.com>
Jake Hedman <jake@hedman.email> Jake Hedman <jake@hedman.email>
traverseda <traverse.da@gmail.com> traverseda <traverse.da@gmail.com>
@ -107,7 +108,6 @@ Jared Crawford <jmcrawford45@gmail.com>
Mike Crowe <drmikecrowe@gmail.com> drmikecrowe <drmikecrowe@gmail.com> Mike Crowe <drmikecrowe@gmail.com> drmikecrowe <drmikecrowe@gmail.com>
Vasilis Gerakaris <vasilis.gerakaris@navarino.gr> Vasilis Gerakaris <vgerak@gmail.com> Vasilis Gerakaris <vasilis.gerakaris@navarino.gr> Vasilis Gerakaris <vgerak@gmail.com>
Angus Hollands <goosey15@gmail.com> Angus Hollands <goosey15@gmail.com>
doronz88 <doron88@gmail.com>
JuanPablo <jpabloaj@gmail.com> JuanPablo <jpabloaj@gmail.com>
Ollie Terrance <ollie.terrance@live.co.uk> Ollie Terrance <ollie.terrance@live.co.uk>
Marcel Bollmann <bollmann@linguistics.rub.de> Marcel Bollmann <bollmann@linguistics.rub.de>
@ -214,6 +214,7 @@ cmidkiff87 <39914727+cmidkiff87@users.noreply.github.com>
jbw3 <jbw3@users.noreply.github.com> jbw3 <jbw3@users.noreply.github.com>
Naveen <172697+naveensrinivasan@users.noreply.github.com> Naveen <172697+naveensrinivasan@users.noreply.github.com>
Blake Ramsdell <blaker@gmail.com> Blake Ramsdell <blaker@gmail.com>
JamesParrott <80779630+JamesParrott@users.noreply.github.com>
jyn <github@jyn.dev> jyn <github@jyn.dev>
Dan Allan <dallan@bnl.gov> Dan Allan <dallan@bnl.gov>
Ned Letcher <nletcher@gmail.com> Ned Letcher <nletcher@gmail.com>
@ -345,7 +346,6 @@ Wilfried Pollan <gitwipo@users.noreply.github.com>
Jacqueline Leykam <me@jacqueline.id.au> Jacqueline Leykam <me@jacqueline.id.au>
Joshix-1 <57299889+Joshix-1@users.noreply.github.com> Joshix-1 <57299889+Joshix-1@users.noreply.github.com>
Nathan Monfils <nathanmonfils@gmail.com> Nathan Monfils <nathanmonfils@gmail.com>
JamesParrott <80779630+JamesParrott@users.noreply.github.com>
Airat Makhmutov <108220148+rautyrauty@users.noreply.github.com> Airat Makhmutov <108220148+rautyrauty@users.noreply.github.com>
Matthieu LAURENT <matthieu.laurent69@protonmail.com> Matthieu LAURENT <matthieu.laurent69@protonmail.com>
Daniel Saunders <dsaunders410@gmail.com> Daniel Saunders <dsaunders410@gmail.com>
@ -355,5 +355,12 @@ amacfie-tc <91503417+amacfie-tc@users.noreply.github.com>
lunrenyi <87307989+lunrenyi@users.noreply.github.com> lunrenyi <87307989+lunrenyi@users.noreply.github.com>
Spencer Bliven <spencer.bliven@gmail.com> Spencer Bliven <spencer.bliven@gmail.com>
Niraj Kulkarni <kulkarniniraj14@gmail.com> Niraj Kulkarni <kulkarniniraj14@gmail.com>
Aidan Courtney <aidanfc97@gmail.com>
Max Nordlund <max.nordlund@gmail.com>
Shawn Wallace <yungwallace@live.com>
Faidon Liambotis <paravoid@debian.org>
Jueun Lee <seirios0107@gmail.com>
Simon Billinge <sbillinge@users.noreply.github.com>
Bala <kumaran.4353@gmail.com>
goodboy <tgoodlet@users.noreply.github.com> goodboy <tgoodlet@users.noreply.github.com>
Atsushi Morimoto <atsushi.morimoto@dena.com> Atsushi Morimoto <atsushi.morimoto@dena.com>

View file

@ -1,6 +1,6 @@
default_language_version: default_language_version:
# force all unspecified python hooks to run python3 # force all unspecified python hooks to run python3
python: python3.10 python: python3.13
#ci: #ci:
# autofix_prs: false # autofix_prs: false
@ -8,7 +8,7 @@ default_language_version:
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: 'v0.5.6' rev: 'v0.8.2'
hooks: hooks:
- id: ruff - id: ruff
args: [., --fix, --exit-non-zero-on-fix] args: [., --fix, --exit-non-zero-on-fix]
@ -18,14 +18,14 @@ repos:
pass_filenames: false pass_filenames: false
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v1.11.1' # Use the sha / tag you want to point at rev: 'v1.13.0' # Use the sha / tag you want to point at
hooks: hooks:
- id: mypy - id: mypy
pass_filenames: false pass_filenames: false
additional_dependencies: additional_dependencies:
- types-ujson - types-ujson
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0 rev: v5.0.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
exclude: | exclude: |

View file

@ -14,8 +14,8 @@ Authors are sorted by number of commits.
* Bob Hyman * Bob Hyman
* anki-code * anki-code
* BlahGeek * BlahGeek
* Daniel Shimon
* pre-commit-ci[bot] * pre-commit-ci[bot]
* Daniel Shimon
* Gyuri Horak * Gyuri Horak
* Jean-Benoist Leger * Jean-Benoist Leger
* christopher * christopher
@ -24,9 +24,9 @@ Authors are sorted by number of commits.
* Burak Yiğit Kaya * Burak Yiğit Kaya
* Matthias Bussonnier * Matthias Bussonnier
* Aaron Griffin * Aaron Griffin
* Peter Ye
* Rob Brewer * Rob Brewer
* virus * virus
* Peter Ye
* Sagar Tewari * Sagar Tewari
* Gordon Ball * Gordon Ball
* Eadaen1 * Eadaen1
@ -70,6 +70,7 @@ Authors are sorted by number of commits.
* Yohei Tamura * Yohei Tamura
* Maximilian Köhl * Maximilian Köhl
* Alexander Sosedkin * Alexander Sosedkin
* doronz88
* Cody Scott * Cody Scott
* Jake Hedman * Jake Hedman
* traverseda * traverseda
@ -82,7 +83,6 @@ Authors are sorted by number of commits.
* Mike Crowe * Mike Crowe
* Vasilis Gerakaris * Vasilis Gerakaris
* Angus Hollands * Angus Hollands
* doronz88
* JuanPablo * JuanPablo
* Ollie Terrance * Ollie Terrance
* Marcel Bollmann * Marcel Bollmann
@ -189,6 +189,7 @@ Authors are sorted by number of commits.
* jbw3 * jbw3
* Naveen * Naveen
* Blake Ramsdell * Blake Ramsdell
* JamesParrott
* jyn * jyn
* Dan Allan * Dan Allan
* Ned Letcher * Ned Letcher
@ -320,7 +321,6 @@ Authors are sorted by number of commits.
* Jacqueline Leykam * Jacqueline Leykam
* Joshix-1 * Joshix-1
* Nathan Monfils * Nathan Monfils
* JamesParrott
* Airat Makhmutov * Airat Makhmutov
* Matthieu LAURENT * Matthieu LAURENT
* Daniel Saunders * Daniel Saunders
@ -330,5 +330,12 @@ Authors are sorted by number of commits.
* lunrenyi * lunrenyi
* Spencer Bliven * Spencer Bliven
* Niraj Kulkarni * Niraj Kulkarni
* Aidan Courtney
* Max Nordlund
* Shawn Wallace
* Faidon Liambotis
* Jueun Lee
* Simon Billinge
* Bala
* goodboy * goodboy
* Atsushi Morimoto * Atsushi Morimoto

View file

@ -4,6 +4,88 @@ Xonsh Change Log
.. current developments .. current developments
v0.19.0
====================
**Added:**
* env: Added ``$XONSH_SUPPRESS_WELCOME`` variable to suppress the welcome message.
**Changed:**
* replaced `case_insensitive_dictionary` dependency with local
`CaseInsensitiveDict` class
**Fixed:**
* parsers: fix deprecation warnings triggered on python3.13
* Fix DeprecationWarning while initializing Expression
**Authors:**
* Gil Forsyth
* pre-commit-ci[bot]
* Evgeny
* doronz88
* JamesParrott
* Jueun Lee
* Simon Billinge
* Bala
v0.18.4
====================
**Changed:**
* Now SystemExit that invoked from callable alias follows to exiting from callable alias instead of exiting the entire shell.
**Fixed:**
* Built-in commands such as `xonfig -h` and `xontrib -h` no longer cause the shell to exit.
* Fixed incorrect quoting behaviour in `activate.xsh` for virtualenv version 20.26.6.
**Authors:**
* anki-code
* pre-commit-ci[bot]
* Peter Ye
* Max Nordlund
* Shawn Wallace
* Faidon Liambotis
v0.18.3
====================
**Added:**
* executables: Added ``locate_relative_path`` functionality to ``locate_file``.
**Changed:**
* ``$MULTILINE_PROMPT`` changed from ``'.'`` to ``' '`` (space)
to have an ability to copy the multiline functions from shell history without deleting the dot manually (#5624 #5634).
**Fixed:**
* Fixed ``$AUTO_CD`` regress after previous refactoring.
* Partial fix for "Bad file descriptor" in case of callable alias with execx invocation inside e.g. ExecAlias (#5645).
* completer: Fixed exception when in python-only completion context (#5632).
* Fixed exception "object has no attribute readlines" in case of redirect callable alias output.
**Authors:**
* Gil Forsyth
* Noortheen Raja
* anki-code
* pre-commit-ci[bot]
* Aidan Courtney
v0.18.2 v0.18.2
==================== ====================
@ -2692,7 +2774,7 @@ v0.8.6
* Exits after concatenating normal files which have a finite size * Exits after concatenating normal files which have a finite size
* Continues to run for special files which do not have a size, * Continues to run for special files which do not have a size,
such as ``/dev/random`` such as ``/dev/random``
* Is interruptable in all cases with Crtl-C. * Is interruptable in all cases with Ctrl-C.
* Callable aliases were not properly raising a ``CalledProcessError`` when they * Callable aliases were not properly raising a ``CalledProcessError`` when they
returned a non-zero exist status when ``$RAISE_SUBPROC_ERROR = True``. This has returned a non-zero exist status when ``$RAISE_SUBPROC_ERROR = True``. This has
been fixed. been fixed.
@ -4434,7 +4516,7 @@ v0.5.0
* Fixed many PEP8 violations that had gone unnoticed * Fixed many PEP8 violations that had gone unnoticed
* Fix failure to detect an Anaconda python distribution if the python was install from the conda-forge channel. * Fix failure to detect an Anaconda python distribution if the python was install from the conda-forge channel.
* current_branch will try and locate the vc binary once * current_branch will try and locate the vc binary once
* May now Crtl-C out of an infinite loop with a subprocess, such as * May now Ctrl-C out of an infinite loop with a subprocess, such as
```while True: sleep 1``. ```while True: sleep 1``.
* Fix for stdin redirects. * Fix for stdin redirects.
* Backgrounding works with ``$XONSH_STORE_STDOUT`` * Backgrounding works with ``$XONSH_STORE_STDOUT``

View file

@ -131,7 +131,8 @@ The xonsh shell is developed by a community of volunteers. There are a few ways
- Solve a `popular issue <https://github.com/xonsh/xonsh/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc>`_ or `high priority issue <https://github.com/xonsh/xonsh/issues?q=is%3Aopen+is%3Aissue+label%3Apriority-high+sort%3Areactions-%2B1-desc>`_ or a `good first issue <https://github.com/xonsh/xonsh/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+sort%3Areactions-%2B1-desc>`_. You can start with the `Developer guide <https://xon.sh/devguide.html>`_. - Solve a `popular issue <https://github.com/xonsh/xonsh/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc>`_ or `high priority issue <https://github.com/xonsh/xonsh/issues?q=is%3Aopen+is%3Aissue+label%3Apriority-high+sort%3Areactions-%2B1-desc>`_ or a `good first issue <https://github.com/xonsh/xonsh/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+sort%3Areactions-%2B1-desc>`_. You can start with the `Developer guide <https://xon.sh/devguide.html>`_.
- Take an `idea <https://github.com/xonsh/xontrib-template/issues?q=is%3Aopen+is%3Aissue+label%3Aidea+sort%3Areactions-%2B1-desc>`_ and `create a new xontrib <https://github.com/xonsh/xontrib-template#why-use-this-template>`_. - Take an `idea <https://github.com/xonsh/xontrib-template/issues?q=is%3Aopen+is%3Aissue+label%3Aidea+sort%3Areactions-%2B1-desc>`_ and `create a new xontrib <https://github.com/xonsh/xontrib-template#why-use-this-template>`_.
- Become xonsh core by deep diving into xonsh and improve the threading and subprocess logic. - Contribute to `xonsh API <https://github.com/xonsh/xonsh/tree/main/xonsh/api>`_.
- Become xonsh core developer by deep diving into xonsh internals. E.g. we feel a lack of Windows support.
- `Become a sponsor to xonsh <https://github.com/sponsors/xonsh>`_. - `Become a sponsor to xonsh <https://github.com/sponsors/xonsh>`_.
- `Write a tweet`_, post or an article to spread the good word about xonsh in the world. - `Write a tweet`_, post or an article to spread the good word about xonsh in the world.
- Give a star to xonsh repository and to `xontribs <https://github.com/topics/xontrib>`_ you like. - Give a star to xonsh repository and to `xontribs <https://github.com/topics/xontrib>`_ you like.

View file

@ -123,7 +123,7 @@
<div class="d-table-cell align-middle" > <div class="d-table-cell align-middle" >
<h2 style="color:white;"><b>XONSH</b> is a Python-powered shell</h2> <h2 style="color:white;"><b>XONSH</b> is a Python-powered shell</h2>
<br> <br>
<p style="font-size: 20px; font-weight: 1;">Xonsh is a modern, full-featured and cross-platform shell. The language is a superset of Python 3.6+ with additional shell primitives that you are used to from Bash and IPython. <p style="font-size: 20px; font-weight: 1;">Xonsh is a modern, full-featured and cross-platform python shell. The language is a superset of Python 3.6+ with additional shell primitives that you are used to from Bash and IPython.
It works on all major systems including Linux, OSX, and Windows. Xonsh is meant for the daily use of experts and novices.</p> It works on all major systems including Linux, OSX, and Windows. Xonsh is meant for the daily use of experts and novices.</p>
<a href="#" data-scroll-nav="3"><i class="icofont-download"></i> Install</a> <a href="#" data-scroll-nav="3"><i class="icofont-download"></i> Install</a>
<a href="https://xon.sh/contents.html" target="_blank"><i class="icofont-ui-note"></i> Docs</a> <a href="https://xon.sh/contents.html" target="_blank"><i class="icofont-ui-note"></i> Docs</a>
@ -139,7 +139,7 @@
<section id="about-area" data-scroll-index="1"> <section id="about-area" data-scroll-index="1">
<div class="container text-center section-heading"> <div class="container text-center section-heading">
<h2>What is Xonsh?</h2> <h2>What is Xonsh?</h2>
<p>The xonsh shell lets you easily mix Python and shell commands in a powerful and simplified approach to the command line.</p> <p>The xonsh as a python shell lets you easily mix Python and shell commands in a powerful and simplified approach to the command line.</p>
</div> </div>
<div class="container" style="margin-top: 30px"> <div class="container" style="margin-top: 30px">

View file

@ -70,7 +70,7 @@ independent of the setting of the $AUTO_CONTINUE option.
``EOF``, ``exit``, and ``quit`` ``EOF``, ``exit``, and ``quit``
=================================== ===================================
The commands ``EOF``, ``exit``, and ``quit`` all alias the same action, which is to The commands ``EOF``, ``exit``, and ``quit`` all alias the same action, which is to
leave xonsh in a safe manner. Typing ``Crtl-d`` is the same as typing ``EOF`` and leave xonsh in a safe manner. Typing ``Ctrl-d`` is the same as typing ``EOF`` and
pressing enter. pressing enter.

View file

@ -135,6 +135,17 @@ variable in Python. The same is true for deleting them too.
Become the Lord of the Files Become the Lord of the Files
>>> del $GOAL >>> del $GOAL
>>> $NUM = "123"
>>> $EXT = $NUM + "456"
>>> $EXT
'123456'
>>> $FNUM = f"{$NUM}456" # Not working with Python 3.12+ (https://github.com/xonsh/xonsh/issues/5166).
>>> $FNUM = "{FILLME}456".format(FILLME=$NUM)
>>> $FNUM
'123456'
>>> "%s456" % $NUM
'123456'
Very nice. Very nice.
.. note:: .. note::
@ -142,6 +153,7 @@ Very nice.
To update ``os.environ`` when the xonsh environment changes set To update ``os.environ`` when the xonsh environment changes set
:ref:`$UPDATE_OS_ENVIRON <update_os_environ>` to ``True``. :ref:`$UPDATE_OS_ENVIRON <update_os_environ>` to ``True``.
The Environment Itself ``${...}`` The Environment Itself ``${...}``
--------------------------------- ---------------------------------
@ -543,12 +555,12 @@ For example, the ``echo`` command has no interaction with the user and is captur
However, some tools have mixed behavior and can be run for either interactive or non-interactive tasks. However, some tools have mixed behavior and can be run for either interactive or non-interactive tasks.
The best example of this is ``ssh``, which allows for remote terminal sessions and executing commands. The best example of this is ``ssh``, which allows for remote terminal sessions and executing commands.
To handle different types of tasks, xonsh has the ``xthread`` and ``xunthread`` built-in aliases. To handle different types of tasks, xonsh has the ``@thread`` and ``@unthread`` built-in decorator aliases.
If you need to capture the output from an interactive tool that has a capturable mode use ``xthread`` to run: If you need to capture the output from an interactive tool that has a capturable mode use ``@thread`` to run:
.. code-block:: xonshcon .. code-block:: xonshcon
@ !(xthread ssh host -T 'echo remote') @ !(@thread ssh host -T 'echo remote')
CommandPipeline(output="remote") CommandPipeline(output="remote")

View file

@ -44,8 +44,6 @@ from being consumed.
Other languages like Lisp, Forth, and Julia also provide their macro systems. Other languages like Lisp, Forth, and Julia also provide their macro systems.
Even restructured text (rST) directives could be considered macros. Even restructured text (rST) directives could be considered macros.
Haskell and other more purely functional languages do not need macros (since
evaluation is lazy anyway), and so do not have them.
If these seem unfamiliar to the Python world, note that Jupyter and IPython If these seem unfamiliar to the Python world, note that Jupyter and IPython
magics ``%`` and ``%%`` are macros! magics ``%`` and ``%%`` are macros!

View file

@ -41,7 +41,7 @@ initializes your personal run control file (usually at ``~/.xonshrc``). To invo
.. code-block:: xonshcon .. code-block:: xonshcon
>>> xonfig web >>> xonfig web
Web config started at 'http://localhost:8421'. Hit Crtl+C to stop. Web config started at 'http://localhost:8421'. Hit Ctrl+C to stop.
127.0.0.1 - - [23/Aug/2020 15:04:39] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [23/Aug/2020 15:04:39] "GET / HTTP/1.1" 200 -
This will open your default browser on a page served from a local server. You can exit the server by typing ``Ctrl+c`` at any time. This will open your default browser on a page served from a local server. You can exit the server by typing ``Ctrl+c`` at any time.

View file

@ -1,23 +0,0 @@
**Added:**
* <news item>
**Changed:**
* <news item>
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* Partial fix for "Bad file descriptor" in case of callable alias with execx invocation inside e.g. ExecAlias (#5645).
**Security:**
* <news item>

View file

@ -1,23 +0,0 @@
**Added:**
* <news item>
**Changed:**
* <news item>
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* completer: Fixed exception when in python-only completion context (#5632).
**Security:**
* <news item>

View file

@ -1,23 +0,0 @@
**Added:**
* <news item>
**Changed:**
* <news item>
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* Fixed exception "object has no attribute readlines" in case of redirect callable alias output.
**Security:**
* <news item>

View file

@ -16,7 +16,7 @@
**Fixed:** **Fixed:**
* Fixed ``$AUTO_CD`` regress after previous refactoring. * Fixed non-int sys.exit codes raising ValueError.
**Security:** **Security:**

View file

@ -1,23 +0,0 @@
**Added:**
* executables: Added ``locate_relative_path`` functionality to ``locate_file``.
**Changed:**
* <news item>
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* <news item>
**Security:**
* <news item>

View file

@ -1,24 +0,0 @@
**Added:**
* <news item>
**Changed:**
* ``$MULTILINE_PROMPT`` changed from ``'.'`` to ``' '`` (space)
to have an ability to copy the multiline functions from shell history without deleting the dot manually (#5624 #5634).
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* <news item>
**Security:**
* <news item>

View file

@ -15,9 +15,7 @@ authors = [{ name = "Anthony Scopatz" }, { email = "scopatz@gmail.com" }]
maintainers = [{ name = "Xonsh Community" }, { email = "xonsh@gil.forsyth.dev" }] maintainers = [{ name = "Xonsh Community" }, { email = "xonsh@gil.forsyth.dev" }]
license = { text = "BSD 2-Clause License" } license = { text = "BSD 2-Clause License" }
requires-python = ">=3.9" requires-python = ">=3.9"
dependencies = [ dependencies = []
"case-insensitive-dictionary; platform_system=='Windows'",
]
[tool.setuptools.dynamic] [tool.setuptools.dynamic]
version = {attr = "xonsh.__version__"} version = {attr = "xonsh.__version__"}
@ -137,7 +135,6 @@ doc = [
"psutil", "psutil",
"pyzmq", "pyzmq",
"matplotlib", "matplotlib",
"doctr",
"tornado", "tornado",
"runthis-sphinxext", "runthis-sphinxext",
"livereload", "livereload",
@ -204,6 +201,7 @@ ignore = [
"E402", # Module level import not at top of file "E402", # Module level import not at top of file
"E501", # line length "E501", # line length
"E731", # Do not assign a lambda expression, use a def "E731", # Do not assign a lambda expression, use a def
"UP031", # Use format specifiers instead of percent format
] ]
select = [ select = [
"B", # https://beta.ruff.rs/docs/rules/#flake8-bugbear-b "B", # https://beta.ruff.rs/docs/rules/#flake8-bugbear-b

View file

@ -10,6 +10,7 @@ from xonsh.parser import Parser
from xonsh.parsers.ast import AST, Call, Pass, With, is_const_str from xonsh.parsers.ast import AST, Call, Pass, With, is_const_str
from xonsh.parsers.fstring_adaptor import FStringAdaptor from xonsh.parsers.fstring_adaptor import FStringAdaptor
from xonsh.pytest.tools import ( from xonsh.pytest.tools import (
ON_WINDOWS,
VER_MAJOR_MINOR, VER_MAJOR_MINOR,
nodes_equal, nodes_equal,
skip_if_pre_3_8, skip_if_pre_3_8,
@ -2652,7 +2653,14 @@ def test_echo_slash_question(check_xonsh_ast):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"case", "case",
[ [
"[]", pytest.param(
"[]",
marks=pytest.mark.xfail(
ON_WINDOWS,
reason="non-zero exit code being raised by brackets",
strict=True,
), # TODO: fix this on a windows machine
),
"[[]]", "[[]]",
"[a]", "[a]",
"[a][b]", "[a][b]",

View file

@ -8,7 +8,19 @@ import pytest
from xonsh.platform import ON_WINDOWS from xonsh.platform import ON_WINDOWS
from xonsh.procs.pipelines import CommandPipeline from xonsh.procs.pipelines import CommandPipeline
from xonsh.pytest.tools import skip_if_on_unix, skip_if_on_windows from xonsh.pytest.tools import (
VER_MAJOR_MINOR,
skip_if_on_unix,
skip_if_on_windows,
)
# TODO: track down which pipeline + spec test is hanging CI
# Skip entire test file for Linux on Python 3.12
pytestmark = pytest.mark.skipif(
not ON_WINDOWS and VER_MAJOR_MINOR == (3, 12),
reason="Backgrounded test is hanging on CI on 3.12 only",
allow_module_level=True,
)
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
@ -122,6 +134,7 @@ def test_casting(cmdline, result, xonsh_execer):
@skip_if_on_windows @skip_if_on_windows
@skip_if_on_unix
def test_background_pgid(xonsh_session, monkeypatch): def test_background_pgid(xonsh_session, monkeypatch):
monkeypatch.setitem(xonsh_session.env, "XONSH_INTERACTIVE", True) monkeypatch.setitem(xonsh_session.env, "XONSH_INTERACTIVE", True)
pipeline = xonsh_session.execer.eval("![echo hi &]") pipeline = xonsh_session.execer.eval("![echo hi &]")

View file

@ -17,9 +17,17 @@ from xonsh.procs.specs import (
cmds_to_specs, cmds_to_specs,
run_subproc, run_subproc,
) )
from xonsh.pytest.tools import skip_if_on_windows from xonsh.pytest.tools import ON_WINDOWS, VER_MAJOR_MINOR, skip_if_on_windows
from xonsh.tools import XonshError from xonsh.tools import XonshError
# TODO: track down which pipeline + spec test is hanging CI
# Skip entire test file for Linux on Python 3.12
pytestmark = pytest.mark.skipif(
not ON_WINDOWS and VER_MAJOR_MINOR == (3, 12),
reason="Backgrounded test is hanging on CI on 3.12 only",
allow_module_level=True,
)
def cmd_sig(sig): def cmd_sig(sig):
return [ return [

View file

@ -46,16 +46,6 @@ def check_token(xsh):
_cases = { _cases = {
"ls": {
"ls -al": [
(Name.Builtin, "ls"),
],
},
"ls-bin": {
"/bin/ls -al": [
(Name.Builtin, "/bin/ls"),
],
},
"print": { "print": {
'print("hello")': [ 'print("hello")': [
(Name.Builtin, "print"), (Name.Builtin, "print"),
@ -94,6 +84,69 @@ _cases = {
(Error, "non-existance-cmd"), (Error, "non-existance-cmd"),
], ],
}, },
"nested": {
"print($(cd))": [
(Name.Builtin, "print"),
(Punctuation, "("),
(Keyword, "$"),
(Punctuation, "("),
(Name.Builtin, "cd"),
(Punctuation, ")"),
(Punctuation, ")"),
(Text.Whitespace, "\n"),
],
},
"subproc-args": {
"cd 192.168.0.1": [
(Text, "192.168.0.1"),
],
},
"backtick": {
r"echo g`.*\w+`": [
(String.Affix, "g"),
(String.Backtick, "`"),
(String.Regex, "."),
(String.Regex, "*"),
(String.Escape, r"\w"),
],
},
"macro": {
r"g!(42, *, 65)": [
(Name, "g"),
(Keyword, "!"),
(Punctuation, "("),
(Number.Integer, "42"),
],
r"bash -c ! export var=42; echo $var": [
(Name.Builtin, "bash"),
(Text, "-c"),
(Keyword, "!"),
(String, "export var=42; echo $var"),
],
},
}
_cases_no_win = {
"ls": {
"ls -al": [
(Name.Builtin, "ls"),
],
},
"ls-bin": {
"/bin/ls -al": [
(Name.Builtin, "/bin/ls"),
],
},
"print": {
'print("hello")': [
(Name.Builtin, "print"),
(Punctuation, "("),
(Literal.String.Double, '"'),
(Literal.String.Double, "hello"),
(Literal.String.Double, '"'),
(Punctuation, ")"),
(Text.Whitespace, "\n"),
]
},
"nested": { "nested": {
'echo @("hello")': [ 'echo @("hello")': [
(Name.Builtin, "echo"), (Name.Builtin, "echo"),
@ -125,20 +178,6 @@ _cases = {
(Text.Whitespace, "\n"), (Text.Whitespace, "\n"),
], ],
}, },
"subproc-args": {
"cd 192.168.0.1": [
(Text, "192.168.0.1"),
],
},
"backtick": {
r"echo g`.*\w+`": [
(String.Affix, "g"),
(String.Backtick, "`"),
(String.Regex, "."),
(String.Regex, "*"),
(String.Escape, r"\w"),
],
},
"macro": { "macro": {
r"g!(42, *, 65)": [ r"g!(42, *, 65)": [
(Name, "g"), (Name, "g"),
@ -167,12 +206,23 @@ def _convert_cases():
yield pytest.param(*item, id=f"{title}-{idx}") yield pytest.param(*item, id=f"{title}-{idx}")
def _convert_cases_no_win():
for title, input_dict in _cases_no_win.items():
for idx, item in enumerate(input_dict.items()):
yield pytest.param(*item, id=f"{title}-{idx}")
@pytest.mark.parametrize("inp, expected", list(_convert_cases())) @pytest.mark.parametrize("inp, expected", list(_convert_cases()))
@skip_if_on_windows
def test_xonsh_lexer(inp, expected, check_token): def test_xonsh_lexer(inp, expected, check_token):
check_token(inp, expected) check_token(inp, expected)
@pytest.mark.parametrize("inp, expected", list(_convert_cases_no_win()))
@skip_if_on_windows
def test_xonsh_lexer_no_win(inp, expected, check_token):
check_token(inp, expected)
# can't seem to get thie test to import pyghooks and define on_lscolors_change handler like live code does. # can't seem to get thie test to import pyghooks and define on_lscolors_change handler like live code does.
# so we declare the event handler directly here. # so we declare the event handler directly here.
@pytest.fixture @pytest.fixture

View file

@ -8,6 +8,7 @@ import pytest
from xonsh.commands_cache import ( from xonsh.commands_cache import (
SHELL_PREDICTOR_PARSER, SHELL_PREDICTOR_PARSER,
CaseInsensitiveDict,
CommandsCache, CommandsCache,
_Commands, _Commands,
executables_in, executables_in,
@ -306,3 +307,77 @@ def test_executables_in(xession):
else: else:
result = set(executables_in(test_path)) result = set(executables_in(test_path))
assert expected == result assert expected == result
def test_caseinsdict_constructor():
actual = CaseInsensitiveDict({"key1": "val1", "Key2": "Val2"})
assert isinstance(actual, CaseInsensitiveDict)
assert actual["key1"] == "val1"
assert actual["Key2"] == "Val2"
def test_caseinsdict_getitem():
actual = CaseInsensitiveDict({"Key1": "Val1"})
assert actual["Key1"] == "Val1"
assert actual["key1"] == "Val1"
def test_caseinsdict_setitem():
actual = CaseInsensitiveDict({"Key1": "Val1"})
actual["Key1"] = "Val2"
assert actual["Key1"] == "Val2"
assert actual["key1"] == "Val2"
actual["key1"] = "Val3"
assert actual["Key1"] == "Val3"
assert actual["key1"] == "Val3"
def test_caseinsdict_delitem():
actual = CaseInsensitiveDict({"Key1": "Val1", "Key2": "Val2"})
del actual["Key1"]
assert actual == CaseInsensitiveDict({"Key2": "Val2"})
del actual["key2"]
assert actual == CaseInsensitiveDict({})
def test_caseinsdict_contains():
actual = CaseInsensitiveDict({"Key1": "Val1"})
assert actual.__contains__("Key1")
assert actual.__contains__("key1")
assert not actual.__contains__("key2")
def test_caseinsdict_get():
actual = CaseInsensitiveDict({"Key1": "Val1"})
assert actual.get("Key1") == "Val1"
assert actual.get("key1") == "Val1"
assert actual.get("key2", "no val") == "no val"
assert actual.get("key1", "no val") == "Val1"
def test_caseinsdict_update():
actual = CaseInsensitiveDict({"Key1": "Val1"})
actual.update({"Key2": "Val2"})
assert actual["key2"] == "Val2"
def test_caseinsdict_keys():
actual = CaseInsensitiveDict({"Key1": "Val1"})
assert next(actual.keys()) == "Key1"
def test_caseinsdict_items():
actual = CaseInsensitiveDict({"Key1": "Val1"})
assert next(actual.items()) == ("Key1", "Val1")
def test_caseinsdict_repr():
actual = CaseInsensitiveDict({"Key1": "Val1"})
assert actual.__repr__() == "CaseInsensitiveDict({'Key1': 'Val1'})"
def test_caseinsdict_copy():
initial = CaseInsensitiveDict({"Key1": "Val1"})
actual = initial.copy()
assert actual == initial
assert id(actual) != id(initial)

View file

@ -17,6 +17,7 @@ from xonsh.pytest.tools import (
ON_DARWIN, ON_DARWIN,
ON_TRAVIS, ON_TRAVIS,
ON_WINDOWS, ON_WINDOWS,
VER_FULL,
skip_if_on_darwin, skip_if_on_darwin,
skip_if_on_msys, skip_if_on_msys,
skip_if_on_unix, skip_if_on_unix,
@ -329,6 +330,22 @@ f
"hello\n", "hello\n",
0, 0,
), ),
# test system exit in unthreadable alias (see #5689)
(
"""
from xonsh.tools import unthreadable
@unthreadable
def _f():
import sys
sys.exit(42)
aliases['f'] = _f
print(![f].returncode)
""",
"42\n",
0,
),
# test ambiguous globs # test ambiguous globs
( (
""" """
@ -594,8 +611,9 @@ first
), ),
# testing alias stack: parallel threaded callable aliases. # testing alias stack: parallel threaded callable aliases.
# This breaks if the __ALIAS_STACK variables leak between threads. # This breaks if the __ALIAS_STACK variables leak between threads.
( pytest.param(
""" (
"""
from time import sleep from time import sleep
aliases['a'] = lambda: print(1, end="") or sleep(0.2) or print(1, end="") aliases['a'] = lambda: print(1, end="") or sleep(0.2) or print(1, end="")
aliases['b'] = 'a' aliases['b'] = 'a'
@ -604,8 +622,14 @@ a | a
a | b | a a | b | a
a | a | b | b a | a | b | b
""", """,
"1" * 2 * 4, "1" * 2 * 4,
0, 0,
),
# TODO: investigate errors on Python 3.13
marks=pytest.mark.skipif(
VER_FULL > (3, 12) and not ON_WINDOWS,
reason="broken pipes on Python 3.13 likely due to changes in threading behavior",
),
), ),
# test $SHLVL # test $SHLVL
( (
@ -723,7 +747,7 @@ if not ON_WINDOWS:
@skip_if_no_xonsh @skip_if_no_xonsh
@pytest.mark.parametrize("case", ALL_PLATFORMS) @pytest.mark.parametrize("case", ALL_PLATFORMS)
@pytest.mark.flaky(reruns=3, reruns_delay=2) @pytest.mark.flaky(reruns=4, reruns_delay=2)
def test_script(case): def test_script(case):
script, exp_out, exp_rtn = case script, exp_out, exp_rtn = case
if ON_DARWIN: if ON_DARWIN:

View file

@ -2,12 +2,15 @@ import sys
from pathlib import Path from pathlib import Path
from subprocess import check_output from subprocess import check_output
import pytest
from xonsh.pytest.tools import ON_WINDOWS from xonsh.pytest.tools import ON_WINDOWS
def test_xonsh_activator(tmp_path): @pytest.mark.parametrize("dir_name", ["venv", "venv with space"])
def test_xonsh_activator(tmp_path, dir_name):
# Create virtualenv # Create virtualenv
venv_dir = tmp_path / "venv" venv_dir = tmp_path / dir_name
assert b"XonshActivator" in check_output( assert b"XonshActivator" in check_output(
[sys.executable, "-m", "virtualenv", str(venv_dir)] [sys.executable, "-m", "virtualenv", str(venv_dir)]
) )
@ -35,7 +38,13 @@ def test_xonsh_activator(tmp_path):
# Activate # Activate
venv_python = check_output( venv_python = check_output(
[sys.executable, "-m", "xonsh", "-c", f"source {activate_path}; which python"] [
sys.executable,
"-m",
"xonsh",
"-c",
f"source r'{activate_path}'; which python",
]
).decode() ).decode()
assert Path(venv_python).parent == bin_path assert Path(venv_python).parent == bin_path
@ -46,7 +55,7 @@ def test_xonsh_activator(tmp_path):
"-m", "-m",
"xonsh", "xonsh",
"-c", "-c",
f"source {activate_path}; deactivate; " f"source r'{activate_path}'; deactivate; "
"import shutil; shutil.which('python') or shutil.which('python3')", "import shutil; shutil.which('python') or shutil.which('python3')",
] ]
).decode() ).decode()

View file

@ -1 +1 @@
__version__ = "0.18.2" __version__ = "0.19.0"

2
xonsh/api/README.md Normal file
View file

@ -0,0 +1,2 @@
Xonsh API was introduced to reduce boilerplate code in a solutions that based on xonsh.
We're very welcome to extend Xonsh API with shortcuts, code snippets and helpful functions.

View file

@ -354,7 +354,9 @@ class ArgParser(ap.ArgumentParser):
add_args(parser, func, allowed_params=args, doc=doc) add_args(parser, func, allowed_params=args, doc=doc)
return parser return parser
def _parse_known_args(self, arg_strings: list[str], namespace: ap.Namespace): def _parse_known_args(
self, arg_strings: list[str], namespace: ap.Namespace, *args, **kwargs
):
arg_set = set(arg_strings) arg_set = set(arg_strings)
if ( if (
self.commands self.commands
@ -363,7 +365,7 @@ class ArgParser(ap.ArgumentParser):
and (set(self.commands.choices).isdisjoint(arg_set)) and (set(self.commands.choices).isdisjoint(arg_set))
): ):
arg_strings = [self.default_command] + arg_strings arg_strings = [self.default_command] + arg_strings
return super()._parse_known_args(arg_strings, namespace) return super()._parse_known_args(arg_strings, namespace, *args, **kwargs)
def run_with_partial_args(func: tp.Callable, ns: dict[str, tp.Any]): def run_with_partial_args(func: tp.Callable, ns: dict[str, tp.Any]):
@ -543,6 +545,9 @@ class ArgparseCompleter:
if not act_res: if not act_res:
# it is not a option string: pass # it is not a option string: pass
break break
if isinstance(act_res, list):
assert len(act_res) == 1
act_res = act_res[0]
# it is a valid option and advance # it is a valid option and advance
self.remaining_args = self.remaining_args[1:] self.remaining_args = self.remaining_args[1:]
act, *_, value = act_res act, *_, value = act_res

View file

@ -23,8 +23,52 @@ from xonsh.procs.executables import (
is_executable_in_windows, is_executable_in_windows,
) )
class CaseInsensitiveDict(dict[tp.Any, tp.Any]):
def __init__(self, *args, **kwargs):
super().__init__()
self._store = {}
self.update(*args, **kwargs)
def __setitem__(self, key, value):
# Store the key in lowercase but preserve the original case for display
self._store[key.casefold()] = key
super().__setitem__(key.casefold(), value)
def __getitem__(self, key):
return super().__getitem__(key.casefold())
def __delitem__(self, key):
del self._store[key.casefold()]
super().__delitem__(key.casefold())
def __contains__(self, key):
return key.casefold() in self._store
def get(self, key, default=None):
return super().get(key.casefold(), default)
def update(self, *args, **kwargs):
for k, v in dict(*args, **kwargs).items():
self[k] = v
def keys(self):
# Return the original keys with their original casing
return (self._store[k] for k in self._store)
def items(self):
return ((self._store[k], self[k]) for k in self._store)
def __repr__(self):
return f"{self.__class__.__name__}({dict(self.items())})"
def copy(self):
return CaseInsensitiveDict(self.items())
CacheDict: tp.Union[type[CaseInsensitiveDict], type[dict]]
if ON_WINDOWS: if ON_WINDOWS:
from case_insensitive_dict import CaseInsensitiveDict as CacheDict CacheDict = CaseInsensitiveDict
else: else:
CacheDict = dict CacheDict = dict

View file

@ -33,7 +33,7 @@ def complete_command(command: CommandContext):
kwargs = {} kwargs = {}
if show_desc: if show_desc:
kwargs["description"] = "Alias" if is_alias else path kwargs["description"] = "Alias" if is_alias else path
yield RichCompletion(s, append_space=True, **kwargs) yield RichCompletion(s, append_space=True, **kwargs) # type: ignore
if xp.ON_WINDOWS: if xp.ON_WINDOWS:
for i in executables_in("."): for i in executables_in("."):
if i.startswith(cmd): if i.startswith(cmd):

View file

@ -1627,6 +1627,10 @@ class PromptSetting(Xettings):
"For example, to have stderr appear on a red background, the " "For example, to have stderr appear on a red background, the "
'prefix & postfix pair would be "{BACKGROUND_RED}" & "{RESET}".', 'prefix & postfix pair would be "{BACKGROUND_RED}" & "{RESET}".',
) )
XONSH_SUPPRESS_WELCOME = Var.with_default(
False,
"Suppresses the welcome message.",
)
class PromptHistorySetting(Xettings): class PromptHistorySetting(Xettings):

1
xonsh/lib/README.md Normal file
View file

@ -0,0 +1 @@
Xonsh library contains the code that independent to xonsh core or a code that is fork from another projects.

View file

@ -548,6 +548,7 @@ def main_xonsh(args):
ignore_sigtstp() ignore_sigtstp()
if ( if (
env["XONSH_INTERACTIVE"] env["XONSH_INTERACTIVE"]
and not env["XONSH_SUPPRESS_WELCOME"]
and sys.stdin.isatty() # In case the interactive mode is forced but no tty (input from pipe). and sys.stdin.isatty() # In case the interactive mode is forced but no tty (input from pipe).
and not any(os.path.isfile(i) for i in env["XONSHRC"]) and not any(os.path.isfile(i) for i in env["XONSHRC"])
and not any(os.path.isdir(i) for i in env["XONSHRC_DIR"]) and not any(os.path.isdir(i) for i in env["XONSHRC_DIR"])
@ -605,7 +606,14 @@ def main_xonsh(args):
err_type, err, _ = exc_info err_type, err, _ = exc_info
if err_type is SystemExit: if err_type is SystemExit:
code = getattr(exc_info[1], "code", 0) code = getattr(exc_info[1], "code", 0)
exit_code = int(code) if code is not None else 0 if code is None:
exit_code = 0
else:
exit_code = code
try:
exit_code = int(code)
except ValueError:
pass
XSH.exit = exit_code XSH.exit = exit_code
else: else:
exit_code = 1 exit_code = 1

View file

@ -6,7 +6,9 @@ from xonsh.platform import PYTHON_VERSION_INFO
@lazyobject @lazyobject
def Parser(): def Parser():
if PYTHON_VERSION_INFO > (3, 10): if PYTHON_VERSION_INFO >= (3, 13):
from xonsh.parsers.v313 import Parser as p
elif PYTHON_VERSION_INFO > (3, 10):
from xonsh.parsers.v310 import Parser as p from xonsh.parsers.v310 import Parser as p
elif PYTHON_VERSION_INFO > (3, 9): elif PYTHON_VERSION_INFO > (3, 9):
from xonsh.parsers.v39 import Parser as p from xonsh.parsers.v39 import Parser as p

View file

@ -109,8 +109,10 @@ from ast import ( # noqa # pylint: disable=unused-import
walk, walk,
withitem, withitem,
) )
from typing import Optional
from xonsh.built_ins import XSH from xonsh.built_ins import XSH
from xonsh.platform import PYTHON_VERSION_INFO
from xonsh.tools import find_next_break, get_logical_line, subproc_toks from xonsh.tools import find_next_break, get_logical_line, subproc_toks
STATEMENTS = ( STATEMENTS = (
@ -139,8 +141,25 @@ STATEMENTS = (
) )
def const_str(s: str, **kwargs): def const_str(
return Constant(value=s, kind="str", **kwargs) s: str,
lineno: Optional[int] = None,
col_offset: Optional[int] = None,
is_raw: bool = True,
):
if PYTHON_VERSION_INFO >= (3, 13):
# looks like this attribute is no longer needed to be set explicitly
constant = Constant(value=s, kind="str")
else:
constant = Constant(value=s, kind="str")
if is_raw:
# this attribute is not documented within the ast object
constant.is_raw = is_raw # type: ignore
if lineno is not None:
constant.lineno = lineno
if col_offset is not None:
constant.col_offset = col_offset
return constant
def is_const_str(node): def is_const_str(node):

View file

@ -738,7 +738,9 @@ class BaseParser:
def p_eval_input(self, p): def p_eval_input(self, p):
"""eval_input : testlist newlines_opt""" """eval_input : testlist newlines_opt"""
p1 = p[1] p1 = p[1]
p[0] = ast.Expression(body=p1, lineno=p1.lineno, col_offset=p1.col_offset) p[0] = ast.Expression(body=p1)
p[0].lineno = p1.lineno
p[0].col_offset = p1.col_offset
def p_func_call(self, p): def p_func_call(self, p):
"""func_call : LPAREN arglist_opt RPAREN""" """func_call : LPAREN arglist_opt RPAREN"""
@ -2155,7 +2157,8 @@ class BaseParser:
| minus_tok term | minus_tok term
""" """
p1 = p[1] p1 = p[1]
op = self._term_binops[p1.value](lineno=p1.lineno, col_offset=p1.lexpos) op = self._term_binops[p1.value]()
op.lineno, op.col_offset = p1.lineno, p1.lexpos
p[0] = [op, p[2]] p[0] = [op, p[2]]
def p_term(self, p): def p_term(self, p):
@ -2194,7 +2197,9 @@ class BaseParser:
f"operation {p1!r} not supported", f"operation {p1!r} not supported",
self.currloc(lineno=p.lineno, column=p.lexpos), self.currloc(lineno=p.lineno, column=p.lexpos),
) )
p[0] = [op(lineno=p1.lineno, col_offset=p1.lexpos), p[2]] op_node = op()
op_node.lineno, op_node.col_offset = p1.lineno, p1.lexpos
p[0] = [op_node, p[2]]
_factor_ops = {"+": ast.UAdd, "-": ast.USub, "~": ast.Invert} _factor_ops = {"+": ast.UAdd, "-": ast.USub, "~": ast.Invert}
@ -3047,7 +3052,7 @@ class BaseParser:
else: else:
targ = ensure_has_elts(targs) targ = ensure_has_elts(targs)
store_ctx(targ) store_ctx(targ)
comp = ast.comprehension(target=targ, iter=it, ifs=[]) comp = ast.comprehension(target=targ, iter=it, ifs=[], is_async=0)
comps = [comp] comps = [comp]
p0 = {"comps": comps} p0 = {"comps": comps}
if p5 is not None: if p5 is not None:

264
xonsh/parsers/v313.py Normal file
View file

@ -0,0 +1,264 @@
# type: ignore
# TODO: remove line above once mypy understands the match statement
"""Handles changes since PY313
handle
- import-alias requiring lineno
- match statement
"""
from ast import match_case
from ast import parse as pyparse
from xonsh.parsers import ast
from xonsh.parsers.ast import xonsh_call
from xonsh.parsers.base import (
RE_STRINGPREFIX,
del_ctx,
ensure_has_elts,
lopen_loc,
store_ctx,
)
from xonsh.parsers.fstring_adaptor import FStringAdaptor
from xonsh.parsers.v310 import Parser as ThreeTenParser
class Parser(ThreeTenParser):
def p_eval_input(self, p):
"""eval_input : testlist newlines_opt"""
p1 = p[1]
expression = ast.Expression(body=p1)
expression.lineno = p1.lineno
expression.col_offset = p1.col_offset
p[0] = expression
def p_pm_term(self, p):
"""
pm_term : plus_tok term
| minus_tok term
"""
p1 = p[1]
op = self._term_binops[p1.value]()
op.lineno = p1.lineno
op.col_offset = p1.lexpos
p[0] = [op, p[2]]
def p_atom_lbrace(self, p):
"""atom : lbrace_tok dictorsetmaker_opt RBRACE"""
p1, p2 = p[1], p[2]
p1, p1_tok = p1.value, p1
if p2 is None:
p0 = ast.Dict(
keys=[],
values=[],
lineno=self.lineno,
col_offset=self.col,
)
p0.ctx = ast.Load()
else:
p0 = p2
p0.lineno, p0.col_offset = p1_tok.lineno, p1_tok.lexpos
p[0] = p0
# case blocks
def p_case_block(self, p):
"""
case_block : case_tok patterns COLON suite
| case_tok patterns IF test COLON suite
"""
loc = self.get_line_cols(p, 1)
match list(p):
case [_, _, pattern, _, suite]:
p[0] = match_case(pattern=pattern, body=suite)
p[0].lineno = loc["lineno"]
p[0].end_lineno = loc["end_lineno"]
p[0].col_offset = loc["col_offset"]
p[0].end_col_offset = loc["end_col_offset"]
case [_, _, pattern, _, guard, _, suite]:
p[0] = match_case(pattern=pattern, body=suite, guard=guard)
p[0].lineno = loc["lineno"]
p[0].end_lineno = loc["end_lineno"]
p[0].col_offset = loc["col_offset"]
p[0].end_col_offset = loc["end_col_offset"]
case _:
raise AssertionError()
def p_string_literal(self, p):
"""string_literal : string_tok"""
p1 = p[1]
prefix = RE_STRINGPREFIX.match(p1.value).group().lower()
if "p" in prefix and "f" in prefix:
new_pref = prefix.replace("p", "")
value_without_p = new_pref + p1.value[len(prefix) :]
try:
s = pyparse(value_without_p).body[0].value
except SyntaxError:
s = None
if s is None:
try:
s = FStringAdaptor(
value_without_p, new_pref, filename=self.lexer.fname
).run()
except SyntaxError as e:
self._set_error(
str(e), self.currloc(lineno=p1.lineno, column=p1.lexpos)
)
s = ast.increment_lineno(s, p1.lineno - 1)
p[0] = xonsh_call(
"__xonsh__.path_literal", [s], lineno=p1.lineno, col=p1.lexpos
)
elif "p" in prefix:
value_without_p = prefix.replace("p", "") + p1.value[len(prefix) :]
s = ast.const_str(
s=ast.literal_eval(value_without_p),
lineno=p1.lineno,
col_offset=p1.lexpos,
)
p[0] = xonsh_call(
"__xonsh__.path_literal", [s], lineno=p1.lineno, col=p1.lexpos
)
elif "f" in prefix:
try:
s = pyparse(p1.value).body[0].value
except SyntaxError:
s = None
if s is None:
try:
s = FStringAdaptor(
p1.value, prefix, filename=self.lexer.fname
).run()
except SyntaxError as e:
self._set_error(
str(e), self.currloc(lineno=p1.lineno, column=p1.lexpos)
)
s = ast.increment_lineno(s, p1.lineno - 1)
if "r" in prefix:
s.is_raw = True
p[0] = s
else:
s = ast.literal_eval(p1.value)
is_bytes = "b" in prefix
is_raw = "r" in prefix
cls = ast.const_bytes if is_bytes else ast.const_str
p[0] = cls(s=s, lineno=p1.lineno, col_offset=p1.lexpos)
p[0].is_raw = is_raw
def p_atom_expr_await(self, p):
"""atom_expr : await_tok atom trailer_list_opt"""
p0 = self.apply_trailers(p[2], p[3])
p1 = p[1]
p0 = ast.Await(value=p0, lineno=p1.lineno, col_offset=p1.lexpos)
p0.ctx = ast.Load()
p[0] = p0
#
# For normal assignments, additional restrictions enforced
# by the interpreter
#
def p_del_stmt(self, p):
"""del_stmt : del_tok exprlist"""
p1 = p[1]
p2 = p[2]
for targ in p2:
del_ctx(targ)
p0 = ast.Delete(targets=p2, lineno=p1.lineno, col_offset=p1.lexpos)
p[0] = p0
#
# Dict or set maker
#
def p_dictorsetmaker_t6(self, p):
"""dictorsetmaker : test COLON test comma_item_list comma_opt"""
p1, p4 = p[1], p[4]
keys = [p1]
vals = [p[3]]
for k, v in zip(p4[::2], p4[1::2]):
keys.append(k)
vals.append(v)
lineno, col = lopen_loc(p1)
p[0] = ast.Dict(keys=keys, values=vals, lineno=lineno, col_offset=col)
p[0].ctx = ast.Load()
def p_dictorsetmaker_i4(self, p):
"""dictorsetmaker : item comma_item_list comma_opt"""
p1, p2 = p[1], p[2]
keys = [p1[0]]
vals = [p1[1]]
for k, v in zip(p2[::2], p2[1::2]):
keys.append(k)
vals.append(v)
lineno, col = lopen_loc(p1[0] or p1[1])
p[0] = ast.Dict(keys=keys, values=vals, lineno=lineno, col_offset=col)
p[0].ctx = ast.Load()
def p_dictorsetmaker_t4_dict(self, p):
"""dictorsetmaker : test COLON testlist"""
keys = [p[1]]
vals = self._list_or_elts_if_not_real_tuple(p[3])
lineno, col = lopen_loc(p[1])
p[0] = ast.Dict(keys=keys, values=vals, lineno=lineno, col_offset=col)
p[0].ctx = ast.Load()
def p_dictorsetmaker_item_comma(self, p):
"""dictorsetmaker : item comma_opt"""
p1 = p[1]
keys = [p1[0]]
vals = [p1[1]]
lineno, col = lopen_loc(p1[0] or p1[1])
p[0] = ast.Dict(keys=keys, values=vals, lineno=lineno, col_offset=col)
p[0].ctx = ast.Load()
def p_dictorsetmaker_t4_set(self, p):
"""dictorsetmaker : test_or_star_expr comma_test_or_star_expr_list comma_opt"""
p[0] = ast.Set(elts=[p[1]] + p[2], lineno=self.lineno, col_offset=self.col)
p[0].ctx = ast.Load()
def p_dictorsetmaker_test_comma(self, p):
"""dictorsetmaker : test_or_star_expr comma_opt"""
elts = self._list_or_elts_if_not_real_tuple(p[1])
p[0] = ast.Set(elts=elts, lineno=self.lineno, col_offset=self.col)
p[0].ctx = ast.Load()
def p_dictorsetmaker_testlist(self, p):
"""dictorsetmaker : testlist"""
elts = self._list_or_elts_if_not_real_tuple(p[1])
p[0] = ast.Set(elts=elts, lineno=self.lineno, col_offset=self.col)
p[0].ctx = ast.Load()
def p_op_factor(self, p):
"""
op_factor : times_tok factor
| at_tok factor
| divide_tok factor
| mod_tok factor
| doublediv_tok factor
"""
p1 = p[1]
op = self._term_binops[p1.value]
if op is None:
self._set_error(
f"operation {p1!r} not supported",
self.currloc(lineno=p.lineno, column=p.lexpos),
)
op = op()
op.lineno = p1.lineno
op.col_offset = p1.lexpos
p[0] = [op, p[2]]
def p_comp_for(self, p):
"""comp_for : FOR exprlist IN or_test comp_iter_opt"""
targs, it, p5 = p[2], p[4], p[5]
if len(targs) == 1:
targ = targs[0]
else:
targ = ensure_has_elts(targs)
store_ctx(targ)
comp = ast.comprehension(target=targ, iter=it, ifs=[], is_async=0)
comps = [comp]
p0 = {"comps": comps}
if p5 is not None:
comps += p5.get("comps", [])
comp.ifs += p5.get("if", [])
p[0] = p0

View file

@ -816,6 +816,10 @@ class ProcProxy:
"stack": spec.stack, "stack": spec.stack,
}, },
) )
except SystemExit as e:
# the alias function is running in the main thread, so we need to
# catch SystemExit to prevent the entire shell from exiting (see #5689)
r = e.code if isinstance(e.code, int) else int(bool(e.code))
except Exception: except Exception:
xt.print_exception(source_msg="Exception in " + get_proc_proxy_name(self)) xt.print_exception(source_msg="Exception in " + get_proc_proxy_name(self))
r = 1 r = 1

View file

@ -18,6 +18,7 @@ VER_MAJOR_MINOR = sys.version_info[:2]
VER_FULL = sys.version_info[:3] VER_FULL = sys.version_info[:3]
ON_DARWIN = platform.system() == "Darwin" ON_DARWIN = platform.system() == "Darwin"
ON_WINDOWS = platform.system() == "Windows" ON_WINDOWS = platform.system() == "Windows"
ON_LINUX = platform.system() == "Linux"
ON_MSYS = sys.platform == "msys" ON_MSYS = sys.platform == "msys"
ON_CONDA = True in [ ON_CONDA = True in [
conda in pytest.__file__.lower() for conda in ["conda", "anaconda", "miniconda"] conda in pytest.__file__.lower() for conda in ["conda", "anaconda", "miniconda"]

View file

@ -5,6 +5,11 @@ class XonshActivator(ViaTemplateActivator):
def templates(self): def templates(self):
yield "activate.xsh" yield "activate.xsh"
@staticmethod
def quote(string):
# leave string unchanged since we do quoting in activate.xsh (see #5699)
return string
@classmethod @classmethod
def supports(cls, interpreter): def supports(cls, interpreter):
return interpreter.version_info >= (3, 5) return interpreter.version_info >= (3, 5)

View file

@ -138,7 +138,7 @@ def bind_server_to(
httpd = cls(("", port), handler_cls) httpd = cls(("", port), handler_cls)
url = f"http://localhost:{port}" url = f"http://localhost:{port}"
print(f"Web config started at '{url}'. Hit Crtl+C to stop.") print(f"Web config started at '{url}'. Hit Ctrl+C to stop.")
if browser: if browser:
import webbrowser import webbrowser