https://github.com/xonsh/xonsh/issues/5538
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
### Motivation
Add `spec.raise_subproc_error` to have an ability to use
`SpecModifierAlias` to manage the errors. Also this is the first step to
#4351.
Generally in scripts it's good to have RAISE_SUBPROC_ERROR=True to avoid
processing the error for every executed command. But in some cases (e.g.
`![]`) it's needed to avoid raising the error. To more elegant doing
this we can make an ability to create SpecModifier.
### After
```xsh
from xonsh.procs.specs import SpecModifierAlias
class SpecModifierNoErrAlias(SpecModifierAlias):
def on_modifer_added(self, spec):
spec.raise_subproc_error = False
aliases['noraise'] = SpecModifierNoErrAlias()
$RAISE_SUBPROC_ERROR = True
if ![noraise git pull]:
git add --all
```
Cc #5443
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
<!---
Thanks for opening a PR on xonsh!
Please do this:
1. Include a news file with your PR
(https://xon.sh/devguide.html#changelog).
2. Add the documentation for your feature into `/docs`.
3. Add the example of usage or before-after behavior.
4. Mention the issue that this PR is addressing e.g. `#1234`.
-->
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
This is continuation for introduced before
`$XONSH_SUBPROC_OUTPUT_FORMAT` (#5377) so news file is not needed.
### Motivation
Having `output_format` in spec and in pipeline allows to switch output
format in `SpecModifierAlias` and during non-blocking work e.g.
```xsh
p=!(echo '1\n2\n3')
p.output_format = 'list_lines'
p.out
# ['1','2','3']
```
```xsh
from xonsh.procs.specs import SpecModifierAlias
class SpecModifierOutputLinesAlias(SpecModifierAlias):
def on_modifer_added(self, spec):
spec.output_format = 'list_lines'
aliases['xlines'] = SpecModifierOutputLinesAlias()
$(xlines echo '1\n2\n3')
# ['1','2','3']
```
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Motivation
This is fix for introduced before
`$XONSH_SUBPROC_OUTPUT_FORMAT='list_lines'` so news file is not needed.
We should return the list for single line output to avoid unintended
reading the string.
### Before
```xsh
$XONSH_SUBPROC_OUTPUT_FORMAT='list_lines'
mkdir -p /tmp/list_lines
cd /tmp/list_lines
touch 123
du $(ls)
# 0 123
for f in $(ls):
print(f)
# 1
# 2
# 3
touch 321
for f in $(ls):
print(f)
# 123
# 321
```
### After
```xsh
$XONSH_SUBPROC_OUTPUT_FORMAT='list_lines'
mkdir -p /tmp/list_lines
cd /tmp/list_lines
touch 123
du $(ls)
# 0 123
for f in $(ls):
print(f)
# 123
touch 321
for f in $(ls):
print(f)
# 123
# 321
```
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
Co-authored-by: a <1@1.1>
I'm going to revert #5423 because it was regress -
https://github.com/xonsh/xonsh/issues/5466 - and we need to fix parser
instead of resolver.
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
### Motivation
* We have no recommended way to force subprocess command be
(un)threadable
* #4214
* #2119
* #5003
* It's interesting opportunity to have a way to modify specs and CP
using `SpecModifierAlias`.
### Before
```xsh
!(ssh host -T "echo 1")
# output='' # EXPECTED: 1
__xonsh__.commands_cache.threadable_predictors['ssh'] = lambda *a, **kw: True
!(ssh host -T "echo 1")
```
### After
```xsh
xthread
# Mark command as threadable.
!(xthread ssh host -T "echo 1")
# output='1'
```
Closes:
* Closes#4214
* Closes#2119
* Partially closes#5003
Implementation of `SpecModifierAlias` will help in:
* #2618
JFYI #5413
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Gil Forsyth <gforsyth@users.noreply.github.com>
Reading stop signals from the process and update the process state.
### The issue
Technically. In a couple of places that critical for processing signals
we have `os.waitpid()`. The function behavior is pretty unobvious and
one of things is processing return code after catching the signal. We
had no good signal processing around this and this PR fixes this. See
also `proc_untraced_waitpid` function description.
From user perspective. For example we have process that is waiting for
user input from terminal e.g. `python -c "input()"` or `fzf`. If this
process will be in captured pipeline e.g. `!(echo 1 | fzf | head)` it
will be suspended by OS and the pipeline will be in the endless loop
with future crashing and corrupting std at the end. This PR fixes this.
### The solution
Technically. The key function is `proc_untraced_waitpid` - it catches
the stop signals and updates the process state.
From user perspective. First of all we expect that users will use
captured object `!()` only for capturable processes. Because of it our
goal here is to just make the behavior in this case stable.
In this PR we detect that process in the pipeline is suspended and we
need to finish the command pipeline carefully:
* Show the message about suspended process.
* Keep suspended process in `jobs`. The same behavior we can see in
bash. This is good because we don't know what process suspended and why.
May be experienced user will want to continue it manually.
* Finish the CommandPipeline with returncode=None and suspended=True.
### Before
```xsh
!(fzf) # or !(python -c "input()")
# Hanging / Exceptions / OSError / No way to end the command.
# After exception:
$(echo 1)
# OSError / IO error
```
### After
```xsh
!(fzf) # or `!(ls | fzf | head)` or `!(python -c "input()")`
# Process ['fzf'] with pid 60000 suspended with signal 22 SIGTTOU and stay in `jobs`.
# This happends when process start waiting for input but there is no terminal attached in captured mode.
# CommandPipeline(returncode=None, suspended=True, ...)
$(echo 1)
# Success.
```
Closes#4752#4577
### Notes
* There is pretty edge case situation when the process was terminated so
fast that we can't catch pid alive and check signal
([src](67d672783d/xonsh/jobs.py (L71-L80))).
I leave it as is for now.
### Mentions
#2159
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Gil Forsyth <gforsyth@users.noreply.github.com>
### Before
Case 1: Handler catches the exit signal and do not pass it forward. As
result xonsh could be suspended by OS or crash. Also exit code is wrong.
See repeatable examples with SIGHUP in
https://github.com/xonsh/xonsh/issues/5381#issuecomment-2097961804.
Case 2: From bash/zsh as login shell run xonsh. Then send quit signal to
xonsh. The terminal state will be broken: disabled SIGINT, mouse pointer
produces mouse state codes.
### After
Case 1: Xonsh exit normally with right exit code. Fixed#5381#5304#5371.
Case 2: After exiting with right exit code the state of the terminal is
good.
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Motivation
There is annoying behavior when you run command in the loop and can't
interrupt e.g. [this
report](https://github.com/xonsh/xonsh/discussions/5371) and a bit
#5342. After diving into this I see the issue around return code.
### The essence
Basically ``p = subprocess.Popen()`` populates ``p.returncode`` after
``p.wait()``, ``p.poll()`` or ``p.communicate()``
([doc](https://docs.python.org/3/library/os.html#os.waitpid)). But if
you're using `os.waitpid()` BEFORE these functions you're capturing
return code from a signal subsystem and ``p.returncode`` will be ``0``
like success but it's not success. So after ``os.waitid`` call you need
to set return code manually ``p.returncode = -os.WTERMSIG(status)`` like
in Popen. Example:
```xsh
python # python interactive
import os, signal, subprocess as sp
p = sp.Popen(['sleep', '100'])
p.pid
# 123
p.wait()
# Ctrl+C or `kill -SIGINT 123` from another terminal
p.returncode
# -2
# BUT:
p = sp.Popen(['sleep', '100'])
p.pid
# 123
pid, status = os.waitpid(p.pid, os.WUNTRACED)
# status=2
# From another terminal:
kill -SIGINT 123
p.wait()
# 0
p.returncode
# 0
```
```xsh
from xonsh.tools import describe_waitpid_status
describe_waitpid_status(2)
# WIFEXITED - False - Return True if the process returning status exited via the exit() system call.
# WEXITSTATUS - 0 - Return the process return code from status.
# WIFSIGNALED - True - Return True if the process returning status was terminated by a signal.
# WTERMSIG - 2 - Return the signal that terminated the process that provided the status value.
# WIFSTOPPED - False - Return True if the process returning status was stopped.
# WSTOPSIG - 0 - Return the signal that stopped the process that provided the status value.
```
See also: [Helpful things for
knight](https://github.com/xonsh/xonsh/pull/5361#issuecomment-2078826181).
### Before
```xsh
$RAISE_SUBPROC_ERROR = True
sleep 100
# Ctrl+C
_.rtn
# 0 # It's wrong and RAISE_SUBPROC_ERROR ignored.
for i in range(5):
print(i)
sleep 5
# 0
# Ctrl+C # Can't interrupt
# 1
# 2
```
### After
```xsh
sleep 100
# Ctrl+C
_.rtn
# -2 # Like in default Popen
$RAISE_SUBPROC_ERROR = True
for i in range(5):
print(i)
sleep 5
# 0
# Ctrl+C
# subprocess.CalledProcessError
```
### Notes
* We need to refactor `xonsh.jobs`. It's pretty uncomfortable work with
module.
* The logic is blurry between Specs, Pipelines and Jobs. We need to
bring more clear functions.
* The `captured` variable looks like "just the way of capturing (stdout,
object)" but in fact it affects all logic very much. We need to create
table where we can see the logic difference for every type of capturing.
E.g. in `captured=stdout` mode we use `xonsh.jobs` to monitor the
command but in `captured=object` we avoid this and some logic from
`xonsh.jobs` applied in `stdout` mode but missed in `object` mode. We
need clear map.
## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**
---------
Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Motivation
* To have an ability to manage the output format added ``$XONSH_SUBPROC_OUTPUT_FORMAT`` to switch the way to return the output lines. Default ``stream_lines`` to return text. Alternative ``list_lines`` to return the list of lines. Also supported custom lambda function.
* Additionally the [proposal to change default behavior](https://github.com/xonsh/xonsh/pull/5377#discussion_r1587627131) for a single line case.
* Closes#3924 as soft solution.
### Before
```xsh
mkdir -p /tmp/tst && cd /tmp/tst && touch 1 2 3
$(ls)
# '1\n2\n3\n'
id $(whoami)
# id: ‘pc\n’: no such user: Invalid argument
du $(ls)
# du: cannot access '1'$'\n''2'$'\n''3'$'\n': No such file or directory
ls $(fzf)
# ls: cannot access 'FUNDING.yml'$'\n': No such file or directory
```
### After
```xsh
mkdir -p /tmp/tst && cd /tmp/tst && touch 1 2 3
$XONSH_SUBPROC_OUTPUT_FORMAT = 'list_lines'
$(ls)
# ['1', '2', '3']
[f for f in $(ls)]
# ['1', '2', '3']
id $(whoami)
# uid=501(user) gid=20(staff)
du $(ls)
# 0 1
# 0 2
# 0 3
ls $(fzf)
# FUNDING.yml
# etc
mkdir -p /tmp/@($(whoami))/dir
cat /etc/passwd | grep $(whoami)
```
### Notes
* It will be good to improve parser for cases like `mkdir -p /tmp/$(whoami)/dir`. PR is welcome!
* I named the default mode as `stream_lines` (instead of just `stream` or `raw`) because in fact we transform raw output into stream of lines and possibly reduce the length of output ([replacing `\r\n` to `\n`](c3a12b2a9c/xonsh/procs/pipelines.py (L380-L383))). May be some day we need to add raw "stream" output format.
* Now anybody can implement bash `IFS` behavior in [bashisms](https://github.com/xonsh/xontrib-bashisms).
---------
Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* Change ![] to return an object for background command
* Fix `fg` for commands started in the background
Fixes#4595
This issue was caused by `CommandPipeline.term_pgid` not being set for commands started in the background.
* todo
* test: remove usage of DummyEnv and setting .env attribute on xession fixture
one step closer to making too much of tweaking to xession during tests
* test: fix tests vox and gitstatus-prompt
* docs: update test-fixture usage
* fix: import flake8 error
* test: remove direct access to XSH in tests
* test: remove usage of XSH in test files
* todo
* test: use tmp-dir to create stubs
* refactor: use fixture factory to mock out XonshSession
* refactor: remove direct access of XSH from functions
* refactor: remove direct access of XSH from functions
* fix: qa checks
* refactor: rename variables to match their values
* test: update failing tests because it had no PATH set previously
* fix: remove builtins usage from pyghooks.py
* style:
* refactor: update tests to use fixtures
* fix: env varialbe is setup per function
some tests accidentally update the env variables and that is leaking
into next tests
* fix: failing vox tests
* test: set commands_cache per test
* test: fix failing tests
* fix: failing tests on linux
ptk-highlight
* fix: failing tests on Windows
cwd-prompt
* test: copy env as to not affect original object
* fix: lazily evaluate cmds-cache in pyghooks
* test: fix failing tests
* fix: qa errors import
* test: set commands-cache per test
while caching path results
* test: speedup test_thread_local_swap
* fix: failing tests on windows
* refactor: Execer doesn't control session
* refactor: XSH.unload will take care of reversing builtins attrs set
* test: use env.update over monkeypatch
* Revert "test: use env.update over monkeypatch"
This reverts commit 010a5022247a098f1741966b8af1bf758663480e.
* specs: Make sure sub-specs are always captured regardless of $XONSH_CAPTURE_ALWAYS
* procs: Ensure env is used in functional aliases
* news: Add always-capture-aliases
* tests: procs: Skip known issues
* tests: Restore all builtins in the 'xonsh_builtins' fixture
* tests: vc: Use monkeypatch to mock CommandsCache functions
* tests: main: Don't use the local machine's xonshrc
* refactor: remove usage of global variables in abbrevs.py
* chore: add flake8-mutable to prevent mutable defaults
* fix: abbrevs expand test
* refactor: add xonsh session singleton
* refactor: fix circular errors when using xonshSession as singleton
* refactor: remove black magicked builtin attributes
* style: black format tests as well
* refactor: update tests to use xonsh-session singleton
* refactor: update abbrevs to not use builtins
* test: remove DummyCommandsCache and patch orig class
* fix: failing test_command_completers
* test: use monkeypatch to update xession fixture
* fix: failing test_pipelines
* fix: failing test_main
* chore: run test suit as single invocation
* test: fix tests/test_xonsh.xsh
* refactor: remove builtins from docs/conf.py
* fix: mypy error in jobs
* fix: test error from test_main
* test: close xession error in test_command_completers
* chore: use pytest-cov for reporting coverage
this will include subprocess calls, and will increase coverage
* style: