Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alias doesn't set returncode #4752

Closed
sbliven opened this issue Apr 12, 2022 · 1 comment · Fixed by #5361
Closed

Alias doesn't set returncode #4752

sbliven opened this issue Apr 12, 2022 · 1 comment · Fixed by #5361
Labels

Comments

@sbliven
Copy link

sbliven commented Apr 12, 2022

xonfig

$ xonfig
+------------------+----------------------+
| xonsh            | 0.10.1               |
| Git SHA          | e9b12c8b             |
| Commit Date      | Jul 24 22:47:37 2021 |
| Python           | 3.9.10               |
| PLY              | 3.11                 |
| have readline    | True                 |
| prompt toolkit   | 3.0.19               |
| shell type       | readline             |
| history backend  | json                 |
| pygments         | 2.9.0                |
| on posix         | True                 |
| on linux         | False                |
| on darwin        | True                 |
| on windows       | False                |
| on cygwin        | False                |
| on msys2         | False                |
| is superuser     | False                |
| default encoding | utf-8                |
| xonsh encoding   | utf-8                |
| encoding errors  | surrogateescape      |
| on jupyter       | False                |
| jupyter kernel   | None                 |
| xontrib 1        | autoxsh              |
| xontrib 2        | back2dir             |
| xontrib 3        | coreutils            |
| xontrib 4        | output_search        |
| xontrib 5        | pipeliner            |
| xontrib 6        | powerline3           |
+------------------+----------------------+

Expected Behavior

Simple shlex aliases set the return code and other properties correctly

$ aliases["falsy"] = "false"
$ !(falsy).returncode
1

More complicated aliases such as those with a redirect get handled through the Alias mechanism, but I would expect the returned CommandPipeline to have the same properties as if the alias text were executed directly.

Current Behavior

Instead, Alias calls always give 0 status. For instance, adding a redirect forces the alias to be an Alias:

$ aliases['negate'] = "false e>/dev/null"
$ !(negate).returncode
0

I think that other parameters such as IO streams are not set either. For instance, aliases['dog'] = xonsh.aliases.ExecAlias('cat') does not set stdin and stdout correctly, while aliases['dog'] = 'cat' works fine.

For community

⬇️ Please click the 👍 reaction instead of leaving a +1 or 👍 comment

@kpblackatverisk
Copy link

kpblackatverisk commented Jun 23, 2022

Callable aliases appear to have the same with xonsh 0.12.2 on top of python 3.7:

from xonsh.tools import unthreadable
# define a simple callable alias which will write to the passed in stdout object
@unthreadable
def _hw(args, stdin=None, stdout=None, stderr=None):
    print('Hello, World!', file=stdout)
    return 0
aliases['hw'] = _hw
hw
# Hello, World!

c = !(hw)
c.end()
# note above that c.end() did NOT tee the stdout file to the console

# now, note below that the c.output is empty, instead of being "Hello, World!"
print(repr(c))
#CommandPipeline(
#  stdin=None,
#  stdout=<_io.BufferedWriter name=4>,
#  stderr=<_io.TextIOWrapper name=6 mode='w' encoding='cp1252'>,
#  pid=1824,
#  returncode=0,
#  args=['hw'],
#  alias=<function _hw at 0x0000028F80114558>,
#  stdin_redirect=['<stdin>', 'r'],
#  stdout_redirect=[4, 'wb'],
#  stderr_redirect=[6, 'w'],
#  timestamps=[1656014067.1607628, 1656014073.1962628],
#  executed_cmd=[],
#  input='',
#  output='',
#  errors=None
#)

# now a new experiment to write directly to the default stdout
@unthreadable
def _hw2(args):
    print('Hello, World!')
    return 0
aliases['hw2'] = _hw2
hw2
# Hello, World!

c = !(hw2)
c.end()
# Hello, World!

# note that this time the default stdout was tee'd
# but that the c.output is still empty, instead of being "Hello, World!"
print(repr(c))
# CommandPipeline(
#   stdin=None,
#   stdout=<_io.BufferedWriter name=4>,
#   stderr=<_io.TextIOWrapper name=6 mode='w' encoding='cp1252'>,
#   pid=1824,
#   returncode=0,
#   args=['hw2'],
#   alias=<function _hw2 at 0x0000028F80114AF8>,
#   stdin_redirect=['<stdin>', 'r'],
#   stdout_redirect=[4, 'wb'],
#   stderr_redirect=[6, 'w'],
#   timestamps=[1656014284.4926257, 1656014286.2126975],
#   executed_cmd=[],
#   input='',
#   output='',
#   errors=None
# )

@anki-code anki-code changed the title ExecAlias doesn't set returncode & other parameters upon execution Alias doesn't set returncode & other parameters upon execution May 15, 2024
@anki-code anki-code changed the title Alias doesn't set returncode & other parameters upon execution Alias doesn't set returncode May 15, 2024
gforsyth added a commit that referenced this issue May 22, 2024
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](https://github.com/xonsh/xonsh/blob/67d672783db6397bdec7ae44a9d9727b1e20a772/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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants