mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 00:14:41 +01:00
Merge branch 'main' into fix_callias_double_open
This commit is contained in:
commit
1850e14061
43 changed files with 774 additions and 212 deletions
42
.authors.yml
42
.authors.yml
|
@ -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
|
||||||
|
|
1
.github/workflows/publish.yml
vendored
1
.github/workflows/publish.yml
vendored
|
@ -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
|
||||||
|
|
11
.github/workflows/test.yml
vendored
11
.github/workflows/test.yml
vendored
|
@ -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 }}
|
|
||||||
|
|
15
.mailmap
15
.mailmap
|
@ -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>
|
||||||
|
|
|
@ -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: |
|
||||||
|
|
15
AUTHORS.rst
15
AUTHORS.rst
|
@ -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
|
||||||
|
|
|
@ -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``
|
||||||
|
|
|
@ -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.
|
||||||
|
|
4
docs/_templates/index.html
vendored
4
docs/_templates/index.html
vendored
|
@ -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">
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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!
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
**Fixed:**
|
**Fixed:**
|
||||||
|
|
||||||
* Fixed ``$AUTO_CD`` regress after previous refactoring.
|
* Fixed non-int sys.exit codes raising ValueError.
|
||||||
|
|
||||||
**Security:**
|
**Security:**
|
||||||
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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
|
||||||
|
|
|
@ -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]",
|
||||||
|
|
|
@ -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 &]")
|
||||||
|
|
|
@ -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 [
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "0.18.2"
|
__version__ = "0.19.0"
|
||||||
|
|
2
xonsh/api/README.md
Normal file
2
xonsh/api/README.md
Normal 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.
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
1
xonsh/lib/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Xonsh library contains the code that independent to xonsh core or a code that is fork from another projects.
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
264
xonsh/parsers/v313.py
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue