Repository: hylang/hy

Issue:
REPL not respecting `sys.ps1` `sys.ps2` if set in `HYSTARTUP`
The [documentation for the REPL](https://docs.hylang.org/en/stable/repl.html#startup-files) gives an example of setting the REPL prompt. Here is the simplest hyrc to illustrate the problem.

```hy
(import sys)
(setv sys.ps1 "* ")
(print f"sys.ps1 overridden to {sys.ps1}.")
```
Starting hy from bash (with HYSTARTUP set appropriately) does not replace sys.ps1. Setting sys.ps1 subsequently gives the expected behaviour.

```bash
> hy
sys.ps1 overridden to * .
Hy 0.28.0 using CPython(main) 3.11.7 on Linux
=> sys.ps1
"=> "
=>  (setv sys.ps1 "* ")
* 
```


Discussion:
I think this used to work, but then I broke it as part of how I implemented restoration of the old `sys.ps1` and `sys.ps2` when quitting from a Hy REPL launched inside the Python REPL (#2355). Probably the right thing to do is have the user set different variables, say `repl-ps1` and `repl-ps2`. The way the old setup worked, if you customized Hy's prompts, Python's would change to the same values, and vice versa.

Resolving patch:
diff --git a/hy/repl.py b/hy/repl.py
--- a/hy/repl.py
+++ b/hy/repl.py
@@ -257,6 +257,9 @@ def __init__(self, spy=False, spy_delimiter=('-' * 30), output_fn=None, locals=N
         self.module.__dict__.update(self.locals)
         self.locals = self.module.__dict__
 
+        self.ps1 = "=> "
+        self.ps2 = "... "
+
         if os.environ.get("HYSTARTUP"):
             try:
                 loader = HyLoader("__hystartup__", os.environ.get("HYSTARTUP"))
@@ -271,6 +274,8 @@ def __init__(self, spy=False, spy_delimiter=('-' * 30), output_fn=None, locals=N
                 imports = {name: mod.__dict__[name] for name in imports}
                 spy = spy or imports.get("repl_spy")
                 output_fn = output_fn or imports.get("repl_output_fn")
+                self.ps1 = imports.get("repl_ps1", self.ps1)
+                self.ps2 = imports.get("repl_ps2", self.ps2)
 
                 # Load imports and defs
                 self.locals.update(imports)
@@ -421,8 +426,8 @@ def run(self):
             builtins.help,
         )
         try:
-            sys.ps1 = "=> "
-            sys.ps2 = "... "
+            sys.ps1 = self.ps1
+            sys.ps2 = self.ps2
             builtins.quit = HyQuitter("quit")
             builtins.exit = HyQuitter("exit")
             builtins.help = HyHelper()


Test patch:
diff --git a/tests/resources/hystartup.hy b/tests/resources/hystartup.hy
--- a/tests/resources/hystartup.hy
+++ b/tests/resources/hystartup.hy
@@ -2,7 +2,8 @@
 
 (setv repl-spy True
       repl-output-fn (fn [x]
-        (.replace (repr x) " " "_")))
+        (.replace (repr x) " " "_"))
+      repl-ps1 "p1? ")
 
 (defmacro hello-world []
   `(+ 1 1))
diff --git a/tests/test_bin.py b/tests/test_bin.py
--- a/tests/test_bin.py
+++ b/tests/test_bin.py
@@ -25,8 +25,8 @@ def pyr(s=""):
 
 def run_cmd(
         cmd, stdin_data=None, expect=0, dontwritebytecode=False,
-        cwd=None, stdout=subprocess.PIPE):
-    env = dict(os.environ)
+        cwd=None, stdout=subprocess.PIPE, env=None):
+    env = {**dict(os.environ), **(env or {})}
     if dontwritebytecode:
         env["PYTHONDONTWRITEBYTECODE"] = "1"
     else:
@@ -149,21 +149,20 @@ def test_mangle_m():
 
 
 def test_ignore_python_env():
-    os.environ.update({"PYTHONTEST": "0"})
-    output, _ = run_cmd("hy -c '(print (do (import os) (. os environ)))'")
+    e = dict(PYTHONTEST = "0")
+
+    output, _ = run_cmd("hy -c '(print (do (import os) (. os environ)))'", env = e)
     assert "PYTHONTEST" in output
-    output, _ = run_cmd("hy -m tests.resources.bin.printenv")
+    output, _ = run_cmd("hy -m tests.resources.bin.printenv", env = e)
     assert "PYTHONTEST" in output
-    output, _ = run_cmd("hy tests/resources/bin/printenv.hy")
+    output, _ = run_cmd("hy tests/resources/bin/printenv.hy", env = e)
     assert "PYTHONTEST" in output
 
-    output, _ = run_cmd("hy -E -c '(print (do (import os) (. os environ)))'")
+    output, _ = run_cmd("hy -E -c '(print (do (import os) (. os environ)))'", env = e)
     assert "PYTHONTEST" not in output
-    os.environ.update({"PYTHONTEST": "0"})
-    output, _ = run_cmd("hy -E -m tests.resources.bin.printenv")
+    output, _ = run_cmd("hy -E -m tests.resources.bin.printenv", env = e)
     assert "PYTHONTEST" not in output
-    os.environ.update({"PYTHONTEST": "0"})
-    output, _ = run_cmd("hy -E tests/resources/bin/printenv.hy")
+    output, _ = run_cmd("hy -E tests/resources/bin/printenv.hy", env = e)
     assert "PYTHONTEST" not in output
 
 
@@ -498,34 +497,34 @@ def test_traceback_shebang(tmp_path):
 
 def test_hystartup():
     # spy == True and custom repl-output-fn
-    os.environ["HYSTARTUP"] = "tests/resources/hystartup.hy"
-    output, _ = run_cmd("hy -i", "[1 2]")
+    env = dict(HYSTARTUP = "tests/resources/hystartup.hy")
+    output, _ = run_cmd("hy -i", "[1 2]", env = env)
+    assert "p1? " in output
     assert "[1, 2]" in output
     assert "[1,_2]" in output
 
-    output, _ = run_cmd("hy -i", "(hello-world)")
+    output, _ = run_cmd("hy -i", "(hello-world)", env = env)
     assert "(hello-world)" not in output
     assert "1 + 1" in output
     assert "2" in output
 
-    output, _ = run_cmd("hy -i", "#rad")
+    output, _ = run_cmd("hy -i", "#rad", env = env)
     assert "#rad" not in output
     assert "'totally' + 'rad'" in output
     assert "'totallyrad'" in output
 
-    output, _ = run_cmd("hy -i --repl-output-fn repr", "[1 2 3 4]")
+    output, _ = run_cmd("hy -i --repl-output-fn repr", "[1 2 3 4]", env = env)
     assert "[1, 2, 3, 4]" in output
     assert "[1 2 3 4]" not in output
     assert "[1,_2,_3,_4]" not in output
 
     # spy == False and custom repl-output-fn
-    os.environ["HYSTARTUP"] = "tests/resources/spy_off_startup.hy"
-    output, _ = run_cmd("hy -i --spy", "[1 2]")  # overwrite spy with cmdline arg
+    # Then overwrite spy with cmdline arg
+    output, _ = run_cmd("hy -i --spy", "[1 2]",
+         env = dict(HYSTARTUP = "tests/resources/spy_off_startup.hy"))
     assert "[1, 2]" in output
     assert "[1,~2]" in output
 
-    del os.environ["HYSTARTUP"]
-
 
 def test_output_buffering(tmp_path):
     tf = tmp_path / "file.txt"