Repository: xonsh/xonsh

Issue:
Run xonsh with `-DVAR VAL`: ValueError: not enough values to unpack
```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 "<frozen _collections_abc>", line 987, in update
ValueError: not enough values to unpack (expected 2, got 1)
Xonsh encountered an issue during launch
```

## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**


Discussion:

Patch:
diff --git a/news/main_d.rst b/news/main_d.rst
new file mode 100644
index 00000000..ea5660f9
--- /dev/null
+++ b/news/main_d.rst
@@ -0,0 +1,23 @@
+**Added:**
+
+* <news item>
+
+**Changed:**
+
+* <news item>
+
+**Deprecated:**
+
+* <news item>
+
+**Removed:**
+
+* <news item>
+
+**Fixed:**
+
+* Fixed ``xonsh -DVAR=VAL`` behavior: initiate env variables before shell initialization.
+
+**Security:**
+
+* <news item>
diff --git a/xonsh/main.py b/xonsh/main.py
index 7feca022..f914db04 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 e667bc5d..d9c9cf98 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 e38ae0e1..ae0f033b 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


Test patch:
diff --git a/tests/test_integrations.py b/tests/test_integrations.py
index bdc30331..deccff8b 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"