Repository: xonsh/xonsh

Issue:
When I run a builtin command (i.e. xonfig -h, xontrib -h, etc.) the shell exits

_Originally posted by @Serrindipity in https://github.com/xonsh/xonsh/discussions/5688_

Whenever I run an invalid xonsh command (i.e. xonfig -h, xontrib -h, etc.), the shell exits. I'm pretty new to all of this, so I'm not sure if I'm doing something wrong. This persists even when running xonsh --no-rc --no-env

Xonsh is installed with brew, version 0.18.3 on MacOS.

I've tried uninstalling and re-installing as well. Not sure what's happening.


Discussion:

Patch:
diff --git a/news/fix-procproxy-sys-exit.rst b/news/fix-procproxy-sys-exit.rst
new file mode 100644
index 00000000..b1a14081
--- /dev/null
+++ b/news/fix-procproxy-sys-exit.rst
@@ -0,0 +1,23 @@
+**Added:**
+
+* <news item>
+
+**Changed:**
+
+* Now SystemExit that invoked from callable alias follows to exiting from callable alias instead of exiting the entire shell.
+
+**Deprecated:**
+
+* <news item>
+
+**Removed:**
+
+* <news item>
+
+**Fixed:**
+
+* Built-in commands such as `xonfig -h` and `xontrib -h` no longer cause the shell to exit.
+
+**Security:**
+
+* <news item>
diff --git a/xonsh/procs/proxies.py b/xonsh/procs/proxies.py
index 6796b77f..0fd29baf 100644
--- a/xonsh/procs/proxies.py
+++ b/xonsh/procs/proxies.py
@@ -792,6 +792,10 @@ class ProcProxy:
                         "stack": spec.stack,
                     },
                 )
+        except SystemExit as e:
+            # the alias function is running in the main thread, so we need to
+            # catch SystemExit to prevent the entire shell from exiting (see #5689)
+            r = e.code if isinstance(e.code, int) else int(bool(e.code))
         except Exception:
             xt.print_exception(source_msg="Exception in " + get_proc_proxy_name(self))
             r = 1


Test patch:
diff --git a/tests/test_integrations.py b/tests/test_integrations.py
index 915a82d5..8609920f 100644
--- a/tests/test_integrations.py
+++ b/tests/test_integrations.py
@@ -329,6 +329,22 @@ f
         "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,
+    ),
     # test ambiguous globs
     (
         """