xonsh/tests/test_integrations.py

1655 lines
38 KiB
Python
Raw Permalink Normal View History

"""Tests involving running Xonsh in subproc.
This requires Xonsh installed in venv or otherwise available on PATH
"""
2016-10-13 02:03:30 -04:00
import os
import re
2016-10-13 02:45:37 -04:00
import shutil
2016-10-16 22:20:36 +03:00
import subprocess as sp
2022-01-31 21:26:34 +05:30
import tempfile
from pathlib import Path
2016-10-13 02:03:30 -04:00
import pytest
import xonsh
2022-03-24 00:46:50 +05:30
from xonsh.dirstack import with_pushd
2022-03-24 00:09:28 +05:30
from xonsh.pytest.tools import (
2022-01-31 21:26:34 +05:30
ON_DARWIN,
ON_TRAVIS,
ON_WINDOWS,
VER_FULL,
2022-01-31 21:26:34 +05:30
skip_if_on_darwin,
skip_if_on_msys,
skip_if_on_unix,
2018-08-30 09:18:49 -05:00
skip_if_on_windows,
)
2016-10-16 21:08:37 +03:00
2018-08-30 09:18:49 -05:00
PATH = (
os.path.join(os.path.abspath(os.path.dirname(__file__)), "bin")
2018-08-30 09:18:49 -05:00
+ os.pathsep
+ os.environ["PATH"]
)
skip_if_no_xonsh = pytest.mark.skipif(
2020-08-10 14:53:31 -04:00
shutil.which("xonsh") is None, reason="xonsh not on PATH"
2019-02-06 14:51:52 -05:00
)
skip_if_no_make = pytest.mark.skipif(
2020-08-10 14:53:31 -04:00
shutil.which("make") is None, reason="make command not on PATH"
2019-02-06 14:51:52 -05:00
)
skip_if_no_sleep = pytest.mark.skipif(
2020-08-10 14:53:31 -04:00
shutil.which("sleep") is None, reason="sleep command not on PATH"
2018-08-30 09:18:49 -05:00
)
2017-02-12 11:42:44 -05:00
base_env = {
"PATH": PATH,
"XONSH_DEBUG": "0",
"XONSH_SHOW_TRACEBACK": "1",
"RAISE_SUBPROC_ERROR": "0",
"FOREIGN_ALIASES_SUPPRESS_SKIP_MESSAGE": "1",
"PROMPT": "",
"TERM": "linux", # disable ansi escape codes
}
2017-02-12 11:42:44 -05:00
2020-08-10 14:53:31 -04:00
def run_xonsh(
cmd,
stdin=sp.PIPE,
stdin_cmd=None,
stdout=sp.PIPE,
stderr=sp.STDOUT,
single_command=False,
interactive=False,
path=None,
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
args=None,
timeout=20,
env=None,
blocking=True,
2020-08-10 14:53:31 -04:00
):
# Env
popen_env = dict(os.environ)
popen_env |= base_env
if path:
popen_env["PATH"] = path
if env:
popen_env |= env
# Args
xonsh = shutil.which("xonsh", path=PATH)
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
popen_args = [xonsh]
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
if not args:
popen_args += ["--no-rc"]
else:
popen_args += args
if interactive:
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
popen_args.append("-i")
if cmd and isinstance(cmd, str) and not cmd.endswith("\n"):
# In interactive mode we need to emulate "Press Enter".
cmd += "\n"
if single_command:
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
popen_args += ["-c", cmd]
input = None
else:
input = cmd
2018-08-30 09:18:49 -05:00
proc = sp.Popen(
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
popen_args,
env=popen_env,
2018-08-30 09:18:49 -05:00
stdin=stdin,
stdout=stdout,
stderr=stderr,
universal_newlines=True,
)
if stdin_cmd:
proc.stdin.write(stdin_cmd)
proc.stdin.flush()
if not blocking:
return proc
2016-10-16 21:08:37 +03:00
try:
out, err = proc.communicate(input=input, timeout=timeout)
2016-10-16 22:20:36 +03:00
except sp.TimeoutExpired:
2016-10-16 21:08:37 +03:00
proc.kill()
raise
return out, err, proc.returncode
2017-02-12 13:11:29 -05:00
def check_run_xonsh(cmd, fmt, exp, exp_rtn=0):
2017-02-12 13:11:29 -05:00
"""The ``fmt`` parameter is a function
that formats the output of cmd, can be None.
"""
2018-09-13 16:49:09 -04:00
out, err, rtn = run_xonsh(cmd, stderr=sp.PIPE)
2017-02-12 13:11:29 -05:00
if callable(fmt):
out = fmt(out)
if callable(exp):
exp = exp()
2018-09-13 16:49:09 -04:00
assert out == exp, err
assert rtn == exp_rtn, err
2017-02-12 13:11:29 -05:00
2016-10-13 02:03:30 -04:00
#
# The following list contains a (stdin, stdout, returncode) tuples
#
ALL_PLATFORMS = [
2018-08-30 09:18:49 -05:00
# test calling a function alias
(
"""
2016-10-13 02:03:30 -04:00
def _f():
print('hello')
aliases['f'] = _f
f
2018-08-30 09:18:49 -05:00
""",
"hello\n",
0,
),
# test redirecting a function alias to a file
(
"""
2016-10-13 02:03:30 -04:00
def _f():
print('Wow Mom!')
aliases['f'] = _f
f > tttt
with open('tttt') as tttt:
s = tttt.read().strip()
print('REDIRECTED OUTPUT: ' + s)
2018-08-30 09:18:49 -05:00
""",
"REDIRECTED OUTPUT: Wow Mom!\n",
0,
),
# test redirecting a function alias from stderr -> stdout
(
"""
2017-12-07 23:24:06 -05:00
def _f(args, stdin, stdout, stderr):
print('The Truth is Out There', file=stderr)
aliases['f'] = _f
f e>o
2018-08-30 09:18:49 -05:00
""",
"The Truth is Out There\n",
0,
),
# test redirecting to a python substitution
(
"""
def _f():
print('Wow Mom!')
aliases['f'] = _f
f > @('tttt')
with open('tttt') as tttt:
s = tttt.read().strip()
print('REDIRECTED OUTPUT: ' + s)
""",
"REDIRECTED OUTPUT: Wow Mom!\n",
0,
),
# test redirecting to a python substitution with p-string
(
"""
def _f():
print('Wow Mom!')
aliases['f'] = _f
f > @(p'tttt')
with open('tttt') as tttt:
s = tttt.read().strip()
print('REDIRECTED OUTPUT: ' + s)
""",
"REDIRECTED OUTPUT: Wow Mom!\n",
0,
),
2018-08-30 09:18:49 -05:00
# test system exit in function alias
(
"""
2016-10-13 03:20:42 -04:00
import sys
def _f():
sys.exit(42)
aliases['f'] = _f
print(![f].returncode)
2018-08-30 09:18:49 -05:00
""",
"42\n",
0,
),
# test uncaptured streaming alias,
# order actually printed in is non-deterministic
(
"""
2016-10-13 22:53:06 -04:00
def _test_stream(args, stdin, stdout, stderr):
2016-10-24 22:41:01 -04:00
print('hallo on stream', file=stderr)
print('hallo on stream', file=stdout)
2016-10-13 22:53:06 -04:00
return 1
aliases['test-stream'] = _test_stream
x = ![test-stream]
print(x.returncode)
2018-08-30 09:18:49 -05:00
""",
"hallo on stream\nhallo on stream\n1\n",
0,
),
# test captured streaming alias
(
"""
2016-10-13 22:53:06 -04:00
def _test_stream(args, stdin, stdout, stderr):
print('hallo on err', file=stderr)
print('hallo on out', file=stdout)
return 1
aliases['test-stream'] = _test_stream
x = !(test-stream)
print(x.returncode)
2018-08-30 09:18:49 -05:00
""",
"1\n",
2018-08-30 09:18:49 -05:00
0,
),
# test captured streaming alias without stderr
(
"""
def _test_stream(args, stdin, stdout, stderr):
print('hallo on err', file=stderr)
print('hallo on out', file=stdout)
return 1
aliases['test-stream'] = _test_stream
with __xonsh__.env.swap(XONSH_SUBPROC_CAPTURED_PRINT_STDERR=True):
x = !(test-stream)
print(x.returncode)
""",
"hallo on err\n1\n",
0,
),
2018-08-30 09:18:49 -05:00
# test piping aliases
(
"""
2016-10-16 12:07:39 -04:00
def dummy(args, inn, out, err):
out.write('hey!')
return 0
def dummy2(args, inn, out, err):
s = inn.read()
out.write(s.upper())
return 0
aliases['d'] = dummy
aliases['d2'] = dummy2
d | d2
2018-08-30 09:18:49 -05:00
""",
"HEY!",
0,
),
# test output larger than most pipe buffers
(
"""
2016-10-18 21:14:08 -04:00
def _g(args, stdin=None):
for i in range(1000):
print('x' * 100)
aliases['g'] = _g
g
2018-08-30 09:18:49 -05:00
""",
(("x" * 100) + "\n") * 1000,
0,
),
# test piping 'real' command
(
"""
2016-10-25 20:55:42 -04:00
with open('tttt', 'w') as fp:
fp.write("Wow mom!\\n")
2020-08-10 14:53:31 -04:00
![python tests/bin/cat tttt | python tests/bin/wc]
2018-08-30 09:18:49 -05:00
""",
" 1 2 10 <stdin>\n" if ON_WINDOWS else " 1 2 9 <stdin>\n",
2018-08-30 09:18:49 -05:00
0,
),
# test double piping 'real' command
(
"""
with open('tttt', 'w') as fp:
fp.write("Wow mom!\\n")
2020-08-10 14:53:31 -04:00
![python tests/bin/cat tttt | python tests/bin/wc | python tests/bin/wc]
2018-08-30 09:18:49 -05:00
""",
" 1 4 18 <stdin>\n" if ON_WINDOWS else " 1 4 16 <stdin>\n",
2018-08-30 09:18:49 -05:00
0,
),
# test unthreadable alias (which should trigger a ProcPoxy call)
(
"""
from xonsh.tools import unthreadable
2017-02-11 14:54:36 -05:00
@unthreadable
def _f():
return 'hello\\n'
aliases['f'] = _f
f
2018-08-30 09:18:49 -05:00
""",
"hello\n",
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,
),
2018-08-30 09:18:49 -05:00
# test ambiguous globs
(
"""
2017-02-26 21:54:39 -05:00
import os
def _echo(args):
print(' '.join(args))
aliases['echo'] = _echo
files = ['Actually_test.tst', 'Actually.tst', 'Complete_test.tst', 'Complete.tst']
# touch the file
for f in files:
with open(f, 'w'):
pass
# echo the files
echo *.tst and echo *_test.tst
echo *_test.tst
echo *_test.tst and echo *.tst
# remove the files
for f in files:
os.remove(f)
2017-02-27 14:11:39 -05:00
""",
2018-08-30 09:18:49 -05:00
"Actually.tst Actually_test.tst Complete.tst Complete_test.tst\n"
"Actually_test.tst Complete_test.tst\n"
"Actually_test.tst Complete_test.tst\n"
"Actually_test.tst Complete_test.tst\n"
"Actually.tst Actually_test.tst Complete.tst Complete_test.tst\n",
0,
),
#
# test ambiguous line continuations
#
(
"""
2017-03-02 22:49:52 -05:00
def _echo(args):
print(' '.join(args))
aliases['echo'] = _echo
2020-06-21 15:55:30 +02:00
echo --option1 \\
2017-03-02 22:49:52 -05:00
--option2
2020-06-21 15:55:30 +02:00
echo missing \\
EOL""",
"--option1 --option2\nmissing EOL\n",
2018-08-30 09:18:49 -05:00
0,
),
#
# test @$() with aliases
#
(
"""
2017-04-21 17:46:04 -04:00
aliases['ls'] = 'spam spam sausage spam'
echo @$(which ls)
2018-08-30 09:18:49 -05:00
""",
"spam spam sausage spam\n",
0,
),
#
2020-04-01 22:45:01 +02:00
# test @$() without leading/trailig WS
#
(
"""
def _echo(args):
print(' '.join(args))
aliases['echo'] = _echo
echo foo_@$(echo spam)_bar
""",
"foo_spam_bar\n",
0,
),
#
# test @$() outer product
#
(
"""
def _echo(args):
print(' '.join(args))
aliases['echo'] = _echo
echo foo_@$(echo spam sausage)_bar
""",
"foo_spam_bar foo_sausage_bar\n",
0,
),
#
2018-08-30 09:18:49 -05:00
# test redirection
#
(
"""
echo Just the place for a snark. >tttt
2020-08-10 14:53:31 -04:00
python tests/bin/cat tttt
2018-08-30 09:18:49 -05:00
""",
"Just the place for a snark.\n",
0,
),
#
# Test completion registration and subproc stack
#
(
"""
2018-08-20 19:57:56 -04:00
def _f():
def j():
pass
global aliases
aliases['j'] = j
def completions(pref, *args):
return set(['hello', 'world'])
completer add j completions "start"
_f()
del _f
2018-08-30 09:18:49 -05:00
""",
"",
0,
),
2019-08-27 11:03:26 -04:00
#
# test single check_output
#
(
"""
def _echo(args):
print(' '.join(args))
aliases['echo'] = _echo
from xonsh.api.subprocess import check_output
2019-08-27 11:03:26 -04:00
print(check_output(["echo", "hello"]).decode("utf8"))
""",
feat: add subproc output format, autostrip singleline output (#5377) ### 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`](https://github.com/xonsh/xonsh/blob/c3a12b2a9c2958ce2a8e97a59b41f030f24cb45c/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>
2024-05-02 17:48:25 +02:00
"hello\n",
2019-08-27 11:03:26 -04:00
0,
),
2019-10-01 16:56:27 -04:00
#
# test contextvars
#
(
"""
2019-10-01 17:01:20 -04:00
import sys
2019-10-03 10:27:38 -04:00
if sys.version_info[:2] >= (3, 7):
2019-10-01 17:01:20 -04:00
with open("sourced-file.xsh", "w") as f:
f.write('''
2019-10-01 16:56:27 -04:00
from contextvars import ContextVar
var = ContextVar('var', default='spam')
var.set('foo')
2019-10-01 17:01:20 -04:00
''')
2019-10-01 16:56:27 -04:00
2019-10-01 17:01:20 -04:00
source sourced-file.xsh
2019-10-01 16:56:27 -04:00
2019-10-01 17:01:20 -04:00
print("Var " + var.get())
2019-10-01 16:56:27 -04:00
2019-10-01 17:01:20 -04:00
import os
os.remove('sourced-file.xsh')
else:
print("Var foo")
2019-10-01 16:56:27 -04:00
""",
"Var foo\n",
0,
),
#
# test env with class
#
(
"""
class Cls:
def __init__(self, var):
self.var = var
def __repr__(self):
return self.var
$VAR = Cls("hello")
print($VAR)
echo $VAR
""",
"hello\nhello\n",
0,
),
#
# test logical subprocess operators
#
(
"""
def _echo(args):
print(' '.join(args))
aliases['echo'] = _echo
echo --version and echo a
echo --version && echo a
echo --version or echo a
echo --version || echo a
echo -+version and echo a
echo -+version && echo a
echo -+version or echo a
echo -+version || echo a
echo -~version and echo a
echo -~version && echo a
echo -~version or echo a
echo -~version || echo a
""",
"""--version
a
--version
a
--version
--version
-+version
a
-+version
a
-+version
-+version
-~version
a
-~version
a
-~version
-~version
""",
0,
),
2016-10-13 02:03:30 -04:00
]
UNIX_TESTS = [
# testing alias stack: lambda function
(
"""
def _echo():
echo hello
aliases['echo'] = _echo
echo
""",
"hello\n",
0,
),
# testing alias stack: ExecAlias
(
"""
aliases['echo'] = "echo @('hello')"
echo
""",
"hello\n",
0,
),
# testing alias stack: callable alias (ExecAlias) + no binary location + infinite loop
(
"""
aliases['first'] = "second @(1)"
aliases['second'] = "first @(1)"
first
""",
lambda out: 'Recursive calls to "first" alias.' in out,
0,
),
# testing alias stack: parallel threaded callable aliases.
# This breaks if the __ALIAS_STACK variables leak between threads.
pytest.param(
(
"""
from time import sleep
aliases['a'] = lambda: print(1, end="") or sleep(0.2) or print(1, end="")
aliases['b'] = 'a'
a | a
a | a
a | b | a
a | a | b | b
""",
"1" * 2 * 4,
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 parsing of $SHLVL
$SHLVL = "1"
echo $SHLVL # == 1
$SHLVL = 1
echo $SHLVL # == 1
$SHLVL = "-13"
echo $SHLVL # == 0
$SHLVL = "error"
echo $SHLVL # == 0
$SHLVL = 999
echo $SHLVL # == 999
$SHLVL = 1000
echo $SHLVL # == 1
# sourcing a script should maintain $SHLVL
$SHLVL = 5
touch temp_shlvl_test.sh
source-bash temp_shlvl_test.sh
rm temp_shlvl_test.sh
echo $SHLVL # == 5
# creating a subshell should increment the child's $SHLVL and maintain the parents $SHLVL
$SHLVL = 5
xonsh --no-rc -c r'echo $SHLVL' # == 6
echo $SHLVL # == 5
# replacing the current process with another process should derease $SHLVL
# (so that if the new process is a shell, $SHLVL is maintained)
$SHLVL = 5
xexec python3 -c 'import os; print(os.environ["SHLVL"])' # == 4
""",
"""1
1
0
0
999
1
5
6
5
4
""",
0,
),
# test $() inside piped callable alias
(
r"""
def _callme(args):
result = $(python -c 'print("tree");print("car")')
print(result[::-1])
print('one\ntwo\nthree')
aliases['callme'] = _callme
callme | grep t
""",
"""eert
two
three
""",
0,
),
# test ![] inside piped callable alias
(
r"""
def _callme(args):
python -c 'print("tree");print("car")'
print('one\ntwo\nthree')
aliases['callme'] = _callme
callme | grep t
""",
"""tree
two
three
""",
0,
),
# test $[] inside piped callable alias
pytest.param(
(
r"""
def _callme(args):
$[python -c 'print("tree");print("car")']
print('one\ntwo\nthree')
aliases['callme'] = _callme
callme | grep t
""",
"""tree
two
three
""",
0,
),
marks=pytest.mark.xfail(reason="$[] does not send stdout through the pipe"),
),
]
2016-10-13 02:03:30 -04:00
if not ON_WINDOWS:
ALL_PLATFORMS = tuple(ALL_PLATFORMS) + tuple(UNIX_TESTS)
@skip_if_no_xonsh
2018-08-30 09:18:49 -05:00
@pytest.mark.parametrize("case", ALL_PLATFORMS)
@pytest.mark.flaky(reruns=4, reruns_delay=2)
2016-10-13 02:03:30 -04:00
def test_script(case):
script, exp_out, exp_rtn = case
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
if ON_DARWIN:
script = script.replace("tests/bin", str(Path(__file__).parent / "bin"))
2016-10-16 21:08:37 +03:00
out, err, rtn = run_xonsh(script)
Introduce new resolver for executables to replace commands_cache usages and speed up everything (#5544) * remove commands_cache from pyghooks to avoid cc.update_cache on every key press * create executables.py * replace cc.locate_binary to locate_executable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * vc: replace locate_binary * pyghooks: remove commands_cache * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove unused func _yield_executables * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Move `executables_in` from tools to commands_cache to avoid circular imports. * First steps to `procs.executables` that is based on `commands_cache` and `tools`. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * test_executables * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add not recommended notes * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add `get_paths` with test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix source test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * vc: remove tests because commands cache is not used * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * specs: fix exception for recursive call * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * specs: fix exception for recursive call * improve test_locate_executable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix test * beautify pathext * tests * docs * tests * update locators * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * locate_file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * comments * return environ locate bin test * comments * Update xonsh/procs/executables.py Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * Update xonsh/procs/executables.py Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * Update xonsh/procs/executables.py Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add itertools * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * moving is_executable * doc * optimization is_executable_in_windows * micro improvements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * news * news * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * bump test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- 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: Jason R. Coombs <jaraco@jaraco.com>
2024-07-09 09:44:03 +02:00
out = out.replace("bash: no job control in this shell\n", "")
if callable(exp_out):
Introduce new resolver for executables to replace commands_cache usages and speed up everything (#5544) * remove commands_cache from pyghooks to avoid cc.update_cache on every key press * create executables.py * replace cc.locate_binary to locate_executable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * vc: replace locate_binary * pyghooks: remove commands_cache * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove unused func _yield_executables * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Move `executables_in` from tools to commands_cache to avoid circular imports. * First steps to `procs.executables` that is based on `commands_cache` and `tools`. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * test_executables * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add not recommended notes * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add `get_paths` with test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix source test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * vc: remove tests because commands cache is not used * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * specs: fix exception for recursive call * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * specs: fix exception for recursive call * improve test_locate_executable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix test * beautify pathext * tests * docs * tests * update locators * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * locate_file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * comments * return environ locate bin test * comments * Update xonsh/procs/executables.py Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * Update xonsh/procs/executables.py Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * Update xonsh/procs/executables.py Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add itertools * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * moving is_executable * doc * optimization is_executable_in_windows * micro improvements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * news * news * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * bump test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- 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: Jason R. Coombs <jaraco@jaraco.com>
2024-07-09 09:44:03 +02:00
assert exp_out(
out
), f"CASE:\nscript=***\n{script}\n***,\nExpected: {exp_out!r},\nActual: {out!r}"
else:
assert exp_out == out
2016-10-16 21:08:37 +03:00
assert exp_rtn == rtn
2016-10-13 02:03:30 -04:00
2017-01-11 23:29:28 -05:00
ALL_PLATFORMS_STDERR = [
2018-08-30 09:18:49 -05:00
# test redirecting a function alias
(
"""
2017-12-07 23:24:06 -05:00
def _f(args, stdin, stdout):
2017-01-11 23:29:28 -05:00
print('Wow Mom!', file=stdout)
aliases['f'] = _f
f o>e
2018-08-30 09:18:49 -05:00
""",
"Wow Mom!\n",
0,
)
2017-01-11 23:29:28 -05:00
]
2018-08-30 09:18:49 -05:00
@skip_if_no_xonsh
2018-08-30 09:18:49 -05:00
@pytest.mark.parametrize("case", ALL_PLATFORMS_STDERR)
def test_script_stderr(case):
2017-01-11 23:29:28 -05:00
script, exp_err, exp_rtn = case
out, err, rtn = run_xonsh(script, stderr=sp.PIPE)
assert exp_err == err
assert exp_rtn == rtn
2018-08-30 09:18:49 -05:00
@skip_if_no_xonsh
@skip_if_on_windows
2018-08-30 09:18:49 -05:00
@pytest.mark.parametrize(
"cmd, fmt, exp",
[
("pwd", None, lambda: os.getcwd() + "\n"),
("echo WORKING", None, "WORKING\n"),
("ls -f", lambda out: out.splitlines().sort(), os.listdir().sort()),
(
"$FOO='foo' $BAR=2 xonsh --no-rc -c r'echo -n $FOO$BAR'",
None,
"foo2",
),
2018-08-30 09:18:49 -05:00
],
)
2017-02-11 19:22:30 -05:00
def test_single_command_no_windows(cmd, fmt, exp):
2017-02-12 13:11:29 -05:00
check_run_xonsh(cmd, fmt, exp)
2017-02-11 19:22:30 -05:00
@skip_if_no_xonsh
def test_eof_syntax_error():
"""Ensures syntax errors for EOF appear on last line."""
2018-08-30 09:18:49 -05:00
script = "x = 1\na = (1, 0\n"
out, err, rtn = run_xonsh(script, stderr=sp.PIPE)
assert "line 0" not in err
assert "EOF in multi-line statement" in err and "line 2" in err
@skip_if_no_xonsh
2018-07-20 19:21:15 -04:00
def test_open_quote_syntax_error():
2018-08-30 09:18:49 -05:00
script = (
"#!/usr/bin/env xonsh\n\n"
'echo "This is line 3"\n'
'print ("This is line 4")\n'
'x = "This is a string where I forget the closing quote on line 5\n'
'echo "This is line 6"\n'
)
2018-07-20 19:21:15 -04:00
out, err, rtn = run_xonsh(script, stderr=sp.PIPE)
assert """('code: "This is line 3"',)""" not in err
assert "line 5" in err
2018-08-30 09:18:49 -05:00
assert "SyntaxError:" in err
2018-07-20 19:21:15 -04:00
2018-08-30 09:18:49 -05:00
_bad_case = pytest.mark.skipif(
ON_DARWIN or ON_WINDOWS or ON_TRAVIS, reason="bad platforms"
)
2017-02-12 13:11:29 -05:00
@skip_if_no_xonsh
def test_atdollar_no_output():
# see issue 1521
script = """
def _echo(args):
print(' '.join(args))
2021-06-14 01:47:05 -04:00
aliases['echo'] = _echo
@$(echo)
"""
out, err, rtn = run_xonsh(script, stderr=sp.PIPE)
assert "command is empty" in err
@skip_if_no_xonsh
def test_empty_command():
script = "$['']\n"
out, err, rtn = run_xonsh(script, stderr=sp.PIPE)
assert "command is empty" in err
@skip_if_no_xonsh
2017-02-12 13:11:29 -05:00
@_bad_case
def test_printfile():
2018-08-30 09:18:49 -05:00
check_run_xonsh("printfile.xsh", None, "printfile.xsh\n")
2017-02-12 13:11:29 -05:00
@skip_if_no_xonsh
2017-02-12 13:11:29 -05:00
@_bad_case
def test_printname():
2018-08-30 09:18:49 -05:00
check_run_xonsh("printfile.xsh", None, "printfile.xsh\n")
2017-02-12 13:11:29 -05:00
@skip_if_no_xonsh
2017-02-12 13:11:29 -05:00
@_bad_case
def test_sourcefile():
2018-08-30 09:18:49 -05:00
check_run_xonsh("printfile.xsh", None, "printfile.xsh\n")
2017-02-12 13:11:29 -05:00
@skip_if_no_xonsh
2017-02-25 21:45:32 -05:00
@_bad_case
2018-08-30 09:18:49 -05:00
@pytest.mark.parametrize(
"cmd, fmt, exp",
[
# test subshell wrapping
(
"""
2017-02-25 21:45:32 -05:00
with open('tttt', 'w') as fp:
fp.write("Wow mom!\\n")
(wc) < tttt
2018-08-30 09:18:49 -05:00
""",
None,
" 1 2 9 <stdin>\n",
),
# test subshell statement wrapping
(
"""
2017-02-25 21:45:32 -05:00
with open('tttt', 'w') as fp:
fp.write("Wow mom!\\n")
(wc;) < tttt
2018-08-30 09:18:49 -05:00
""",
None,
" 1 2 9 <stdin>\n",
),
],
)
2017-02-25 21:45:32 -05:00
def test_subshells(cmd, fmt, exp):
check_run_xonsh(cmd, fmt, exp)
@skip_if_no_xonsh
@skip_if_on_windows
2018-08-30 09:18:49 -05:00
@pytest.mark.parametrize("cmd, exp", [("pwd", lambda: os.getcwd() + "\n")])
2016-10-16 21:08:37 +03:00
def test_redirect_out_to_file(cmd, exp, tmpdir):
2018-08-30 09:18:49 -05:00
outfile = tmpdir.mkdir("xonsh_test_dir").join("xonsh_test_file")
command = f"{cmd} > {outfile}\n"
2016-10-16 21:43:38 +03:00
out, _, _ = run_xonsh(command)
content = outfile.read()
2016-10-26 00:40:47 -04:00
if callable(exp):
exp = exp()
assert content == exp
2019-02-06 14:51:52 -05:00
@skip_if_no_make
@skip_if_no_xonsh
@skip_if_no_sleep
2019-02-06 15:23:41 -05:00
@skip_if_on_windows
@pytest.mark.xfail(strict=False) # TODO: fixme (super flaky on OSX)
2019-02-06 14:51:52 -05:00
def test_xonsh_no_close_fds():
# see issue https://github.com/xonsh/xonsh/issues/2984
makefile = (
"default: all\n"
"all:\n"
"\t$(MAKE) s\n"
"s:\n"
"\t$(MAKE) a b\n"
"a:\n"
"\tsleep 1\n"
"b:\n"
2019-02-06 14:54:51 -05:00
"\tsleep 1\n"
)
with tempfile.TemporaryDirectory() as d, with_pushd(d):
2019-02-06 14:54:51 -05:00
with open("Makefile", "w") as f:
2019-02-06 14:51:52 -05:00
f.write(makefile)
2019-02-06 14:54:51 -05:00
out = sp.check_output(["make", "-sj2", "SHELL=xonsh"], universal_newlines=True)
2019-02-06 14:51:52 -05:00
assert "warning" not in out
2020-01-12 14:45:52 -05:00
@skip_if_no_xonsh
2020-01-12 14:45:52 -05:00
@pytest.mark.parametrize(
"cmd, fmt, exp",
[
("cat tttt | wc", lambda x: x > "", True),
], # noqa E231 (black removes space)
2020-01-12 14:45:52 -05:00
)
def test_pipe_between_subprocs(cmd, fmt, exp):
2021-09-26 21:02:24 +05:30
"""verify pipe between subprocesses doesn't throw an exception"""
2020-01-12 14:45:52 -05:00
check_run_xonsh(cmd, fmt, exp)
2020-03-01 15:06:33 -05:00
@skip_if_no_xonsh
2020-03-01 15:06:33 -05:00
@skip_if_on_windows
def test_negative_exit_codes_fail():
# see issue 3309
script = 'python -c "import os; os.abort()" && echo OK\n'
out, err, rtn = run_xonsh(script)
assert "OK" != out
assert "OK" != err
2020-04-07 15:07:24 +02:00
2020-04-07 23:10:35 +02:00
@skip_if_no_xonsh
2020-04-07 15:07:24 +02:00
@pytest.mark.parametrize(
2020-04-07 23:10:35 +02:00
"cmd, exp",
2020-04-07 15:07:24 +02:00
[
2020-04-07 23:10:35 +02:00
("echo '&'", "&\n"),
("echo foo'&'", "foo'&'\n"),
("echo foo '&'", "foo &\n"),
("echo foo '&' bar", "foo & bar\n"),
2020-04-07 15:07:24 +02:00
],
)
2020-04-07 23:10:35 +02:00
def test_ampersand_argument(cmd, exp):
script = f"""
2020-04-07 23:10:35 +02:00
#!/usr/bin/env xonsh
def _echo(args):
print(' '.join(args))
aliases['echo'] = _echo
{cmd}
"""
2020-04-07 23:10:35 +02:00
out, _, _ = run_xonsh(script)
assert out == exp
@skip_if_no_xonsh
@pytest.mark.parametrize(
"cmd, exp",
[
("echo '>'", ">\n"),
("echo '2>'", "2>\n"),
("echo '2>1'", "2>1\n"),
],
)
def test_redirect_argument(cmd, exp):
script = f"""
#!/usr/bin/env xonsh
def _echo(args):
print(' '.join(args))
aliases['echo'] = _echo
{cmd}
"""
out, _, _ = run_xonsh(script)
assert out == exp
# issue 3402
@skip_if_no_xonsh
@skip_if_on_windows
@pytest.mark.parametrize(
"cmd, exp_rtn",
[
("2+2", 0),
("import sys; sys.exit(0)", 0),
("import sys; sys.exit(100)", 100),
("@('exit')", 0),
("exit 100", 100),
("exit unknown", 1),
("exit()", 0),
("exit(100)", 100),
("__xonsh__.exit=0", 0),
("__xonsh__.exit=100", 100),
("raise Exception()", 1),
("raise SystemExit(100)", 100),
("sh -c 'exit 0'", 0),
("sh -c 'exit 1'", 1),
],
)
def test_single_command_return_code(cmd, exp_rtn):
_, _, rtn = run_xonsh(cmd, single_command=True)
assert rtn == exp_rtn
@skip_if_no_xonsh
@skip_if_on_msys
@skip_if_on_windows
@skip_if_on_darwin
def test_argv0():
check_run_xonsh("checkargv0.xsh", None, "OK\n")
@pytest.mark.parametrize("interactive", [True, False])
def test_loading_correctly(monkeypatch, interactive):
# Ensure everything loads correctly in interactive mode (e.g. #4289)
monkeypatch.setenv("SHELL_TYPE", "prompt_toolkit")
monkeypatch.setenv("XONSH_LOGIN", "1")
monkeypatch.setenv("XONSH_INTERACTIVE", "1")
out, err, ret = run_xonsh(
"import xonsh; echo -n AAA @(xonsh.__file__) BBB",
interactive=interactive,
single_command=True,
)
assert not err
assert ret == 0
our_xonsh = (
xonsh.__file__
) # make sure xonsh didn't fail and fallback to the system shell
assert f"AAA {our_xonsh} BBB" in out # ignore tty warnings/prompt text
@skip_if_no_xonsh
@pytest.mark.parametrize(
"cmd",
[
"x = 0; (lambda: x)()",
"x = 0; [x for _ in [0]]",
],
)
def test_exec_function_scope(cmd):
_, _, rtn = run_xonsh(cmd, single_command=True)
assert rtn == 0
@skip_if_on_unix
def test_run_currentfolder(monkeypatch):
"""Ensure we can run an executable in the current folder
when file is not on path
"""
batfile = Path(__file__).parent / "bin" / "hello_world.bat"
monkeypatch.chdir(batfile.parent)
cmd = batfile.name
out, _, _ = run_xonsh(cmd, stdout=sp.PIPE, stderr=sp.PIPE, path=os.environ["PATH"])
assert out.strip() == "hello world"
@skip_if_on_unix
def test_run_dynamic_on_path():
"""Ensure we can run an executable which is added to the path
after xonsh is loaded
"""
batfile = Path(__file__).parent / "bin" / "hello_world.bat"
cmd = f"$PATH.add(r'{batfile.parent}');![hello_world.bat]"
out, _, _ = run_xonsh(cmd, path=os.environ["PATH"])
assert out.strip() == "hello world"
@skip_if_on_unix
def test_run_fail_not_on_path():
"""Test that xonsh fails to run an executable when not on path
or in current folder
"""
cmd = "hello_world.bat"
out, _, _ = run_xonsh(cmd, stdout=sp.PIPE, stderr=sp.PIPE, path=os.environ["PATH"])
assert out != "Hello world"
feat: add superhelp and additional context via new FuncAlias (#5366) ### Goals * Make callable aliases transparent. * Catch errors in callable aliases and show the name of the source. * Show additional attributes: thredable, capturable. * Closes #5266 ## Exception ### Before ```xsh aliases['cd'] # <function xonsh.dirstack.cd> aliases['trace'] # <function xonsh.aliases.trace> aliases['null'] = lambda: 1/0 null # ZeroDivisionError: division by zero @aliases.register('catch') @aliases.register('me') @aliases.register('if') @aliases.register('you') @aliases.register('can') def _exc(args, stdin, stdout): for line in stdin.readlines(): print(line.strip() + '!', file=stdout, flush=True) return 1/0 if 'i' in $__ALIAS_NAME else 0 echo hey | catch | me | if | you | can # ZeroDivisionError: division by zero <--- ??? # hey!!!!! ``` ### After ```xsh aliases['cd'] # FuncAlias({'name': 'cd', 'func': 'cd'}) aliases['trace'] # FuncAlias({'name': 'trace', 'func': 'trace', '__xonsh_threadable__': False}) $XONSH_SHOW_TRACEBACK=False $RAISE_SUBPROC_ERROR = False aliases['null'] = lambda: 1/0 null #Exception in thread {'cls': 'ProcProxyThread', 'name': 'Thread-15', 'func': FuncAlias({'name': 'null', 'func': '<lambda>'}), 'alias': 'null', 'pid': None} #ZeroDivisionError: division by zero @aliases.register('catch') @aliases.register('me') @aliases.register('if') @aliases.register('you') @aliases.register('can') def _exc(args, stdin, stdout): for line in stdin.readlines(): print(line.strip() + '!', file=stdout, flush=True) return 1/0 if 'i' in $__ALIAS_NAME else 0 echo hey | catch | me | if | you | can # Exception in thread {'cls': 'ProcProxyThread', 'name': 'Thread-8', 'func': FuncAlias({'name': 'if', 'func': '_exc'}), 'alias': 'if', 'pid': None} # ZeroDivisionError: division by zero # hey!!!!! ``` ## Superhelp ### Before ```xsh @aliases.register("hello") def _alias_hello(): """Show world.""" print('world') hello? # No manual entry for hello ``` ### After ```xsh @aliases.register("hello") def _alias_hello(): """Show world.""" print('world') hello? # FuncAlias({'name': 'hello', 'func': '_alias_hello'}): # Show world. ``` ## 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>
2024-05-13 15:11:58 +02:00
ALIASES_THREADABLE_PRINT_CASES = [
(
"""
$RAISE_SUBPROC_ERROR = False
$XONSH_SHOW_TRACEBACK = False
aliases['f'] = lambda: 1/0
echo f1f1f1 ; f ; echo f2f2f2
""",
"^f1f1f1\nException in thread.*FuncAlias.*\nZeroDivisionError.*\nf2f2f2\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = True
$XONSH_SHOW_TRACEBACK = False
aliases['f'] = lambda: 1/0
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nException in thread.*\nZeroDivisionError: .*\nsubprocess.CalledProcessError.*\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = True
$XONSH_SHOW_TRACEBACK = True
aliases['f'] = lambda: 1/0
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nException in thread.*\nTraceback.*\nZeroDivisionError: .*\nsubprocess.CalledProcessError.*\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = False
$XONSH_SHOW_TRACEBACK = True
aliases['f'] = lambda: 1/0
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nException in thread.*FuncAlias.*\nTraceback.*\nZeroDivisionError.*\nf2f2f2\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = False
$XONSH_SHOW_TRACEBACK = False
aliases['f'] = lambda: (None, "I failed", 2)
echo f1f1f1 ; f ; echo f2f2f2
""",
"^f1f1f1\nI failed\nf2f2f2\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = True
$XONSH_SHOW_TRACEBACK = False
aliases['f'] = lambda: (None, "I failed", 2)
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nI failed\nsubprocess.CalledProcessError.*\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = True
$XONSH_SHOW_TRACEBACK = True
aliases['f'] = lambda: (None, "I failed", 2)
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nI failed.*\nTraceback.*\nsubprocess.CalledProcessError.*\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = False
$XONSH_SHOW_TRACEBACK = True
aliases['f'] = lambda: (None, "I failed", 2)
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nI failed\nf2f2f2\n$",
),
]
ALIASES_UNTHREADABLE_PRINT_CASES = [
(
"""
$RAISE_SUBPROC_ERROR = False
$XONSH_SHOW_TRACEBACK = False
aliases['f'] = lambda: 1/0
aliases['f'].__xonsh_threadable__ = False
echo f1f1f1 ; f ; echo f2f2f2
""",
"^f1f1f1\nException in.*FuncAlias.*\nZeroDivisionError.*\nf2f2f2\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = True
$XONSH_SHOW_TRACEBACK = False
aliases['f'] = lambda: 1/0
aliases['f'].__xonsh_threadable__ = False
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nException in.*\nZeroDivisionError: .*\nsubprocess.CalledProcessError.*\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = True
$XONSH_SHOW_TRACEBACK = True
aliases['f'] = lambda: 1/0
aliases['f'].__xonsh_threadable__ = False
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nException in.*\nTraceback.*\nZeroDivisionError: .*\nsubprocess.CalledProcessError.*\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = False
$XONSH_SHOW_TRACEBACK = True
aliases['f'] = lambda: 1/0
aliases['f'].__xonsh_threadable__ = False
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nException in.*FuncAlias.*\nTraceback.*\nZeroDivisionError.*\nf2f2f2\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = False
$XONSH_SHOW_TRACEBACK = False
aliases['f'] = lambda: (None, "I failed", 2)
aliases['f'].__xonsh_threadable__ = False
echo f1f1f1 ; f ; echo f2f2f2
""",
"^f1f1f1\nI failed\nf2f2f2\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = True
$XONSH_SHOW_TRACEBACK = False
aliases['f'] = lambda: (None, "I failed", 2)
aliases['f'].__xonsh_threadable__ = False
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nI failed\nsubprocess.CalledProcessError.*\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = True
$XONSH_SHOW_TRACEBACK = True
aliases['f'] = lambda: (None, "I failed", 2)
aliases['f'].__xonsh_threadable__ = False
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nI failed.*\nTraceback.*\nsubprocess.CalledProcessError.*\n$",
),
(
"""
$RAISE_SUBPROC_ERROR = False
$XONSH_SHOW_TRACEBACK = True
aliases['f'] = lambda: (None, "I failed", 2)
aliases['f'].__xonsh_threadable__ = False
echo f1f1f1 ; f ; echo f2f2f2
""",
"f1f1f1\nI failed\nf2f2f2\n$",
),
]
@skip_if_on_windows
@pytest.mark.parametrize(
"case", ALIASES_THREADABLE_PRINT_CASES + ALIASES_UNTHREADABLE_PRINT_CASES
)
def test_aliases_print(case):
cmd, match = case
out, err, ret = run_xonsh(cmd=cmd, single_command=False)
assert re.match(
match, out, re.MULTILINE | re.DOTALL
), f"\nFailed:\n```\n{cmd.strip()}\n```,\nresult: {out!r}\nexpected: {match!r}."
@skip_if_on_windows
@pytest.mark.parametrize("interactive", [True, False])
def test_raise_subproc_error_with_show_traceback(monkeypatch, interactive):
out, err, ret = run_xonsh(
"$COLOR_RESULTS=False\n$RAISE_SUBPROC_ERROR=False\n$XONSH_SHOW_TRACEBACK=False\nls nofile",
interactive=interactive,
single_command=True,
)
assert ret != 0
assert re.match("ls.*No such file or directory\n", out)
out, err, ret = run_xonsh(
"$COLOR_RESULTS=False\n$RAISE_SUBPROC_ERROR=True\n$XONSH_SHOW_TRACEBACK=False\nls nofile",
interactive=interactive,
single_command=True,
)
assert ret != 0
assert re.match(
"ls:.*No such file or directory\nsubprocess.CalledProcessError: Command '\\['ls', 'nofile'\\]' returned non-zero exit status .*",
out,
re.MULTILINE | re.DOTALL,
)
out, err, ret = run_xonsh(
"$COLOR_RESULTS=False\n$RAISE_SUBPROC_ERROR=True\n$XONSH_SHOW_TRACEBACK=True\nls nofile",
interactive=interactive,
single_command=True,
)
assert ret != 0
assert re.match(
"ls.*No such file or directory.*Traceback .*\nsubprocess.CalledProcessError: Command '\\['ls', 'nofile'\\]' returned non-zero exit status .*",
out,
re.MULTILINE | re.DOTALL,
)
out, err, ret = run_xonsh(
"$COLOR_RESULTS=False\n$RAISE_SUBPROC_ERROR=False\n$XONSH_SHOW_TRACEBACK=True\nls nofile",
interactive=interactive,
single_command=True,
)
assert ret != 0
assert re.match("ls.*No such file or directory\n", out)
def test_main_d():
out, err, ret = run_xonsh(cmd="print($XONSH_HISTORY_BACKEND)", single_command=True)
assert out == "json\n"
out, err, ret = run_xonsh(
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
args=["--no-rc", "-DXONSH_HISTORY_BACKEND='dummy'"],
cmd="print($XONSH_HISTORY_BACKEND)",
single_command=True,
)
assert out == "dummy\n"
@pytest.mark.flaky(reruns=3, reruns_delay=1)
def test_catching_system_exit():
stdin_cmd = "__import__('sys').exit(2)\n"
out, err, ret = run_xonsh(
cmd=None, stdin_cmd=stdin_cmd, interactive=True, single_command=False, timeout=3
)
Read stop signals from the process and update the process state. (#5361) 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>
2024-05-22 17:45:39 +02:00
assert ret > 0
@skip_if_on_windows
@pytest.mark.flaky(reruns=3, reruns_delay=1)
def test_catching_exit_signal():
Read stop signals from the process and update the process state. (#5361) 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>
2024-05-22 17:45:39 +02:00
stdin_cmd = "sleep 0.2; kill -SIGHUP @(__import__('os').getpid())\n"
out, err, ret = run_xonsh(
cmd=None, stdin_cmd=stdin_cmd, interactive=True, single_command=False, timeout=3
)
assert ret > 0
Read stop signals from the process and update the process state. (#5361) 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>
2024-05-22 17:45:39 +02:00
@skip_if_on_windows
def test_forwarding_sighup(tmpdir):
"""We want to make sure that SIGHUP is forwarded to subprocesses when
received, so we spin up a Bash process that waits for SIGHUP and then
writes `SIGHUP` to a file, then exits. Then we check the content of
that file to ensure that the Bash process really did get SIGHUP."""
outfile = tmpdir.mkdir("xonsh_test_dir").join("sighup_test.out")
stdin_cmd = f"""
sleep 0.2
(sleep 1 && kill -SIGHUP @(__import__('os').getppid())) &
bash -c "trap 'echo SIGHUP > {outfile}; exit 0' HUP; sleep 30 & wait $!"
"""
proc = run_xonsh(
cmd=None,
stdin_cmd=stdin_cmd,
stderr=sp.PIPE,
interactive=True,
single_command=False,
blocking=False,
)
proc.wait(timeout=5)
# if this raises FileNotFoundError, then the Bash subprocess probably did not get SIGHUP
assert outfile.read_text("utf-8").strip() == "SIGHUP"
@skip_if_on_windows
def test_on_postcommand_waiting(tmpdir):
"""Ensure that running a subcommand in the on_postcommand hook doesn't
block xonsh from exiting when there is a running foreground process."""
outdir = tmpdir.mkdir("xonsh_test_dir")
stdin_cmd = f"""
sleep 0.2
@events.on_postcommand
def postcmd_hook(**kwargs):
touch {outdir}/sighup_test_postcommand
(sleep 1 && kill -SIGHUP @(__import__('os').getppid())) &
bash -c "trap '' HUP; sleep 30"
"""
proc = run_xonsh(
cmd=None,
stdin_cmd=stdin_cmd,
stderr=sp.PIPE,
interactive=True,
single_command=False,
blocking=False,
)
proc.wait(timeout=5)
Read stop signals from the process and update the process state. (#5361) 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>
2024-05-22 17:45:39 +02:00
@skip_if_on_windows
def test_suspended_captured_process_pipeline():
"""See also test_specs.py:test_specs_with_suspended_captured_process_pipeline"""
stdin_cmd = "!(python -c 'import os, signal, time; time.sleep(0.2); os.kill(os.getpid(), signal.SIGTTIN)')\n"
out, err, ret = run_xonsh(
cmd=None, stdin_cmd=stdin_cmd, interactive=True, single_command=False, timeout=5
)
match = ".*suspended=True.*"
assert re.match(
match, out, re.MULTILINE | re.DOTALL
), f"\nFailed:\n```\n{stdin_cmd.strip()}\n```,\nresult: {out!r}\nexpected: {match!r}."
@skip_if_on_windows
@pytest.mark.flaky(reruns=3, reruns_delay=1)
def test_alias_stability():
"""Testing alias stability after amalgamation regress that described in #5435."""
stdin_cmd = (
"aliases['tst'] = lambda: [print('sleep'), __import__('time').sleep(1)]\n"
"tst\ntst\ntst\n"
)
out, err, ret = run_xonsh(
cmd=None,
stdin_cmd=stdin_cmd,
interactive=True,
single_command=False,
timeout=10,
)
assert re.match(".*sleep.*sleep.*sleep.*", out, re.MULTILINE | re.DOTALL)
@skip_if_on_windows
@pytest.mark.flaky(reruns=3, reruns_delay=1)
def test_captured_subproc_is_not_affected_next_command():
"""Testing #5769."""
stdin_cmd = (
"t = __xonsh__.imp.time.time()\n"
"p = !(sleep 2)\n"
"print('OK_'+'TEST' if __xonsh__.imp.time.time() - t < 1 else 'FAIL_'+'TEST')\n"
"t = __xonsh__.imp.time.time()\n"
"echo 1\n"
"print('OK_'+'TEST' if __xonsh__.imp.time.time() - t < 1 else 'FAIL_'+'TEST')\n"
)
out, err, ret = run_xonsh(
cmd=None,
stdin_cmd=stdin_cmd,
interactive=True,
single_command=False,
timeout=10,
)
assert not re.match(
".*FAIL_TEST.*", out, re.MULTILINE | re.DOTALL
), "The second command after running captured subprocess shouldn't wait the end of the first one."
@skip_if_on_windows
@pytest.mark.flaky(reruns=3, reruns_delay=1)
def test_spec_decorator_alias():
"""Testing spec modifier alias with `@` in the alias name."""
stdin_cmd = (
"from xonsh.procs.specs import SpecAttrDecoratorAlias as mod\n"
'aliases["@dict"] = mod({"output_format": lambda lines: eval("\\n".join(lines))})\n'
"d = $(@dict echo '{\"a\":42}')\n"
"print('Answer =', d['a'])\n"
)
out, err, ret = run_xonsh(
cmd=None,
stdin_cmd=stdin_cmd,
interactive=True,
single_command=False,
timeout=10,
)
assert "Answer = 42" in out
@skip_if_on_windows
@pytest.mark.flaky(reruns=3, reruns_delay=1)
def test_alias_stability_exception():
"""Testing alias stability (exception) after amalgamation regress that described in #5435."""
stdin_cmd = (
"aliases['tst1'] = lambda: [print('sleep'), __import__('time').sleep(1)]\n"
"aliases['tst2'] = lambda: [1/0]\n"
"tst1\ntst2\ntst1\ntst2\n"
)
out, err, ret = run_xonsh(
cmd=None,
stdin_cmd=stdin_cmd,
interactive=True,
single_command=False,
timeout=10,
)
assert re.match(
".*sleep.*ZeroDivisionError.*sleep.*ZeroDivisionError.*",
out,
re.MULTILINE | re.DOTALL,
)
assert "Bad file descriptor" not in out
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
@pytest.mark.parametrize(
"cmd,exp",
[
[
"-i",
".*CONFIG_XONSH_RC_XSH.*HOME_XONSHRC.*CONFIG_XONSH_RCD.*CONFIG_XONSH_PY_RCD.*",
],
["--rc rc.xsh", ".*RCXSH.*"],
["-i --rc rc.xsh", ".*RCXSH.*"],
[
"-c print('CMD')",
".*CONFIG_XONSH_RC_XSH.*CONFIG_XONSH_RCD.*CONFIG_XONSH_PY_RCD.*CMD.*",
],
[
"-i -c print('CMD')",
".*CONFIG_XONSH_RC_XSH.*HOME_XONSHRC.*CONFIG_XONSH_RCD.*CONFIG_XONSH_PY_RCD.*CMD.*",
],
[
"script.xsh",
".*CONFIG_XONSH_RC_XSH.*CONFIG_XONSH_RCD.*CONFIG_XONSH_PY_RCD.*SCRIPT.*",
],
[
"-i script.xsh",
".*CONFIG_XONSH_RC_XSH.*HOME_XONSHRC.*CONFIG_XONSH_RCD.*CONFIG_XONSH_PY_RCD.*SCRIPT.*",
],
["--rc rc.xsh -- script.xsh", ".*RCXSH.*SCRIPT.*"],
["-i --rc rc.xsh -- script.xsh", ".*RCXSH.*SCRIPT.*"],
["--no-rc --rc rc.xsh -- script.xsh", ".*SCRIPT.*"],
["-i --no-rc --rc rc.xsh -- script.xsh", ".*SCRIPT.*"],
],
)
def test_xonshrc(tmpdir, cmd, exp):
# ~/.xonshrc
home = tmpdir.mkdir("home")
(home / ".xonshrc").write_text("echo HOME_XONSHRC", encoding="utf8")
home_xonsh_rc_path = str( # crossplatform path
(Path(home) / ".xonshrc").expanduser()
)
# ~/.config/xonsh/rc.xsh
home_config_xonsh = tmpdir.mkdir("home_config_xonsh")
(home_config_xonsh_rc_xsh := home_config_xonsh / "rc.xsh").write_text(
"echo CONFIG_XONSH_RC_XSH", encoding="utf8"
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
)
# ~/.config/xonsh/rc.d/
home_config_xonsh_rcd = tmpdir.mkdir("home_config_xonsh_rcd")
(home_config_xonsh_rcd / "rcd1.xsh").write_text(
"echo CONFIG_XONSH_RCD", encoding="utf8"
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
)
(home_config_xonsh_rcd / "rcd2.py").write_text(
"__xonsh__.print(__xonsh__.subproc_captured_stdout(['echo', 'CONFIG_XONSH_PY_RCD']))",
encoding="utf8",
)
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
# ~/home/rc.xsh
(rc_xsh := home / "rc.xsh").write_text("echo RCXSH", encoding="utf8")
(script_xsh := home / "script.xsh").write_text("echo SCRIPT_XSH", encoding="utf8")
# Construct $XONSHRC and $XONSHRC_DIR.
xonshrc_files = [
str(home_config_xonsh_rc_xsh),
str(home_xonsh_rc_path),
]
xonshrc_dir = [str(home_config_xonsh_rcd)]
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
args = [
f'-DHOME="{str(home)}"',
Do not load `~/.xonshrc` in not interactive mode (#5491) ### Motivation In #5099 was introduced the logic where all RC files are loaded in interactive and non-interactive modes. This logic is not working good for home based `~/.xonshrc` file. First of all `~/.xonshrc` is the buffer/accumulator of settings focused on interactive mode. Most tools with integration with xonsh (e.g. `conda`, `zoxide`, etc) offer to just do `echo "init_tool()" >> ~/.xonshrc` (`conda` has around 20 lines of init code) to start using the tool and users are doing this without any doubts. But because of after 5099 `~/.xonshrc` is executed in non-interactive mode the adding code to it leads to unexpected and unintended side effects: * If you run a script with shebang (e.g. `#!/usr/bin/env xonsh` or [xonsh-awesome-cli-app](https://github.com/anki-code/xonsh-awesome-cli-app)) or just from `xonsh script.xsh` the code will be unexpected and unintended slower. * If you're using xonsh-based tools (e.g. you install them using pip) and run them in environment that has no packages that initiated in `~/.xonshrc` you will see epic errors. * Additional context: * Bash and Zsh do not load `~/.bashrc` and `~/.zshrc` in non-interactive mode by the same reasons. * We have welcome message `Create ~/.xonshrc file manually or use xonfig to suppress the welcome message` and we don't want to make the process of creating this file complex. All of this leads to bad unexpected and unintended experience. This PR is to fix this. ### Expectation By doing this fix we assume that experienced user who wants to build good repeatable run control files will use another ways to create config files and this has [reflection in docs](https://github.com/xonsh/xonsh/blob/8860f2bd5273d5f3fc08ccf6be6af8163bfec0bd/docs/xonshrc.rst). In the nutshell if you want to create the RC files that affect every run of code you should use one or many of these ways: * Cross-desktop group (XDG) compliant `~/.config/xonsh/rc.xsh` control file. * The system-wide control file `/etc/xonsh/xonshrc` for Linux and OSX and in `%ALLUSERSPROFILE%\xonsh\xonshrc` on Windows. It controls options that are applied to all users of Xonsh on a given system. * The home-based directory `~/.config/xonsh/rc.d/` and system `/etc/xonsh/rc.d/` can contain .xsh files. They will be executed at startup in order. This allows for drop-in configuration where your configuration can be split across scripts and common and local configurations more easily separated. In your configs you need to check `$XONSH_INTERACTIVE` and `$XONSH_LOGIN` explicitly. ### Before `~/.xonshrc` is used in non-interactive mode. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript # RC ``` ### After `~/.xonshrc` is not used in non-interactive mode. Use `-i` if you need it. ```xsh echo "echo RC" >> ~/.xonshrc cd /tmp echo "echo Script" > script.xsh xonsh script.xsh # Script xonsh -i script.xsh # RC # Script ``` ```xsh cd /tmp echo 'echo RC' >> ~/.xonshrc echo '#!/usr/bin/env xonsh' > myscript chmod +x myscript ./myscript ``` Closes #5488 #4096 #5496 ### Fun I want to leave here some nice representation of how it works in bash/sh/zsh from [twitter post](https://twitter.com/paxx39/status/1742768007154479109): ![image](https://github.com/xonsh/xonsh/assets/1708680/cd7b3803-483f-4d5d-bf9d-baa61c794f68) ## 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>
2024-06-13 16:08:23 +02:00
f'-DXONSHRC="{os.pathsep.join(xonshrc_files)}"',
f'-DXONSHRC_DIR="{os.pathsep.join(xonshrc_dir)}"',
]
env = {"HOME": str(home)}
cmd = cmd.replace("rc.xsh", str(rc_xsh)).replace("script.xsh", str(script_xsh))
args = args + cmd.split()
# xonsh
out, err, ret = run_xonsh(
cmd=None,
args=args,
env=env,
)
assert re.match(
exp,
out,
re.MULTILINE | re.DOTALL,
), f"Case: xonsh {cmd},\nExpected: {exp!r},\nResult: {out!r},\nargs={args!r}"
@skip_if_no_xonsh
@skip_if_on_windows
def test_shebang_cr(tmpdir):
testdir = tmpdir.mkdir("xonsh_test_dir")
testfile = "shebang_cr.xsh"
expected_out = "I'm xonsh with shebang␍"
(f := testdir / testfile).write_text(
f"""#!/usr/bin/env xonsh\r\nprint("{expected_out}")""", encoding="utf8"
)
os.chmod(f, 0o777)
command = f"cd {testdir}; ./{testfile}\n"
out, err, rtn = run_xonsh(command)
assert out == f"{expected_out}\n"
test_code = [
"""
$XONSH_SHOW_TRACEBACK = True
@aliases.register
def _e(a,i,o,e):
echo -n O
echo -n E 1>2
execx("echo -n O")
execx("echo -n E 1>2")
print("o")
print("O", file=o)
print("E", file=e)
import tempfile
for i in range(0, 12):
echo -n e
print($(e), !(e), $[e], ![e])
print($(e > @(tempfile.NamedTemporaryFile(delete=False).name)))
print(!(e > @(tempfile.NamedTemporaryFile(delete=False).name)))
print($[e > @(tempfile.NamedTemporaryFile(delete=False).name)])
print(![e > @(tempfile.NamedTemporaryFile(delete=False).name)])
"""
]
@skip_if_on_windows
@pytest.mark.parametrize("test_code", test_code)
def test_callable_alias_no_bad_file_descriptor(test_code):
"""Test no exceptions during any kind of capturing of callable alias. See also #5631."""
out, err, ret = run_xonsh(
test_code, interactive=True, single_command=True, timeout=60
)
assert ret == 0
assert "Error" not in out
assert "Exception" not in out