mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
spec: added raise_subproc_error (#5494)
### Motivation Add `spec.raise_subproc_error` to have an ability to use `SpecModifierAlias` to manage the errors. Also this is the first step to #4351. Generally in scripts it's good to have RAISE_SUBPROC_ERROR=True to avoid processing the error for every executed command. But in some cases (e.g. `![]`) it's needed to avoid raising the error. To more elegant doing this we can make an ability to create SpecModifier. ### After ```xsh from xonsh.procs.specs import SpecModifierAlias class SpecModifierNoErrAlias(SpecModifierAlias): def on_modifer_added(self, spec): spec.raise_subproc_error = False aliases['noraise'] = SpecModifierNoErrAlias() $RAISE_SUBPROC_ERROR = True if ![noraise git pull]: git add --all ``` Cc #5443 ## For community ⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍 comment** --------- Co-authored-by: a <1@1.1> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
6b9f6ba0e1
commit
4e12834e84
4 changed files with 83 additions and 7 deletions
23
news/spec_raise_subproc_error.rst
Normal file
23
news/spec_raise_subproc_error.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* Added ``spec.raise_subproc_error`` for fine-tuning exceptions via ``SpecModifierAlias`` (#5494).
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
|
@ -3,7 +3,7 @@
|
||||||
import itertools
|
import itertools
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
from subprocess import Popen
|
from subprocess import CalledProcessError, Popen
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -163,6 +163,51 @@ def test_interrupted_process_returncode(xonsh_session, captured, interactive):
|
||||||
assert p.proc.returncode == -signal.SIGINT
|
assert p.proc.returncode == -signal.SIGINT
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_on_windows
|
||||||
|
def test_proc_raise_subproc_error(xonsh_session):
|
||||||
|
xonsh_session.env["RAISE_SUBPROC_ERROR"] = False
|
||||||
|
|
||||||
|
specs = cmds_to_specs(cmd := [["ls"]], captured="stdout")
|
||||||
|
specs[-1].raise_subproc_error = True
|
||||||
|
exception = None
|
||||||
|
try:
|
||||||
|
(p := _run_command_pipeline(specs, cmd)).end()
|
||||||
|
assert p.proc.returncode == 0
|
||||||
|
except Exception as e:
|
||||||
|
exception = e
|
||||||
|
assert exception is None
|
||||||
|
|
||||||
|
specs = cmds_to_specs(cmd := [["ls", "nofile"]], captured="stdout")
|
||||||
|
specs[-1].raise_subproc_error = False
|
||||||
|
exception = None
|
||||||
|
try:
|
||||||
|
(p := _run_command_pipeline(specs, cmd)).end()
|
||||||
|
assert p.proc.returncode > 0
|
||||||
|
except Exception as e:
|
||||||
|
exception = e
|
||||||
|
assert exception is None
|
||||||
|
|
||||||
|
specs = cmds_to_specs(cmd := [["ls", "nofile"]], captured="stdout")
|
||||||
|
specs[-1].raise_subproc_error = True
|
||||||
|
exception = None
|
||||||
|
try:
|
||||||
|
(p := _run_command_pipeline(specs, cmd)).end()
|
||||||
|
except Exception as e:
|
||||||
|
assert p.proc.returncode > 0
|
||||||
|
exception = e
|
||||||
|
assert isinstance(exception, CalledProcessError)
|
||||||
|
|
||||||
|
xonsh_session.env["RAISE_SUBPROC_ERROR"] = True
|
||||||
|
specs = cmds_to_specs(cmd := [["ls", "nofile"]], captured="stdout")
|
||||||
|
exception = None
|
||||||
|
try:
|
||||||
|
(p := _run_command_pipeline(specs, cmd)).end()
|
||||||
|
except Exception as e:
|
||||||
|
assert p.proc.returncode > 0
|
||||||
|
exception = e
|
||||||
|
assert isinstance(exception, CalledProcessError)
|
||||||
|
|
||||||
|
|
||||||
@skip_if_on_windows
|
@skip_if_on_windows
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"suspended_pipeline",
|
"suspended_pipeline",
|
||||||
|
|
|
@ -627,14 +627,21 @@ class CommandPipeline:
|
||||||
spec = self.spec
|
spec = self.spec
|
||||||
rtn = self.returncode
|
rtn = self.returncode
|
||||||
|
|
||||||
if rtn is None or rtn == 0 or not XSH.env.get("RAISE_SUBPROC_ERROR"):
|
if rtn is None or rtn == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
raise_subproc_error = spec.raise_subproc_error
|
||||||
raise subprocess.CalledProcessError(rtn, spec.args, output=self.output)
|
if callable(raise_subproc_error):
|
||||||
finally:
|
raise_subproc_error = raise_subproc_error(spec, self)
|
||||||
# this is need to get a working terminal in interactive mode
|
if raise_subproc_error is False:
|
||||||
self._return_terminal()
|
return
|
||||||
|
|
||||||
|
if raise_subproc_error or XSH.env.get("RAISE_SUBPROC_ERROR", True):
|
||||||
|
try:
|
||||||
|
raise subprocess.CalledProcessError(rtn, spec.args, output=self.output)
|
||||||
|
finally:
|
||||||
|
# this is need to get a working terminal in interactive mode
|
||||||
|
self._return_terminal()
|
||||||
|
|
||||||
#
|
#
|
||||||
# Properties
|
# Properties
|
||||||
|
|
|
@ -421,6 +421,7 @@ class SubprocSpec:
|
||||||
self.stack = None
|
self.stack = None
|
||||||
self.spec_modifiers = [] # List of SpecModifierAlias objects that applied to spec.
|
self.spec_modifiers = [] # List of SpecModifierAlias objects that applied to spec.
|
||||||
self.output_format = XSH.env.get("XONSH_SUBPROC_OUTPUT_FORMAT", "stream_lines")
|
self.output_format = XSH.env.get("XONSH_SUBPROC_OUTPUT_FORMAT", "stream_lines")
|
||||||
|
self.raise_subproc_error = None # Spec-based $RAISE_SUBPROC_ERROR.
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
s = self.__class__.__name__ + "(" + str(self.cmd) + ", "
|
s = self.__class__.__name__ + "(" + str(self.cmd) + ", "
|
||||||
|
|
Loading…
Add table
Reference in a new issue