From 6100e767424c3f331513cd49cda987db77782a19 Mon Sep 17 00:00:00 2001 From: nightcityblade Date: Sun, 16 Aug 2026 11:26:32 +0800 Subject: [PATCH 1/4] Set TCP_NODELAY on pydevd client connections --- .../pydevd/_pydevd_bundle/pydevd_comm.py | 4 ++ .../pydevd/tests_python/test_pydevd_comm.py | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py index b935320c..22cd190b 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py @@ -525,6 +525,10 @@ def start_client(host, port): s.setsockopt(socket_module.IPPROTO_TCP, socket_module.TCP_KEEPCNT, 5) except (AttributeError, OSError): pass # May not be available everywhere. + try: + s.setsockopt(socket_module.IPPROTO_TCP, socket_module.TCP_NODELAY, 1) + except (AttributeError, OSError): + pass # May not be available everywhere. try: # 10 seconds default timeout diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py b/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py new file mode 100644 index 00000000..8d245201 --- /dev/null +++ b/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py @@ -0,0 +1,43 @@ +from unittest import mock + +from _pydevd_bundle import pydevd_comm + + +def start_client(monkeypatch, sock): + monkeypatch.setattr(pydevd_comm, "socket", lambda *_: sock) + monkeypatch.setattr(pydevd_comm.socket_module, "getaddrinfo", lambda *_: []) + return pydevd_comm.start_client("localhost", 5678) + + +def test_start_client_sets_tcp_nodelay(monkeypatch): + sock = mock.Mock() + tcp_nodelay = mock.sentinel.tcp_nodelay + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", tcp_nodelay) + + assert start_client(monkeypatch, sock) is sock + sock.setsockopt.assert_any_call( + pydevd_comm.socket_module.IPPROTO_TCP, + tcp_nodelay, + 1, + ) + + +def test_start_client_without_tcp_nodelay(monkeypatch): + sock = mock.Mock() + monkeypatch.delattr(pydevd_comm.socket_module, "TCP_NODELAY", raising=False) + + assert start_client(monkeypatch, sock) is sock + + +def test_start_client_ignores_tcp_nodelay_error(monkeypatch): + sock = mock.Mock() + tcp_nodelay = mock.sentinel.tcp_nodelay + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", tcp_nodelay) + + def set_option(_level, option, _value): + if option is tcp_nodelay: + raise OSError + + sock.setsockopt.side_effect = set_option + + assert start_client(monkeypatch, sock) is sock From 26ff987048c8a3d5e5096d3859bce10f977bd5b0 Mon Sep 17 00:00:00 2001 From: nightcityblade Date: Tue, 18 Aug 2026 11:12:38 +0800 Subject: [PATCH 2/4] Address TCP_NODELAY review feedback --- .../pydevd/_pydevd_bundle/pydevd_comm.py | 4 ++ .../pydevd/tests_python/test_pydevd_comm.py | 51 +++++++++---------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py index 22cd190b..b6e8e123 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py @@ -480,6 +480,10 @@ def start_server(port): pydev_log.info(msg) new_socket, _addr = s.accept() + try: + new_socket.setsockopt(socket_module.IPPROTO_TCP, socket_module.TCP_NODELAY, 1) + except (AttributeError, OSError): + pass # May not be available everywhere. pydev_log.info("Connection accepted") # closing server socket is not necessary but we don't need it s.close() diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py b/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py index 8d245201..d585e3a2 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py @@ -1,43 +1,42 @@ from unittest import mock +import pytest + from _pydevd_bundle import pydevd_comm def start_client(monkeypatch, sock): - monkeypatch.setattr(pydevd_comm, "socket", lambda *_: sock) - monkeypatch.setattr(pydevd_comm.socket_module, "getaddrinfo", lambda *_: []) - return pydevd_comm.start_client("localhost", 5678) - - -def test_start_client_sets_tcp_nodelay(monkeypatch): - sock = mock.Mock() - tcp_nodelay = mock.sentinel.tcp_nodelay - monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", tcp_nodelay) - - assert start_client(monkeypatch, sock) is sock - sock.setsockopt.assert_any_call( - pydevd_comm.socket_module.IPPROTO_TCP, - tcp_nodelay, - 1, + monkeypatch.setattr(pydevd_comm, "socket", mock.Mock(return_value=sock)) + monkeypatch.setattr( + pydevd_comm.socket_module, + "getaddrinfo", + lambda *_: [(pydevd_comm.AF_INET, pydevd_comm.SOCK_STREAM, 0, "", ("127.0.0.1", 5678))], ) + assert pydevd_comm.start_client("localhost", 5678) is sock + sock.connect.assert_called_once_with(("localhost", 5678)) -def test_start_client_without_tcp_nodelay(monkeypatch): +def test_start_client_sets_tcp_nodelay(monkeypatch): sock = mock.Mock() - monkeypatch.delattr(pydevd_comm.socket_module, "TCP_NODELAY", raising=False) + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay) - assert start_client(monkeypatch, sock) is sock + start_client(monkeypatch, sock) + sock.setsockopt.assert_any_call(pydevd_comm.socket_module.IPPROTO_TCP, mock.sentinel.tcp_nodelay, 1) -def test_start_client_ignores_tcp_nodelay_error(monkeypatch): +@pytest.mark.parametrize("error", [AttributeError, OSError]) +def test_start_client_ignores_tcp_nodelay_error(monkeypatch, error): sock = mock.Mock() - tcp_nodelay = mock.sentinel.tcp_nodelay - monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", tcp_nodelay) + sock.setsockopt.side_effect = error - def set_option(_level, option, _value): - if option is tcp_nodelay: - raise OSError + start_client(monkeypatch, sock) + sock.setsockopt.assert_any_call(pydevd_comm.socket_module.IPPROTO_TCP, pydevd_comm.socket_module.TCP_NODELAY, 1) - sock.setsockopt.side_effect = set_option - assert start_client(monkeypatch, sock) is sock +def test_start_server_sets_tcp_nodelay(monkeypatch): + server, accepted = mock.Mock(), mock.Mock() + address = ("127.0.0.1", 5678) + server.configure_mock(**{"accept.return_value": (accepted, address), "getsockname.return_value": address}) + monkeypatch.setattr(pydevd_comm, "create_server_socket", mock.Mock(return_value=server)) + assert pydevd_comm.start_server(0) is accepted + accepted.setsockopt.assert_called_once_with(pydevd_comm.socket_module.IPPROTO_TCP, pydevd_comm.socket_module.TCP_NODELAY, 1) From 6c3f75fa99def18bfe92a940182f721b6a652b58 Mon Sep 17 00:00:00 2001 From: nightcityblade Date: Wed, 19 Aug 2026 23:05:43 +0800 Subject: [PATCH 3/4] Test TCP_NODELAY error handling on server sockets --- .../pydevd/tests_python/test_pydevd_comm.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py b/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py index d585e3a2..e07684b7 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py @@ -28,9 +28,10 @@ def test_start_client_sets_tcp_nodelay(monkeypatch): def test_start_client_ignores_tcp_nodelay_error(monkeypatch, error): sock = mock.Mock() sock.setsockopt.side_effect = error + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay) start_client(monkeypatch, sock) - sock.setsockopt.assert_any_call(pydevd_comm.socket_module.IPPROTO_TCP, pydevd_comm.socket_module.TCP_NODELAY, 1) + sock.setsockopt.assert_any_call(pydevd_comm.socket_module.IPPROTO_TCP, mock.sentinel.tcp_nodelay, 1) def test_start_server_sets_tcp_nodelay(monkeypatch): @@ -38,5 +39,19 @@ def test_start_server_sets_tcp_nodelay(monkeypatch): address = ("127.0.0.1", 5678) server.configure_mock(**{"accept.return_value": (accepted, address), "getsockname.return_value": address}) monkeypatch.setattr(pydevd_comm, "create_server_socket", mock.Mock(return_value=server)) + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay) + assert pydevd_comm.start_server(0) is accepted + accepted.setsockopt.assert_called_once_with(pydevd_comm.socket_module.IPPROTO_TCP, mock.sentinel.tcp_nodelay, 1) + + +@pytest.mark.parametrize("error", [AttributeError, OSError]) +def test_start_server_ignores_tcp_nodelay_error(monkeypatch, error): + server, accepted = mock.Mock(), mock.Mock() + address = ("127.0.0.1", 5678) + server.configure_mock(**{"accept.return_value": (accepted, address), "getsockname.return_value": address}) + accepted.setsockopt.side_effect = error + monkeypatch.setattr(pydevd_comm, "create_server_socket", mock.Mock(return_value=server)) + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay) + assert pydevd_comm.start_server(0) is accepted - accepted.setsockopt.assert_called_once_with(pydevd_comm.socket_module.IPPROTO_TCP, pydevd_comm.socket_module.TCP_NODELAY, 1) + accepted.setsockopt.assert_called_once_with(pydevd_comm.socket_module.IPPROTO_TCP, mock.sentinel.tcp_nodelay, 1) From 2682bbe33009319ab5d2357ed7059ef3ee512073 Mon Sep 17 00:00:00 2001 From: nightcityblade Date: Thu, 20 Aug 2026 11:06:53 +0800 Subject: [PATCH 4/4] Test startup without TCP_NODELAY support --- .../pydevd/tests_python/test_pydevd_comm.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py b/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py index e07684b7..8a62544c 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_pydevd_comm.py @@ -18,7 +18,7 @@ def start_client(monkeypatch, sock): def test_start_client_sets_tcp_nodelay(monkeypatch): sock = mock.Mock() - monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay) + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay, raising=False) start_client(monkeypatch, sock) sock.setsockopt.assert_any_call(pydevd_comm.socket_module.IPPROTO_TCP, mock.sentinel.tcp_nodelay, 1) @@ -28,7 +28,7 @@ def test_start_client_sets_tcp_nodelay(monkeypatch): def test_start_client_ignores_tcp_nodelay_error(monkeypatch, error): sock = mock.Mock() sock.setsockopt.side_effect = error - monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay) + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay, raising=False) start_client(monkeypatch, sock) sock.setsockopt.assert_any_call(pydevd_comm.socket_module.IPPROTO_TCP, mock.sentinel.tcp_nodelay, 1) @@ -39,7 +39,7 @@ def test_start_server_sets_tcp_nodelay(monkeypatch): address = ("127.0.0.1", 5678) server.configure_mock(**{"accept.return_value": (accepted, address), "getsockname.return_value": address}) monkeypatch.setattr(pydevd_comm, "create_server_socket", mock.Mock(return_value=server)) - monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay) + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay, raising=False) assert pydevd_comm.start_server(0) is accepted accepted.setsockopt.assert_called_once_with(pydevd_comm.socket_module.IPPROTO_TCP, mock.sentinel.tcp_nodelay, 1) @@ -51,7 +51,18 @@ def test_start_server_ignores_tcp_nodelay_error(monkeypatch, error): server.configure_mock(**{"accept.return_value": (accepted, address), "getsockname.return_value": address}) accepted.setsockopt.side_effect = error monkeypatch.setattr(pydevd_comm, "create_server_socket", mock.Mock(return_value=server)) - monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay) + monkeypatch.setattr(pydevd_comm.socket_module, "TCP_NODELAY", mock.sentinel.tcp_nodelay, raising=False) assert pydevd_comm.start_server(0) is accepted accepted.setsockopt.assert_called_once_with(pydevd_comm.socket_module.IPPROTO_TCP, mock.sentinel.tcp_nodelay, 1) + + +def test_startup_ignores_missing_tcp_nodelay(monkeypatch): + monkeypatch.delattr(pydevd_comm.socket_module, "TCP_NODELAY", raising=False) + start_client(monkeypatch, mock.Mock()) + + server, accepted = mock.Mock(), mock.Mock() + address = ("127.0.0.1", 5678) + server.configure_mock(**{"accept.return_value": (accepted, address), "getsockname.return_value": address}) + monkeypatch.setattr(pydevd_comm, "create_server_socket", mock.Mock(return_value=server)) + assert pydevd_comm.start_server(0) is accepted