From 9b4fcc465cc656498331a3b90472463b275cb909 Mon Sep 17 00:00:00 2001 From: Rich Chiodo false Date: Fri, 1 May 2026 11:05:42 -0700 Subject: [PATCH 1/4] Create test for issue --- .../pydevd/tests_python/test_pydev_monkey.py | 171 +++++++++--------- tests/test_data/issue_1905/README.md | 45 +++++ .../issue_1905/repro_joblib_parallel.py | 15 ++ .../issue_1905/repro_loky_manager.py | 16 ++ 4 files changed, 162 insertions(+), 85 deletions(-) create mode 100644 tests/test_data/issue_1905/README.md create mode 100644 tests/test_data/issue_1905/repro_joblib_parallel.py create mode 100644 tests/test_data/issue_1905/repro_loky_manager.py diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_pydev_monkey.py b/src/debugpy/_vendored/pydevd/tests_python/test_pydev_monkey.py index 2938c249a..eb2aafaee 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_pydev_monkey.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_pydev_monkey.py @@ -1,6 +1,7 @@ # coding: utf-8 import os import sys +from typing import Any, Generator import pytest @@ -16,16 +17,16 @@ @pytest.fixture(autouse=True) -def save_setup_holder(): - original = SetupHolder.setup +def save_setup_holder() -> Generator[None, Any, None]: + original: None = SetupHolder.setup yield SetupHolder.setup = original -def test_monkey(): +def test_monkey() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True} check = """C:\\bin\\python.exe -u -c connect(\\"127.0.0.1\\")""" - debug_command = ( + debug_command: str = ( "import sys; " "sys.path.insert(0, r'%s'); " "import pydevd; pydevd.config('quoted-line', ''); " @@ -35,23 +36,23 @@ def test_monkey(): 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command assert "C:\\bin\\python.exe -u -c %s" % debug_command == pydev_monkey.patch_arg_str_win(check) -def test_str_to_args_windows(): +def test_str_to_args_windows() -> None: assert ["a", "b"] == pydev_monkey.str_to_args_windows('a "b"') -def test_monkey_patch_return_original_args(): - check = ["echo", '"my"', '"args"'] +def test_monkey_patch_return_original_args() -> None: + check: list[str] = ["echo", '"my"', '"args"'] res = pydev_monkey.patch_args(check[:]) assert res == check -def test_monkey_patch_pathlib_args(): +def test_monkey_patch_pathlib_args() -> None: try: import pathlib except ImportError: @@ -62,19 +63,19 @@ def test_monkey_patch_pathlib_args(): assert res == check -def test_monkey_patch_wrong_object_type(): +def test_monkey_patch_wrong_object_type() -> None: check = [1, 22, '"my"', '"args"'] res = pydev_monkey.patch_args(check[:]) assert res == check -def test_monkey_patch_wrong_object_type_2(): +def test_monkey_patch_wrong_object_type_2() -> None: check = ["C:\\bin\\python.exe", "-u", 1, '-qcconnect("127.0.0.1")'] res = pydev_monkey.patch_args(check[:]) assert res == check -def test_monkey_patch_args_module_subprocess_pathlib(): +def test_monkey_patch_args_module_subprocess_pathlib() -> None: try: import pathlib except ImportError: @@ -106,23 +107,23 @@ def test_monkey_patch_args_module_subprocess_pathlib(): ] -def test_monkey_patch_args_indc(): +def test_monkey_patch_args_indc() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-u", "-c", 'connect("127.0.0.1")'] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-u", "-c", 'connect("127.0.0.1")'] + debug_command: str = ( "import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-u", "-c", debug_command] -def test_separate_future_imports(): +def test_separate_future_imports() -> None: found = pydev_monkey._separate_future_imports("""from __future__ import print_function\nprint(1)""") assert found == ("from __future__ import print_function;", "\nprint(1)") @@ -141,153 +142,153 @@ def test_separate_future_imports(): assert found == ("from __future__ import division\nfrom __future__ import (\n\nprint_function, absolute_import\n);", "print(1)") -def test_monkey_patch_args_indc_future_import(): +def test_monkey_patch_args_indc_future_import() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-u", "-c", 'from __future__ import print_function;connect("127.0.0.1")'] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-u", "-c", 'from __future__ import print_function;connect("127.0.0.1")'] + debug_command: str = ( "from __future__ import print_function;import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-u", "-c", debug_command] -def test_monkey_patch_args_indc_future_import2(): +def test_monkey_patch_args_indc_future_import2() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-u", "-c", 'from __future__ import print_function\nconnect("127.0.0.1")'] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-u", "-c", 'from __future__ import print_function\nconnect("127.0.0.1")'] + debug_command: str = ( "from __future__ import print_function;import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" '\nconnect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-u", "-c", debug_command] -def test_monkey_patch_args_indc2(): +def test_monkey_patch_args_indc2() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-u", '-qcconnect("127.0.0.1")'] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-u", '-qcconnect("127.0.0.1")'] + debug_command: str = ( "import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-u", "-qc", debug_command] -def test_monkey_patch_args_x_flag(): +def test_monkey_patch_args_x_flag() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-X", "faulthandler", "-c", 'connect("127.0.0.1")'] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-X", "faulthandler", "-c", 'connect("127.0.0.1")'] + debug_command: str = ( "import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-X", "faulthandler", "-c", debug_command] -def test_monkey_patch_args_flag_in_single_arg_1(): +def test_monkey_patch_args_flag_in_single_arg_1() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-qX", "faulthandler", "-c", 'connect("127.0.0.1")'] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-qX", "faulthandler", "-c", 'connect("127.0.0.1")'] + debug_command: str = ( "import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-qX", "faulthandler", "-c", debug_command] -def test_monkey_patch_args_flag_in_single_arg_2(): +def test_monkey_patch_args_flag_in_single_arg_2() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-qX", "faulthandler", "-c", 'connect("127.0.0.1")'] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-qX", "faulthandler", "-c", 'connect("127.0.0.1")'] + debug_command: str = ( "import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-qX", "faulthandler", "-c", debug_command] -def test_monkey_patch_args_flag_in_single_arg_3(): +def test_monkey_patch_args_flag_in_single_arg_3() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-qc", 'connect("127.0.0.1")'] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-qc", 'connect("127.0.0.1")'] + debug_command: str = ( "import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-qc", debug_command] -def test_monkey_patch_args_x_flag_inline(): +def test_monkey_patch_args_x_flag_inline() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-Xfaulthandler", "-c", 'connect("127.0.0.1")', "arg1"] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-Xfaulthandler", "-c", 'connect("127.0.0.1")', "arg1"] + debug_command: str = ( "import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-Xfaulthandler", "-c", debug_command, "arg1"] -def test_monkey_patch_args_c_flag_inline(): +def test_monkey_patch_args_c_flag_inline() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-X", "faulthandler", '-cconnect("127.0.0.1")', "arg1"] - debug_command = ( + check: list[str] = ["C:\\bin\\python.exe", "-X", "faulthandler", '-cconnect("127.0.0.1")', "arg1"] + debug_command: str = ( "import sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config('quoted-line', ''); " "pydevd.settrace(host='127.0.0.1', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=None, client_access_token=None, __setup_holder__=%s); " "" 'connect("127.0.0.1")' ) % (pydev_src_dir, sorted_dict_repr(SetupHolder.setup)) if sys.platform == "win32": - debug_command = debug_command.replace('"', '\\"') - debug_command = '"%s"' % debug_command + debug_command: str = debug_command.replace('"', '\\"') + debug_command: str = '"%s"' % debug_command res = pydev_monkey.patch_args(check) assert res == ["C:\\bin\\python.exe", "-X", "faulthandler", "-c", debug_command, "arg1"] -def test_monkey_patch_args_module(): +def test_monkey_patch_args_module() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "multiprocess": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-m", "test"] + check: list[str] = ["C:\\bin\\python.exe", "-m", "test"] from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file assert pydev_monkey.patch_args(check) == [ @@ -308,9 +309,9 @@ def test_monkey_patch_args_module(): ] -def test_monkey_patch_args_unbuffered_module(): +def test_monkey_patch_args_unbuffered_module() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "multiprocess": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-u", "-m", "test"] + check: list[str] = ["C:\\bin\\python.exe", "-u", "-m", "test"] from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file assert pydev_monkey.patch_args(check) == [ @@ -332,9 +333,9 @@ def test_monkey_patch_args_unbuffered_module(): ] -def test_monkey_patch_args_module_inline(): +def test_monkey_patch_args_module_inline() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "multiprocess": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-qOmtest"] + check: list[str] = ["C:\\bin\\python.exe", "-qOmtest"] from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file assert pydev_monkey.patch_args(check) == [ @@ -356,9 +357,9 @@ def test_monkey_patch_args_module_inline(): ] -def test_monkey_patch_args_module_inline2(): +def test_monkey_patch_args_module_inline2() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "multiprocess": True, "skip-notify-stdin": True} - check = ["C:\\bin\\python.exe", "-qOm", "test"] + check: list[str] = ["C:\\bin\\python.exe", "-qOm", "test"] from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file assert pydev_monkey.patch_args(check) == [ @@ -380,9 +381,9 @@ def test_monkey_patch_args_module_inline2(): ] -def test_monkey_patch_args_no_indc(): +def test_monkey_patch_args_no_indc() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0"} - check = ["C:\\bin\\python.exe", 'connect(\\"127.0.0.1\\")', "with spaces"] + check: list[str] = ["C:\\bin\\python.exe", 'connect(\\"127.0.0.1\\")', "with spaces"] from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file assert pydev_monkey.patch_args(check) == [ @@ -401,18 +402,18 @@ def test_monkey_patch_args_no_indc(): ] -def test_monkey_patch_args_no_indc_with_pydevd(): +def test_monkey_patch_args_no_indc_with_pydevd() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0"} - check = ["C:\\bin\\python.exe", "pydevd.py", 'connect(\\"127.0.0.1\\")', "bar"] + check: list[str] = ["C:\\bin\\python.exe", "pydevd.py", 'connect(\\"127.0.0.1\\")', "bar"] assert pydev_monkey.patch_args(check) == ["C:\\bin\\python.exe", "pydevd.py", 'connect(\\"127.0.0.1\\")', "bar"] -def test_monkey_patch_args_no_indc_without_pydevd(): +def test_monkey_patch_args_no_indc_without_pydevd() -> None: from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file SetupHolder.setup = {"client": "127.0.0.1", "port": "0"} - check = ["C:\\bin\\python.exe", "target.py", 'connect(\\"127.0.0.1\\")', "bar"] + check: list[str] = ["C:\\bin\\python.exe", "target.py", 'connect(\\"127.0.0.1\\")', "bar"] assert pydev_monkey.patch_args(check) == [ "C:\\bin\\python.exe", get_pydevd_file(), @@ -431,15 +432,15 @@ def test_monkey_patch_args_no_indc_without_pydevd(): @pytest.mark.parametrize("use_bytes", [True, False]) -def test_monkey_patch_c_program_arg(use_bytes): +def test_monkey_patch_c_program_arg(use_bytes) -> None: from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "module": "ignore.this"} - check = ["C:\\bin\\python.exe", "-u", "target.py", "-c", "-áéíóú"] + check: list[str] = ["C:\\bin\\python.exe", "-u", "target.py", "-c", "-áéíóú"] encode = lambda s: s if use_bytes: - check = [c.encode("utf-8") for c in check] + check: list[bytes] = [c.encode("utf-8") for c in check] encode = lambda s: s.encode("utf-8") assert pydev_monkey.patch_args(check) == [ @@ -460,9 +461,9 @@ def test_monkey_patch_c_program_arg(use_bytes): ] -def test_monkey_patch_args_module_single_arg(): +def test_monkey_patch_args_module_single_arg() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "multiprocess": True, "module": "ignore.this"} - check = ["C:\\bin\\python.exe", "-mtest", "bar"] + check: list[str] = ["C:\\bin\\python.exe", "-mtest", "bar"] from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file assert pydev_monkey.patch_args(check) == [ @@ -483,8 +484,8 @@ def test_monkey_patch_args_module_single_arg(): ] -def test_monkey_patch_args_stdin(): +def test_monkey_patch_args_stdin() -> None: SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "multiprocess": True, "module": "ignore.this"} - check = ["C:\\bin\\python.exe", "-Xfaulthandler", "-"] + check: list[str] = ["C:\\bin\\python.exe", "-Xfaulthandler", "-"] # i.e.: we don't deal with the stdin. assert pydev_monkey.patch_args(check) == check diff --git a/tests/test_data/issue_1905/README.md b/tests/test_data/issue_1905/README.md new file mode 100644 index 000000000..3f04afcd3 --- /dev/null +++ b/tests/test_data/issue_1905/README.md @@ -0,0 +1,45 @@ +# Issue #1905 full runtime repro + +This folder contains minimal runtime repro scripts for: +- direct loky manager startup +- joblib Parallel worker startup + +## Scripts + +- `repro_loky_manager.py` +- `repro_joblib_parallel.py` + +## Prerequisites + +- `joblib` installed in the Python environment used to run debugpy. +- Run from repo root with `PYTHONPATH=src` so `python -m debugpy` uses this checkout. + +## Matrix commands (PowerShell) + +```powershell +$py = "c:/Users/rchiodo/source/repos/debugpy/.venv/Scripts/python.exe" +$env:PYTHONPATH = "C:/Users/rchiodo/source/repos/debugpy/src" + +& $py tests/test_data/issue_1905/repro_loky_manager.py +& $py -m debugpy --listen 127.0.0.1:0 tests/test_data/issue_1905/repro_loky_manager.py +& $py -m debugpy --listen 127.0.0.1:0 --configure-subProcess false tests/test_data/issue_1905/repro_loky_manager.py +& $py -m debugpy --listen 127.0.0.1:0 --configure-subProcess true tests/test_data/issue_1905/repro_loky_manager.py + +& $py tests/test_data/issue_1905/repro_joblib_parallel.py +& $py -m debugpy --listen 127.0.0.1:0 tests/test_data/issue_1905/repro_joblib_parallel.py +& $py -m debugpy --listen 127.0.0.1:0 --configure-subProcess false tests/test_data/issue_1905/repro_joblib_parallel.py +& $py -m debugpy --listen 127.0.0.1:0 --configure-subProcess true tests/test_data/issue_1905/repro_joblib_parallel.py + +Remove-Item Env:PYTHONPATH +``` + +## Optional log capture + +Set `DEBUGPY_LOG_DIR` before each invocation to collect subprocess patching logs. +Search logs for patching lines and traceback evidence. + +## Notes + +- On Windows/Python 3.13 in this workspace, the scripts pass in all modes and logs show string argv values for `-c` patching. +- The issue discussion indicates failures on Linux/macOS where some patched argv values may be bytes. +- On affected platforms, compare behavior between default subprocess handling and `--configure-subProcess false`. diff --git a/tests/test_data/issue_1905/repro_joblib_parallel.py b/tests/test_data/issue_1905/repro_joblib_parallel.py new file mode 100644 index 000000000..2222b58fe --- /dev/null +++ b/tests/test_data/issue_1905/repro_joblib_parallel.py @@ -0,0 +1,15 @@ +from joblib import Parallel, delayed + + +def work() -> int: + return 42 + + +def main() -> int: + result = Parallel(n_jobs=4)(delayed(work)() for _ in range(8)) + print(result) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_data/issue_1905/repro_loky_manager.py b/tests/test_data/issue_1905/repro_loky_manager.py new file mode 100644 index 000000000..0df0a3397 --- /dev/null +++ b/tests/test_data/issue_1905/repro_loky_manager.py @@ -0,0 +1,16 @@ +from joblib.externals.loky.backend import get_context + + +def main() -> int: + manager = get_context().Manager() + try: + values = manager.list() + values.append("ok") + print(list(values)) + return 0 + finally: + manager.shutdown() + + +if __name__ == "__main__": + raise SystemExit(main()) From 117fde1048d2814ab88bac3dcebe7b387a3f1560 Mon Sep 17 00:00:00 2001 From: rchiodo Date: Fri, 1 May 2026 20:08:06 +0000 Subject: [PATCH 2/4] Trying out a fix --- .../pydevd/_pydev_bundle/pydev_monkey.py | 186 ++++++++++-------- .../pydevd/tests_python/test_pydev_monkey.py | 25 ++- 2 files changed, 124 insertions(+), 87 deletions(-) diff --git a/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py b/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py index 6288a5348..eae4e51ff 100644 --- a/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py +++ b/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py @@ -1,7 +1,9 @@ # License: EPL +from _thread import _local import os import re import sys +from typing import Generator from _pydev_bundle._pydev_saved_modules import threading from _pydevd_bundle.pydevd_constants import ( get_global_debugger, @@ -25,13 +27,13 @@ # Things that are dependent on having the pydevd debugger # =============================================================================== -pydev_src_dir = os.path.dirname(os.path.dirname(__file__)) +pydev_src_dir: str = os.path.dirname(os.path.dirname(__file__)) -_arg_patch = threading.local() +_arg_patch: _local = threading.local() @contextmanager -def skip_subprocess_arg_patch(): +def skip_subprocess_arg_patch() -> Generator[None, Any, None]: _arg_patch.apply_arg_patching = False try: yield @@ -39,7 +41,7 @@ def skip_subprocess_arg_patch(): _arg_patch.apply_arg_patching = True -def _get_apply_arg_patching(): +def _get_apply_arg_patching() -> os.Any | bool: return getattr(_arg_patch, "apply_arg_patching", True) @@ -58,7 +60,7 @@ def _get_setup_updated_with_protocol_and_ppid(setup, is_exec=False): # If it's an exec, keep it what it was. setup[pydevd_constants.ARGUMENT_PPID] = os.getpid() - protocol = pydevd_constants.get_protocol() + protocol: str = pydevd_constants.get_protocol() if protocol == pydevd_constants.HTTP_JSON_PROTOCOL: setup[pydevd_constants.ARGUMENT_HTTP_JSON_PROTOCOL] = True @@ -74,11 +76,11 @@ def _get_setup_updated_with_protocol_and_ppid(setup, is_exec=False): else: pydev_log.debug("Unexpected protocol: %s", protocol) - mode = pydevd_defaults.PydevdCustomization.DEBUG_MODE + mode: str = pydevd_defaults.PydevdCustomization.DEBUG_MODE if mode: setup["debug-mode"] = mode - preimport = pydevd_defaults.PydevdCustomization.PREIMPORT + preimport: str = pydevd_defaults.PydevdCustomization.PREIMPORT if preimport: setup["preimport"] = preimport @@ -92,12 +94,12 @@ def _get_setup_updated_with_protocol_and_ppid(setup, is_exec=False): class _LastFutureImportFinder(ast.NodeVisitor): - def __init__(self): + def __init__(self) -> None: self.last_future_import_found = None - def visit_ImportFrom(self, node): + def visit_ImportFrom(self, node) -> None: if node.module == "__future__": - self.last_future_import_found = node + self.last_future_import_found: ast.ImportFrom = node def _get_offset_from_line_col(code, line, col): @@ -133,7 +135,7 @@ def _separate_future_imports(code): if visitor.last_future_import_found is None: return "", code - node = visitor.last_future_import_found + node: ast.ImportFrom = visitor.last_future_import_found offset = -1 if hasattr(node, "end_lineno") and hasattr(node, "end_col_offset"): # Python 3.8 onwards has these (so, use when possible). @@ -144,9 +146,9 @@ def _separate_future_imports(code): # end line/col not available, let's just find the offset and then search # for the alias from there. line, col = node.lineno, node.col_offset - offset = _get_offset_from_line_col(code, line - 1, col) # ast lines are 1-based, make it 0-based. + offset: int = _get_offset_from_line_col(code, line - 1, col) # ast lines are 1-based, make it 0-based. if offset >= 0 and node.names: - from_future_import_name = node.names[-1].name + from_future_import_name: str = node.names[-1].name i = code.find(from_future_import_name, offset) if i < 0: offset = -1 @@ -154,7 +156,7 @@ def _separate_future_imports(code): offset = i + len(from_future_import_name) if offset >= 0: - for i in range(offset, len(code)): + for i: int in range(offset, len(code)): if code[i] in (" ", "\t", ";", ")", "\n"): offset += 1 else: @@ -183,19 +185,25 @@ def _separate_future_imports(code): return "", code -def _get_python_c_args(host, port, code, args, setup): +def _get_python_c_args(host, port, code, args, setup) -> bytes | str: setup = _get_setup_updated_with_protocol_and_ppid(setup) # i.e.: We want to make the repr sorted so that it works in tests. setup_repr = setup if setup is None else (sorted_dict_repr(setup)) - future_imports = "" + # Normalize code to str for processing if it's bytes (can happen on Linux/WSL + # with loky/joblib subprocesses). We'll convert back to the original type at the end. + code_is_bytes: bool = isinstance(code, bytes) + if code_is_bytes: + code: str = code.decode("utf-8") + + future_imports: str = "" if "__future__" in code: # If the code has a __future__ import, we need to be able to strip the __future__ # imports from the code and add them to the start of our code snippet. future_imports, code = _separate_future_imports(code) - return ( + result: str = ( "%simport sys; sys.path.insert(0, r'%s'); import pydevd; pydevd.config(%r, %r); " "pydevd.settrace(host=%r, port=%s, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True, access_token=%r, client_access_token=%r, __setup_holder__=%s); " "%s" @@ -212,6 +220,12 @@ def _get_python_c_args(host, port, code, args, setup): code, ) + # Convert back to the original type if it was bytes + if code_is_bytes: + result: bytes = result.encode("utf-8") + + return result + def _get_host_port(): import pydevd @@ -220,14 +234,14 @@ def _get_host_port(): return host, port -def _is_managed_arg(arg): +def _is_managed_arg(arg) -> bool: pydevd_py = _get_str_type_compatible(arg, "pydevd.py") if arg.endswith(pydevd_py): return True return False -def _on_forked_process(setup_tracing=True): +def _on_forked_process(setup_tracing=True) -> None: pydevd_constants.after_fork() pydev_log.initialize_debug_stream(reinitialize=True) @@ -240,7 +254,7 @@ def _on_forked_process(setup_tracing=True): pydevd.settrace_forked(setup_tracing=setup_tracing) -def _on_set_trace_for_new_thread(global_debugger): +def _on_set_trace_for_new_thread(global_debugger) -> None: if global_debugger is not None: if not PYDEVD_USE_SYS_MONITORING: global_debugger.enable_tracing() @@ -274,7 +288,7 @@ def _get_str_type_compatible(s, args): # =============================================================================== # Things related to monkey-patching # =============================================================================== -def is_python(path): +def is_python(path) -> bool: single_quote, double_quote = _get_str_type_compatible(path, ["'", '"']) if path.endswith(single_quote) or path.endswith(double_quote): @@ -314,7 +328,7 @@ def remove_quotes_from_args(args): new_args = [] for x in args: if isinstance(x, Path): - x = x.as_posix() + x: str = x.as_posix() else: if not isinstance(x, (bytes, str)): raise InvalidTypeInArgsException(str(type(x))) @@ -377,7 +391,7 @@ def patch_args(args, is_exec=False): original_args = args try: unquoted_args = remove_quotes_from_args(args) - except InvalidTypeInArgsException as e: + except InvalidTypeInArgsException as e: InvalidTypeInArgsException: pydev_log.info("Unable to monkey-patch subprocess arguments because a type found in the args is invalid: %s", e) return original_args @@ -422,17 +436,17 @@ def patch_args(args, is_exec=False): # # python -O -Q old -v -c "import sys;print(sys)" - params_with_combinable_arg = set(("W", "X", "Q", "c", "m")) + params_with_combinable_arg: set[Literal['W', 'X', 'Q', 'c', 'm']] = set(("W", "X", "Q", "c", "m")) module_name = None - before_module_flag = "" + before_module_flag: str = "" module_name_i_start = -1 module_name_i_end = -1 code = None code_i = -1 code_i_end = -1 - code_flag = "" + code_flag: str = "" filename = None filename_i = -1 @@ -471,15 +485,15 @@ def patch_args(args, is_exec=False): # python -qmtest before_module_flag = arg_as_str[:j] # before_module_flag would then be "-q" if before_module_flag == "-": - before_module_flag = "" - module_name_i_start = i + before_module_flag: str = "" + module_name_i_start: int = i if not remainder: module_name = unquoted_args[i + 1] - module_name_i_end = i + 1 + module_name_i_end: int = i + 1 else: # i.e.: python -qmtest should provide 'test' as the module_name module_name = unquoted_args[i][j + 1 :] - module_name_i_end = module_name_i_start + module_name_i_end: int = module_name_i_start break_out = True break @@ -493,12 +507,12 @@ def patch_args(args, is_exec=False): if not remainder: # arg_as_str is something as "-qc", "import sys" code = unquoted_args[i + 1] - code_i_end = i + 2 + code_i_end: int = i + 2 else: # if arg_as_str is something as "-qcimport sys" code = remainder # code would be "import sys" - code_i_end = i + 1 - code_i = i + code_i_end: int = i + 1 + code_i: int = i break_out = True break @@ -519,7 +533,7 @@ def patch_args(args, is_exec=False): # So, we should support whatever runpy.run_path # supports in this case. - filename_i = i + filename_i: int = i if _is_managed_arg(filename): # no need to add pydevd twice pydev_log.debug("Skipped monkey-patching as pydevd.py is in args already.") @@ -543,7 +557,7 @@ def patch_args(args, is_exec=False): return quote_args(new_args) - first_non_vm_index = max(filename_i, module_name_i_start) + first_non_vm_index: int = max(filename_i, module_name_i_start) if first_non_vm_index == -1: pydev_log.debug("Unable to fix arguments to attach debugger on subprocess (could not resolve filename nor module name).") return original_args @@ -558,7 +572,7 @@ def patch_args(args, is_exec=False): if before_module_flag: new_args.append(before_module_flag) - add_module_at = len(new_args) + 1 + add_module_at: int = len(new_args) + 1 new_args.extend( setup_to_argv( @@ -601,12 +615,12 @@ def str_to_args_windows(args): ARG = 1 IN_DOUBLE_QUOTE = 2 - state = DEFAULT + state: int = DEFAULT backslashes = 0 - buf = "" + buf: str = "" - args_len = len(args) - for i in range(args_len): + args_len: int = len(args) + for i: int in range(args_len): ch = args[i] if ch == "\\": backslashes += 1 @@ -618,7 +632,7 @@ def str_to_args_windows(args): buf += "\\" if backslashes == 1: if state == DEFAULT: - state = ARG + state: int = ARG buf += '"' backslashes = 0 @@ -627,7 +641,7 @@ def str_to_args_windows(args): else: # false alarm, treat passed backslashes literally... if state == DEFAULT: - state = ARG + state: int = ARG while backslashes > 0: backslashes -= 1 @@ -638,16 +652,16 @@ def str_to_args_windows(args): # skip continue elif state == ARG: - state = DEFAULT + state: int = DEFAULT result.append(buf) - buf = "" + buf: str = "" continue if state in (DEFAULT, ARG): if ch == '"': - state = IN_DOUBLE_QUOTE + state: int = IN_DOUBLE_QUOTE else: - state = ARG + state: int = ARG buf += ch elif state == IN_DOUBLE_QUOTE: @@ -659,7 +673,7 @@ def str_to_args_windows(args): buf += '"' i += 1 else: - state = ARG + state: int = ARG else: buf += ch @@ -677,12 +691,12 @@ def patch_arg_str_win(arg_str): # Fix https://youtrack.jetbrains.com/issue/PY-9767 (args may be empty) if not args or not is_python(args[0]): return arg_str - arg_str = " ".join(patch_args(args)) + arg_str: str = " ".join(patch_args(args)) pydev_log.debug("New args: %s", arg_str) return arg_str -def monkey_patch_module(module, funcname, create_func): +def monkey_patch_module(module, funcname, create_func) -> None: if hasattr(module, funcname): original_name = "original_" + funcname if not hasattr(module, original_name): @@ -690,11 +704,11 @@ def monkey_patch_module(module, funcname, create_func): setattr(module, funcname, create_func(original_name)) -def monkey_patch_os(funcname, create_func): +def monkey_patch_os(funcname, create_func) -> None: monkey_patch_module(os, funcname, create_func) -def warn_multiproc(): +def warn_multiproc() -> None: pass # TODO: Provide logging as messages to the IDE. # pydev_log.error_once( # "pydev debugger: New process is launching (breakpoints won't work in the new process).\n" @@ -847,7 +861,7 @@ def create_warn_fork_exec(original_name): _posixsubprocess.fork_exec(args, executable_list, close_fds, ... (13 more)) """ - def new_warn_fork_exec(*args): + def new_warn_fork_exec(*args) -> os.Any | None: try: import _posixsubprocess @@ -881,7 +895,7 @@ def create_subprocess_warn_fork_exec(original_name): subprocess._fork_exec(args, executable_list, close_fds, ... (13 more)) """ - def new_warn_fork_exec(*args): + def new_warn_fork_exec(*args) -> os.Any | None: try: import subprocess @@ -929,13 +943,13 @@ def new_CreateProcess(*args): return new_CreateProcess -def create_fork(original_name): +def create_fork(original_name) -> Callable[[], Any]: def new_fork(): # A simple fork will result in a new python process is_new_python_process = True - frame = sys._getframe() + frame: sys.FrameType = sys._getframe() - apply_arg_patch = _get_apply_arg_patching() + apply_arg_patch: os.Any | bool = _get_apply_arg_patching() is_subprocess_fork = False while frame is not None: @@ -944,18 +958,18 @@ def new_fork(): # If we're actually in subprocess.Popen creating a child, it may # result in something which is not a Python process, (so, we # don't want to connect with it in the forked version). - executable = frame.f_locals.get("executable") + executable: os.Any | None = frame.f_locals.get("executable") if executable is not None: is_new_python_process = False if is_python(executable): is_new_python_process = True break - frame = frame.f_back + frame: sys.FrameType | None = frame.f_back frame = None # Just make sure we don't hold on to it. - protocol = pydevd_constants.get_protocol() - debug_mode = PydevdCustomization.DEBUG_MODE + protocol: str = pydevd_constants.get_protocol() + debug_mode: str = PydevdCustomization.DEBUG_MODE child_process = getattr(os, original_name)() # fork if not child_process: @@ -973,19 +987,19 @@ def new_fork(): return new_fork -def send_process_created_message(): - py_db = get_global_debugger() +def send_process_created_message() -> None: + py_db: None = get_global_debugger() if py_db is not None: py_db.send_process_created_message() -def send_process_about_to_be_replaced(): - py_db = get_global_debugger() +def send_process_about_to_be_replaced() -> None: + py_db: None = get_global_debugger() if py_db is not None: py_db.send_process_about_to_be_replaced() -def patch_new_process_functions(): +def patch_new_process_functions() -> None: # os.execl(path, arg0, arg1, ...) # os.execle(path, arg0, arg1, ..., env) # os.execlp(file, arg0, arg1, ...) @@ -1050,7 +1064,7 @@ def patch_new_process_functions(): monkey_patch_module(_subprocess, "CreateProcess", create_CreateProcess) -def patch_new_process_functions_with_warning(): +def patch_new_process_functions_with_warning() -> None: monkey_patch_os("execl", create_warn_multiproc) monkey_patch_os("execle", create_warn_multiproc) monkey_patch_os("execlp", create_warn_multiproc) @@ -1096,15 +1110,15 @@ def patch_new_process_functions_with_warning(): class _NewThreadStartupWithTrace: - def __init__(self, original_func, args, kwargs): - self.original_func = original_func - self.args = args - self.kwargs = kwargs + def __init__(self, original_func, args, kwargs) -> None: + self.original_func: Any = original_func + self.args: Any = args + self.kwargs: Any = kwargs def __call__(self): # We monkey-patch the thread creation so that this function is called in the new thread. At this point # we notify of its creation and start tracing it. - py_db = get_global_debugger() + py_db: None = get_global_debugger() thread_id = None if py_db is not None: @@ -1112,11 +1126,11 @@ def __call__(self): # the start_new_thread internal machinery and thread._bootstrap has not finished), so, the code below needs # to make sure that we use the current thread bound to the original function and not use # threading.current_thread() unless we're sure it's a dummy thread. - t = getattr(self.original_func, "__self__", getattr(self.original_func, "im_self", None)) + t: os.Any | None = getattr(self.original_func, "__self__", getattr(self.original_func, "im_self", None)) if not isinstance(t, threading.Thread): # This is not a threading.Thread but a Dummy thread (so, get it as a dummy thread using # currentThread). - t = threading.current_thread() + t: threading.Thread = threading.current_thread() if not getattr(t, "is_pydev_daemon_thread", False): thread_id = get_current_thread_id(t) @@ -1144,10 +1158,10 @@ def __call__(self): class _NewThreadStartupWithoutTrace: - def __init__(self, original_func, args, kwargs): - self.original_func = original_func - self.args = args - self.kwargs = kwargs + def __init__(self, original_func, args, kwargs) -> None: + self.original_func: Any = original_func + self.args: Any = args + self.kwargs: Any = kwargs def __call__(self): return self.original_func(*self.args, **self.kwargs) @@ -1172,15 +1186,15 @@ def _get_threading_modules_to_patch(): threading_modules_to_patch = _get_threading_modules_to_patch() -def patch_thread_module(thread_module): +def patch_thread_module(thread_module) -> None: # Note: this is needed not just for the tracing, but to have an early way to # notify that a thread was created (i.e.: tests_python.test_debugger_json.test_case_started_exited_threads_protocol) - start_thread_attrs = ["_start_new_thread", "start_new_thread", "start_new"] - start_joinable_attrs = ["start_joinable_thread", "_start_joinable_thread"] - check = start_thread_attrs + start_joinable_attrs + start_thread_attrs: list[str] = ["_start_new_thread", "start_new_thread", "start_new"] + start_joinable_attrs: list[str] = ["start_joinable_thread", "_start_joinable_thread"] + check: list[str] = start_thread_attrs + start_joinable_attrs replace_attrs = [] - for attr in check: + for attr: str in check: if hasattr(thread_module, attr): replace_attrs.append(attr) @@ -1240,12 +1254,12 @@ def pydev_start_joinable_thread(self, function, *args, **kwargs): setattr(thread_module, attr, pydev_start_new_thread) -def patch_thread_modules(): +def patch_thread_modules() -> None: for t in threading_modules_to_patch: patch_thread_module(t) -def undo_patch_thread_modules(): +def undo_patch_thread_modules() -> None: for t in threading_modules_to_patch: try: t.start_new_thread = t._original_start_new_thread @@ -1273,7 +1287,7 @@ def undo_patch_thread_modules(): pass -def disable_trace_thread_modules(): +def disable_trace_thread_modules() -> None: """ Can be used to temporarily stop tracing threads created with thread.start_new_thread. """ @@ -1281,7 +1295,7 @@ def disable_trace_thread_modules(): _UseNewThreadStartup = _NewThreadStartupWithoutTrace -def enable_trace_thread_modules(): +def enable_trace_thread_modules() -> None: """ Can be used to start tracing threads created with thread.start_new_thread again. """ diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_pydev_monkey.py b/src/debugpy/_vendored/pydevd/tests_python/test_pydev_monkey.py index eb2aafaee..c2f0630f5 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_pydev_monkey.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_pydev_monkey.py @@ -440,7 +440,7 @@ def test_monkey_patch_c_program_arg(use_bytes) -> None: encode = lambda s: s if use_bytes: - check: list[bytes] = [c.encode("utf-8") for c in check] + check: list[bytes] = [c.encode("utf-8") for c: bytes in check] encode = lambda s: s.encode("utf-8") assert pydev_monkey.patch_args(check) == [ @@ -489,3 +489,26 @@ def test_monkey_patch_args_stdin() -> None: check: list[str] = ["C:\\bin\\python.exe", "-Xfaulthandler", "-"] # i.e.: we don't deal with the stdin. assert pydev_monkey.patch_args(check) == check + + +def test_monkey_patch_args_c_with_bytes() -> None: + # Regression test for issue #1905: on Linux/WSL with loky/joblib, subprocess args + # can be bytes, causing TypeError when patching -c arguments. This test ensures + # the patch_args function handles bytes argv correctly. + SetupHolder.setup = {"client": "127.0.0.1", "port": "0", "ppid": os.getpid(), "protocol-quoted-line": True, "skip-notify-stdin": True} + check: list[bytes] = [ + b"C:\\bin\\python.exe", + b"-u", + b"-c", + b'from joblib.externals.loky.backend import get_context; get_context().Manager()', + ] + + # Should not raise TypeError about bytes/str mismatch. + result = pydev_monkey.patch_args(check) + + # Result should be a list with bytes elements (since input was bytes) + assert isinstance(result, list) + assert all(isinstance(item, (bytes, str)) for item in result) + # The result should contain our patched debugpy setup somewhere + result_str: str = b"".join(item.encode() if isinstance(item, str) else item for item in result).decode() + assert "pydevd.settrace" in result_str From aa137f9bb563bc87b78b32ae516f6d0dd34e06d5 Mon Sep 17 00:00:00 2001 From: rchiodo Date: Fri, 1 May 2026 20:42:42 +0000 Subject: [PATCH 3/4] Fix syntax errors --- src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py b/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py index eae4e51ff..0bc573db9 100644 --- a/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py +++ b/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py @@ -1,4 +1,6 @@ # License: EPL +from __future__ import annotations + from _thread import _local import os import re From c589bc0026149781581f18bcdca6bce7114c8990 Mon Sep 17 00:00:00 2001 From: rchiodo Date: Fri, 1 May 2026 20:47:36 +0000 Subject: [PATCH 4/4] Really fix syntax errors --- .../_vendored/pydevd/_pydev_bundle/pydev_monkey.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py b/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py index 0bc573db9..09fcadec3 100644 --- a/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py +++ b/src/debugpy/_vendored/pydevd/_pydev_bundle/pydev_monkey.py @@ -158,7 +158,7 @@ def _separate_future_imports(code): offset = i + len(from_future_import_name) if offset >= 0: - for i: int in range(offset, len(code)): + for i in range(offset, len(code)): if code[i] in (" ", "\t", ";", ")", "\n"): offset += 1 else: @@ -393,7 +393,7 @@ def patch_args(args, is_exec=False): original_args = args try: unquoted_args = remove_quotes_from_args(args) - except InvalidTypeInArgsException as e: InvalidTypeInArgsException: + except InvalidTypeInArgsException as e: pydev_log.info("Unable to monkey-patch subprocess arguments because a type found in the args is invalid: %s", e) return original_args @@ -622,7 +622,7 @@ def str_to_args_windows(args): buf: str = "" args_len: int = len(args) - for i: int in range(args_len): + for i in range(args_len): ch = args[i] if ch == "\\": backslashes += 1 @@ -1196,7 +1196,7 @@ def patch_thread_module(thread_module) -> None: check: list[str] = start_thread_attrs + start_joinable_attrs replace_attrs = [] - for attr: str in check: + for attr in check: if hasattr(thread_module, attr): replace_attrs.append(attr)