From dc8728bdafcf3c1967305df57451c225e8bf8ba2 Mon Sep 17 00:00:00 2001 From: Anthony Scopatz Date: Sat, 5 Oct 2019 16:37:45 -0400 Subject: [PATCH] Added THREAD_SUBPROCS env var --- tests/test_builtins.py | 28 ++++++++++++++++++++++++++++ xonsh/built_ins.py | 6 ++++-- xonsh/environ.py | 22 ++++++++++++++++++++++ xontrib/bashisms.py | 2 ++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/tests/test_builtins.py b/tests/test_builtins.py index 29950df65..36dcd2c59 100644 --- a/tests/test_builtins.py +++ b/tests/test_builtins.py @@ -6,6 +6,7 @@ import re import builtins import types from ast import AST, Module, Interactive, Expression +from subprocess import Popen import pytest @@ -25,8 +26,10 @@ from xonsh.built_ins import ( in_macro_call, call_macro, enter_macro, + cmds_to_specs, ) from xonsh.environ import Env +from xonsh.proc import PopenThread, ProcProxy, ProcProxyThread from tools import skip_if_on_windows @@ -388,3 +391,28 @@ def test_enter_macro(): assert obj.macro_block == "wakka" assert obj.macro_globals assert obj.macro_locals + + +@skip_if_on_windows +def test_cmds_to_specs_thread_subproc(xonsh_builtins): + env = xonsh_builtins.__xonsh__.env + cmds = [["pwd"]] + # First check that threadable subprocs become threadable + env['THREAD_SUBPROCS'] = True + specs = cmds_to_specs(cmds, captured='hiddenobject') + assert specs[0].cls is PopenThread + # turn off threading and check we use Popen + env['THREAD_SUBPROCS'] = False + specs = cmds_to_specs(cmds, captured='hiddenobject') + assert specs[0].cls is Popen + + # now check the threadbility of callable aliases + cmds = [[lambda: "Keras Selyrian"]] + # check that threadable alias become threadable + env['THREAD_SUBPROCS'] = True + specs = cmds_to_specs(cmds, captured='hiddenobject') + assert specs[0].cls is ProcProxyThread + # turn off threading and check we use ProcProxy + env['THREAD_SUBPROCS'] = False + specs = cmds_to_specs(cmds, captured='hiddenobject') + assert specs[0].cls is ProcProxy diff --git a/xonsh/built_ins.py b/xonsh/built_ins.py index 586c0ad99..f522af7a4 100644 --- a/xonsh/built_ins.py +++ b/xonsh/built_ins.py @@ -738,7 +738,8 @@ class SubprocSpec: if not callable(alias): return self.is_proxy = True - thable = getattr(alias, "__xonsh_threadable__", True) + env = builtins.__xonsh__.env + thable = env.get('THREAD_SUBPROCS') and getattr(alias, "__xonsh_threadable__", True) cls = ProcProxyThread if thable else ProcProxy self.cls = cls self.threadable = thable @@ -790,6 +791,7 @@ def _safe_pipe_properties(fd, use_tty=False): def _update_last_spec(last): + env = builtins.__xonsh__.env captured = last.captured last.last_in_pipeline = True if not captured: @@ -799,7 +801,7 @@ def _update_last_spec(last): pass else: cmds_cache = builtins.__xonsh__.commands_cache - thable = cmds_cache.predict_threadable( + thable = env.get('THREAD_SUBPROCS') and cmds_cache.predict_threadable( last.args ) and cmds_cache.predict_threadable(last.cmd) if captured and thable: diff --git a/xonsh/environ.py b/xonsh/environ.py index 500ce14ee..e3dba35e0 100644 --- a/xonsh/environ.py +++ b/xonsh/environ.py @@ -571,6 +571,7 @@ def DEFAULT_ENSURERS(): "SUGGEST_MAX_NUM": (is_int, int, str), "SUGGEST_THRESHOLD": (is_int, int, str), "SUPPRESS_BRANCH_TIMEOUT_MESSAGE": (is_bool, to_bool, bool_to_str), + "THREAD_SUBPROCS": (is_bool, to_bool, bool_to_str), "UPDATE_COMPLETIONS_ON_KEYPRESS": (is_bool, to_bool, bool_to_str), "UPDATE_OS_ENVIRON": (is_bool, to_bool, bool_to_str), "UPDATE_PROMPT_ON_KEYPRESS": (is_bool, to_bool, bool_to_str), @@ -755,6 +756,7 @@ def DEFAULT_VALUES(): "SUGGEST_COMMANDS": True, "SUGGEST_MAX_NUM": 5, "SUGGEST_THRESHOLD": 3, + "THREAD_SUBPROCS": True, "TITLE": DEFAULT_TITLE, "UPDATE_COMPLETIONS_ON_KEYPRESS": False, "UPDATE_OS_ENVIRON": False, @@ -1159,6 +1161,26 @@ def DEFAULT_DOCS(): "not always happen.", configurable=False, ), + "THREAD_SUBPROCS": VarDocs( + "Whether or not to try to run subrocess mode in a Python thread, " + "when applicable. There are various trade-offs, which normally " + "affects only interactive sessions.\n\nWhen True:\n\n" + "* Xonsh is able capture & store the stdin, stdout, and stderr \n" + " of threadable subprocesses.\n" + "* However, stopping threaded suprocs with ^Z (i.e. ``SIGTSTP``)\n" + " is disabled as it causes deadlocked terminals.\n" + " ``SIGTSTP`` may still be issued and only the physical pressing\n" + " of ``Ctrl+Z`` is ignored.\n" + "* Thredable commands are run with ``PopenThread`` and threadable \n" + " alias are run with ``ProcProxyThread``.\n\n" + "When False:\n\n" + "* Xonsh may not be able to capture stdin, stdout, and stderr streams \n" + " unless explicitly asked to do so.\n" + "* Stopping the thread with yields to job control.\n" + "* Thredable commands are run with ``Popen`` and threadable \n" + " alias are run with ``ProcProxy``.\n\n" + "The desired effect is often up to the command, user, or use case." + ), "TITLE": VarDocs( "The title text for the window in which xonsh is running. Formatted " "in the same manner as ``$PROMPT``, see 'Customizing the Prompt' " diff --git a/xontrib/bashisms.py b/xontrib/bashisms.py index 567602aac..e4c8ae0ab 100644 --- a/xontrib/bashisms.py +++ b/xontrib/bashisms.py @@ -2,6 +2,7 @@ import shlex import sys import re +import builtins __all__ = () @@ -62,3 +63,4 @@ def alias(args, stdin=None): aliases["alias"] = alias +builtins.__xonsh__.env['THREAD_SUBPROCS'] = False