From b19a89b432380e86e169d93952a35aaddf897ecb Mon Sep 17 00:00:00 2001 From: Andy Kipp Date: Mon, 6 May 2024 14:09:10 +0200 Subject: [PATCH] Fixed ``xonsh -DVAR=VAL`` behavior: initiate env variables before shell initialization. (#5396) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Before Case 1: ```xsh xonsh --no-rc --no-env -DCOLOR_OUTPUT 0 # space between -DVAR and VALUE ``` ```xsh Traceback (most recent call last): File "/Users/pc/.local/xonsh-env/lib/python3.12/site-packages/xonsh/main.py", line 478, in main args = premain(argv) ^^^^^^^^^^^^^ File "/Users/pc/.local/xonsh-env/lib/python3.12/site-packages/xonsh/main.py", line 420, in premain env.update([x.split("=", 1) for x in args.defines]) File "", line 987, in update ValueError: not enough values to unpack (expected 2, got 1) Xonsh encountered an issue during launch ``` Case 2: ```xsh xonsh --no-rc --no-env -DXONSH_HISTORY_BACKEND=dummy history info # json # EXPECTED: dummy ``` ### After Case 1: ```xsh xonsh --no-rc --no-env -DCOLOR_OUTPUT 0 # Wrong format for -DCOLOR_OUTPUT argument. Use -DVAR=VAL form. ``` Case 2: ```xsh xonsh --no-rc --no-env -DXONSH_HISTORY_BACKEND=dummy history info # dummy ``` Closes #5395 ## 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> --- news/main_d.rst | 23 +++++++++++++++++++++++ tests/test_integrations.py | 16 +++++++++++++++- xonsh/main.py | 17 +++++++++++++++-- xonsh/pygments_cache.py | 2 +- xonsh/tools.py | 7 +++++++ 5 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 news/main_d.rst diff --git a/news/main_d.rst b/news/main_d.rst new file mode 100644 index 000000000..ea5660f9a --- /dev/null +++ b/news/main_d.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Fixed ``xonsh -DVAR=VAL`` behavior: initiate env variables before shell initialization. + +**Security:** + +* diff --git a/tests/test_integrations.py b/tests/test_integrations.py index bdc303313..deccff8bf 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -49,6 +49,7 @@ def run_xonsh( single_command=False, interactive=False, path=None, + add_args: list = None, ): env = dict(os.environ) if path is None: @@ -69,7 +70,8 @@ def run_xonsh( input = None else: input = cmd - + if add_args: + args += add_args proc = sp.Popen( args, env=env, @@ -1040,3 +1042,15 @@ def test_raise_subproc_error_with_show_traceback(monkeypatch, interactive): ) 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( + add_args=["-DXONSH_HISTORY_BACKEND='dummy'"], + cmd="print($XONSH_HISTORY_BACKEND)", + single_command=True, + ) + assert out == "dummy\n" diff --git a/xonsh/main.py b/xonsh/main.py index 7feca0221..f914db049 100644 --- a/xonsh/main.py +++ b/xonsh/main.py @@ -29,6 +29,7 @@ from xonsh.tools import ( print_color, print_exception, to_bool_or_int, + unquote, ) from xonsh.xonfig import print_welcome_screen from xonsh.xontribs import auto_load_xontribs_from_entrypoints, xontribs_load @@ -415,9 +416,21 @@ def premain(argv=None): "XONSH_INTERACTIVE": args.force_interactive or (args.mode == XonshMode.interactive), } - env = start_services(shell_kwargs, args, pre_env=pre_env) + + # Load -DVAR=VAL arguments. if args.defines is not None: - env.update([x.split("=", 1) for x in args.defines]) + for x in args.defines: + try: + var, val = x.split("=", 1) + pre_env[var] = unquote(val) + except Exception: + print( + f"Wrong format for -D{x} argument. Use -DVAR=VAL form.", + file=sys.stderr, + ) + sys.exit(1) + + start_services(shell_kwargs, args, pre_env=pre_env) return args diff --git a/xonsh/pygments_cache.py b/xonsh/pygments_cache.py index e667bc5d1..d9c9cf986 100644 --- a/xonsh/pygments_cache.py +++ b/xonsh/pygments_cache.py @@ -306,7 +306,7 @@ def write_cache(filename): d = os.path.dirname(filename) os.makedirs(d, exist_ok=True) s = pformat(CACHE) - with open(filename, "w") as f: + with open(filename, "w", encoding="utf-8") as f: f.write(s) diff --git a/xonsh/tools.py b/xonsh/tools.py index e38ae0e10..ae0f033b7 100644 --- a/xonsh/tools.py +++ b/xonsh/tools.py @@ -2854,3 +2854,10 @@ def describe_waitpid_status(status): ] for f in funcs: print(f.__name__, "-", f(status), "-", f.__doc__) + + +def unquote(s: str, chars="'\""): + """Strip paired quotes from string once.""" + if len(s) >= 2 and s[0] == s[-1] and s[0] in chars: + return s[1:-1] + return s