From 79fd77fa0f06f8fa362bfd671b85828de81f0328 Mon Sep 17 00:00:00 2001 From: Aaron Leese Date: Fri, 29 Nov 2024 14:39:35 -0600 Subject: [PATCH 1/5] add check for windows and dont use os.setsid/getpgid() if running on windows --- src/juv/_run_replace.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/juv/_run_replace.py b/src/juv/_run_replace.py index c38e838..60052fc 100644 --- a/src/juv/_run_replace.py +++ b/src/juv/_run_replace.py @@ -1,21 +1,32 @@ from __future__ import annotations import os +import platform import signal import subprocess import sys from uv import find_uv_bin +IS_WINDOWS = platform.system() def run(script: str, args: list[str]) -> None: - process = subprocess.Popen( # noqa: S603 - [os.fsdecode(find_uv_bin()), *args], - stdin=subprocess.PIPE, - stdout=sys.stdout, - stderr=sys.stderr, - preexec_fn=os.setsid, # noqa: PLW1509 - ) + if not IS_WINDOWS: + process = subprocess.Popen( # noqa: S603 + [os.fsdecode(find_uv_bin()), *args], + stdin=subprocess.PIPE, + stdout=sys.stdout, + stderr=sys.stderr, + preexec_fn=os.setsid, # noqa: PLW1509 + ) + else: + process = subprocess.Popen( # noqa: S603 + [os.fsdecode(find_uv_bin()), *args], + stdin=subprocess.PIPE, + stdout=sys.stdout, + stderr=sys.stderr, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP + ) assert process.stdin is not None # noqa: S101 process.stdin.write(script.encode()) @@ -25,6 +36,9 @@ def run(script: str, args: list[str]) -> None: try: process.wait() except KeyboardInterrupt: - os.killpg(os.getpgid(process.pid), signal.SIGTERM) + if not IS_WINDOWS: + os.killpg(os.getpgid(process.pid), signal.SIGTERM) + else: + os.kill(process.pid, signal.SIGTERM) finally: process.wait() From f5c9789153691578f0d79dfaa752390cdd73dbe7 Mon Sep 17 00:00:00 2001 From: Aaron Leese Date: Fri, 29 Nov 2024 14:42:26 -0600 Subject: [PATCH 2/5] Add ignore_cleanup_errors=True to avoid an error on closing of jupyterlab, and added raw string identifier before the notebook path in case the notebook's path has a \t in it, so that doesnt get turned into a tab --- src/juv/_run_template.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/juv/_run_template.py b/src/juv/_run_template.py index c123774..c85ae8c 100644 --- a/src/juv/_run_template.py +++ b/src/juv/_run_template.py @@ -75,7 +75,7 @@ def as_with_arg(self) -> str: juv_data_dir = Path(user_data_dir("juv")) juv_data_dir.mkdir(parents=True, exist_ok=True) -temp_dir = tempfile.TemporaryDirectory(dir=juv_data_dir) +temp_dir = tempfile.TemporaryDirectory(dir=juv_data_dir, ignore_cleanup_errors=True) merged_dir = Path(temp_dir.name) def handle_termination(signum, frame): @@ -129,7 +129,7 @@ def handle_termination(signum, frame): version = importlib.metadata.version("jupyterlab") print("JUV_MANGED=" + "jupyterlab" + "," + version, file=sys.stderr) -sys.argv = ["jupyter-lab", "{notebook}", *{args}] +sys.argv = ["jupyter-lab", r"{notebook}", *{args}] main() """ @@ -148,7 +148,7 @@ def handle_termination(signum, frame): version = importlib.metadata.version("notebook") print("JUV_MANGED=" + "notebook" + "," + version, file=sys.stderr) -sys.argv = ["jupyter-notebook", "{notebook}", *{args}] +sys.argv = ["jupyter-notebook", r"{notebook}", *{args}] main() """ @@ -167,7 +167,7 @@ def handle_termination(signum, frame): version = importlib.metadata.version("notebook") print("JUV_MANGED=" + "notebook" + "," + version, file=sys.stderr) -sys.argv = ["jupyter-notebook", "{notebook}", *{args}] +sys.argv = ["jupyter-notebook", r"{notebook}", *{args}] main() """ @@ -187,7 +187,7 @@ def handle_termination(signum, frame): print("JUV_MANGED=" + "nbclassic" + "," + version, file=sys.stderr) os.environ["JUPYTER_DATA_DIR"] = str(merged_dir) -sys.argv = ["jupyter-nbclassic", "{notebook}", *{args}] +sys.argv = ["jupyter-nbclassic", r"{notebook}", *{args}] main() """ From e5078a4f509185d781a68a30045752f689c33fae Mon Sep 17 00:00:00 2001 From: Aaron Leese Date: Fri, 29 Nov 2024 14:43:21 -0600 Subject: [PATCH 3/5] avoid unicode errors and temp dir cleanup errors on windows --- tests/test_juv.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/test_juv.py b/tests/test_juv.py index 84a9464..de22fb2 100644 --- a/tests/test_juv.py +++ b/tests/test_juv.py @@ -810,7 +810,9 @@ def test_stamp( ) -> None: # we need to run these tests in this folder because it uses the git history - with tempfile.TemporaryDirectory(dir=SELF_DIR) as tmpdir: + with tempfile.TemporaryDirectory( + dir=SELF_DIR, ignore_cleanup_errors=True + ) as tmpdir: tmp_path = pathlib.Path(tmpdir) monkeypatch.chdir(tmp_path) @@ -840,7 +842,9 @@ def test_stamp_script( ) -> None: # we need to run these tests in this folder because it uses the git history - with tempfile.TemporaryDirectory(dir=SELF_DIR) as tmpdir: + with tempfile.TemporaryDirectory( + dir=SELF_DIR, ignore_cleanup_errors=True + ) as tmpdir: tmp_path = pathlib.Path(tmpdir) monkeypatch.chdir(tmp_path) @@ -851,11 +855,11 @@ def test_stamp_script( # /// -def main() -> None: │ - print("Hello from foo.py!") │ - │ - │ -if __name__ == "__main__": │ +def main() -> None: | + print("Hello from foo.py!") | + | + | +if __name__ == "__main__": | main() """) result = invoke(["stamp", "foo.py", "--date", "2006-01-02"]) @@ -874,11 +878,11 @@ def main() -> None: # /// -def main() -> None: │ - print("Hello from foo.py!") │ - │ - │ -if __name__ == "__main__": │ +def main() -> None: | + print("Hello from foo.py!") | + | + | +if __name__ == "__main__": | main() """) @@ -889,7 +893,9 @@ def test_stamp_clear( ) -> None: # we need to run these tests in this folder because it uses the git history - with tempfile.TemporaryDirectory(dir=SELF_DIR) as tmpdir: + with tempfile.TemporaryDirectory( + dir=SELF_DIR, ignore_cleanup_errors=True + ) as tmpdir: tmp_path = pathlib.Path(tmpdir) monkeypatch.chdir(tmp_path) From 99a092162399fef72ed0a48a1a4b72b1b2a2f9d9 Mon Sep 17 00:00:00 2001 From: Aaron Leese Date: Tue, 3 Dec 2024 18:04:43 -0600 Subject: [PATCH 4/5] platform and tempdir changes --- src/juv/_run_replace.py | 8 ++++---- src/juv/_run_template.py | 11 ++++++++++- tests/test_juv.py | 22 +++++++++++++--------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/juv/_run_replace.py b/src/juv/_run_replace.py index 60052fc..2f87402 100644 --- a/src/juv/_run_replace.py +++ b/src/juv/_run_replace.py @@ -1,14 +1,14 @@ from __future__ import annotations import os -import platform import signal import subprocess import sys from uv import find_uv_bin -IS_WINDOWS = platform.system() +IS_WINDOWS = sys.platform.startswith("win") + def run(script: str, args: list[str]) -> None: if not IS_WINDOWS: @@ -25,8 +25,8 @@ def run(script: str, args: list[str]) -> None: stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr, - creationflags=subprocess.CREATE_NEW_PROCESS_GROUP - ) + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP, + ) assert process.stdin is not None # noqa: S101 process.stdin.write(script.encode()) diff --git a/src/juv/_run_template.py b/src/juv/_run_template.py index c85ae8c..bd8afe4 100644 --- a/src/juv/_run_template.py +++ b/src/juv/_run_template.py @@ -75,7 +75,16 @@ def as_with_arg(self) -> str: juv_data_dir = Path(user_data_dir("juv")) juv_data_dir.mkdir(parents=True, exist_ok=True) -temp_dir = tempfile.TemporaryDirectory(dir=juv_data_dir, ignore_cleanup_errors=True) +# Custom TemporaryDirectory for Python < 3.10 +# TODO: Use `ignore_cleanup_errors=True` in Python 3.10+ +class TemporaryDirectoryIgnoreErrors(tempfile.TemporaryDirectory): + def cleanup(self): + try: + super().cleanup() + except Exception: + pass # Ignore cleanup errors + +temp_dir = TemporaryDirectory(dir=juv_data_dir) merged_dir = Path(temp_dir.name) def handle_termination(signum, frame): diff --git a/tests/test_juv.py b/tests/test_juv.py index de22fb2..875b42d 100644 --- a/tests/test_juv.py +++ b/tests/test_juv.py @@ -21,6 +21,16 @@ SELF_DIR = pathlib.Path(__file__).parent +# Custom TemporaryDirectory for Python < 3.10 +# TODO: Use `ignore_cleanup_errors=True` in Python 3.10+ +class TemporaryDirectoryIgnoreErrors(tempfile.TemporaryDirectory): + def cleanup(self): + try: + super().cleanup() + except Exception: + pass # Ignore cleanup errors + + def invoke(args: list[str], uv_python: str = "3.13") -> Result: return CliRunner().invoke( cli, @@ -810,9 +820,7 @@ def test_stamp( ) -> None: # we need to run these tests in this folder because it uses the git history - with tempfile.TemporaryDirectory( - dir=SELF_DIR, ignore_cleanup_errors=True - ) as tmpdir: + with TemporaryDirectoryIgnoreErrors(dir=SELF_DIR) as tmpdir: tmp_path = pathlib.Path(tmpdir) monkeypatch.chdir(tmp_path) @@ -842,9 +850,7 @@ def test_stamp_script( ) -> None: # we need to run these tests in this folder because it uses the git history - with tempfile.TemporaryDirectory( - dir=SELF_DIR, ignore_cleanup_errors=True - ) as tmpdir: + with TemporaryDirectoryIgnoreErrors(dir=SELF_DIR) as tmpdir: tmp_path = pathlib.Path(tmpdir) monkeypatch.chdir(tmp_path) @@ -893,9 +899,7 @@ def test_stamp_clear( ) -> None: # we need to run these tests in this folder because it uses the git history - with tempfile.TemporaryDirectory( - dir=SELF_DIR, ignore_cleanup_errors=True - ) as tmpdir: + with TemporaryDirectoryIgnoreErrors(dir=SELF_DIR) as tmpdir: tmp_path = pathlib.Path(tmpdir) monkeypatch.chdir(tmp_path) From 932c525d56fc0b42d2fa600e3805d17491279703 Mon Sep 17 00:00:00 2001 From: Trevor Manz Date: Tue, 3 Dec 2024 20:07:59 -0500 Subject: [PATCH 5/5] Ruff fixes --- tests/test_juv.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_juv.py b/tests/test_juv.py index 875b42d..27a47b6 100644 --- a/tests/test_juv.py +++ b/tests/test_juv.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import os import pathlib import re @@ -22,13 +23,11 @@ # Custom TemporaryDirectory for Python < 3.10 -# TODO: Use `ignore_cleanup_errors=True` in Python 3.10+ +# TODO: Use `ignore_cleanup_errors=True` in Python 3.10+ # noqa: TD002, TD003 class TemporaryDirectoryIgnoreErrors(tempfile.TemporaryDirectory): - def cleanup(self): - try: + def cleanup(self) -> None: + with contextlib.suppress(Exception): super().cleanup() - except Exception: - pass # Ignore cleanup errors def invoke(args: list[str], uv_python: str = "3.13") -> Result: