From be5bddcc770aaf9008d608f0e92c13386ac286ca Mon Sep 17 00:00:00 2001 From: Song Lin Date: Thu, 28 Aug 2025 16:18:32 +0800 Subject: [PATCH 01/15] Query available services dynamically --- Lib/test/test_socket.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 76fd33c7dc8767..e5d4aea7785a78 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1259,6 +1259,38 @@ def testGetServBy(self): # Find one service that exists, then check all the related interfaces. # I've ordered this by protocols that have both a tcp and udp # protocol, at least for modern Linuxes. + def query_available_service(*protocols): + services_file = '/etc/services' + if not os.path.exists(services_file): + return None + services_found = dict() + with open(services_file, 'r') as f: + for line in f: + line = line.strip() + if line.startswith('#'): + # Skip comment line. + continue + tokens = line.split() + if len(tokens) < 2: + continue + if '/' not in tokens[1]: + continue + try: + _, entry_protocol = tokens[1].split('/') + except: + continue + entry_name = tokens[0] + if entry_name not in services_found: + services_found[entry_name] = [entry_protocol] + else: + services_found[entry_name].append(entry_protocol) + for protocol in protocols: + if protocol not in services_found[entry_name]: + break + else: + return entry_name + return None + if ( sys.platform.startswith( ('linux', 'android', 'freebsd', 'netbsd', 'gnukfreebsd')) @@ -1276,7 +1308,12 @@ def testGetServBy(self): except OSError: pass else: - raise OSError + service = query_available_service('tcp', 'udp') + if service is None: + service = query_available_service('tcp') + if service is None: + self.skipTest('No available service found.') + port = socket.getservbyname(service, 'tcp') # Try same call with optional protocol omitted # Issue gh-71123: this fails on Android before API level 23. if not (support.is_android and platform.android_ver().api_level < 23): From 376e9b270daa91960f16d544250359c00e32c338 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 09:21:20 +0000 Subject: [PATCH 02/15] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst diff --git a/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst b/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst new file mode 100644 index 00000000000000..9b7f8b6f7b5ea7 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst @@ -0,0 +1 @@ +Fix `testGetServBy` in `test_socket.py` fails on systems without certain entries in `/etc/services` From 843478d43979310ab5017293e008cf50bb9e0022 Mon Sep 17 00:00:00 2001 From: Song Lin Date: Thu, 28 Aug 2025 19:16:48 +0800 Subject: [PATCH 03/15] Use double backticks in news entry --- .../next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst b/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst index 9b7f8b6f7b5ea7..9a70592e793c9e 100644 --- a/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst +++ b/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst @@ -1 +1 @@ -Fix `testGetServBy` in `test_socket.py` fails on systems without certain entries in `/etc/services` +Fix ``testGetServBy`` in ``test_socket.py`` fails on systems without certain entries in ``/etc/services`` From 24fe33c20e31b80681158f5b3f1ab0e4ac16f877 Mon Sep 17 00:00:00 2001 From: Song Lin <124190229+ggqlq@users.noreply.github.com> Date: Fri, 29 Aug 2025 14:08:36 +0800 Subject: [PATCH 04/15] Update Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- .../next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst b/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst index 9a70592e793c9e..948fdbb0e86886 100644 --- a/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst +++ b/Misc/NEWS.d/next/Tests/2025-08-28-09-21-18.gh-issue-138216.lNF79V.rst @@ -1 +1 @@ -Fix ``testGetServBy`` in ``test_socket.py`` fails on systems without certain entries in ``/etc/services`` +Fix ``testGetServBy`` in ``test_socket.py`` on systems without certain entries in ``/etc/services``. From 3e05eb0f45cf8455fa8fda0387bf5a57d185b9e2 Mon Sep 17 00:00:00 2001 From: Song Lin Date: Fri, 29 Aug 2025 16:41:22 +0800 Subject: [PATCH 05/15] Remove UDP parsing --- Lib/test/test_socket.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index e5d4aea7785a78..8778889d3d2778 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1308,9 +1308,7 @@ def query_available_service(*protocols): except OSError: pass else: - service = query_available_service('tcp', 'udp') - if service is None: - service = query_available_service('tcp') + service = query_available_service('tcp') if service is None: self.skipTest('No available service found.') port = socket.getservbyname(service, 'tcp') From df7e54fbdbe1f641f01aeba61c6f30e22859f6ce Mon Sep 17 00:00:00 2001 From: Song Lin Date: Fri, 29 Aug 2025 17:10:33 +0800 Subject: [PATCH 06/15] Move query_available_service function outside --- Lib/test/test_socket.py | 64 ++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 8778889d3d2778..b6e5d93c4db16f 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -201,6 +201,36 @@ def _have_socket_hyperv(): s.close() return True +def _query_available_service(expected_protocols, services_file = '/etc/services'): + if not os.path.exists(services_file): + return None + services_found = dict() + with open(services_file, 'r') as f: + for line in f: + line = line.strip() + if line.startswith('#'): + # Skip comment line. + continue + tokens = line.split() + if len(tokens) < 2: + continue + if '/' not in tokens[1]: + continue + try: + _, entry_protocol = tokens[1].split('/') + except: + continue + entry_name = tokens[0] + if entry_name not in services_found: + services_found[entry_name] = [entry_protocol] + else: + services_found[entry_name].append(entry_protocol) + for protocol in expected_protocols: + if protocol not in services_found[entry_name]: + break + else: + return entry_name + return None @contextlib.contextmanager def socket_setdefaulttimeout(timeout): @@ -1259,38 +1289,6 @@ def testGetServBy(self): # Find one service that exists, then check all the related interfaces. # I've ordered this by protocols that have both a tcp and udp # protocol, at least for modern Linuxes. - def query_available_service(*protocols): - services_file = '/etc/services' - if not os.path.exists(services_file): - return None - services_found = dict() - with open(services_file, 'r') as f: - for line in f: - line = line.strip() - if line.startswith('#'): - # Skip comment line. - continue - tokens = line.split() - if len(tokens) < 2: - continue - if '/' not in tokens[1]: - continue - try: - _, entry_protocol = tokens[1].split('/') - except: - continue - entry_name = tokens[0] - if entry_name not in services_found: - services_found[entry_name] = [entry_protocol] - else: - services_found[entry_name].append(entry_protocol) - for protocol in protocols: - if protocol not in services_found[entry_name]: - break - else: - return entry_name - return None - if ( sys.platform.startswith( ('linux', 'android', 'freebsd', 'netbsd', 'gnukfreebsd')) @@ -1308,7 +1306,7 @@ def query_available_service(*protocols): except OSError: pass else: - service = query_available_service('tcp') + service = _query_available_service(('tcp')) if service is None: self.skipTest('No available service found.') port = socket.getservbyname(service, 'tcp') From e3ec244921196b8e22556321f04ba3c47f4e1bfc Mon Sep 17 00:00:00 2001 From: Song Lin <124190229+ggqlq@users.noreply.github.com> Date: Fri, 29 Aug 2025 17:51:01 +0800 Subject: [PATCH 07/15] Update Lib/test/test_socket.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index b6e5d93c4db16f..329a32343292b5 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1306,7 +1306,7 @@ def testGetServBy(self): except OSError: pass else: - service = _query_available_service(('tcp')) + service = _query_available_service(['tcp']) if service is None: self.skipTest('No available service found.') port = socket.getservbyname(service, 'tcp') From 9fc34d05b891f81cf433e5e25a5f42698468a9fc Mon Sep 17 00:00:00 2001 From: Song Lin <124190229+ggqlq@users.noreply.github.com> Date: Fri, 29 Aug 2025 17:51:37 +0800 Subject: [PATCH 08/15] Update Lib/test/test_socket.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 329a32343292b5..a351c6faefced0 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1308,7 +1308,7 @@ def testGetServBy(self): else: service = _query_available_service(['tcp']) if service is None: - self.skipTest('No available service found.') + self.skipTest('No available TCP service found.') port = socket.getservbyname(service, 'tcp') # Try same call with optional protocol omitted # Issue gh-71123: this fails on Android before API level 23. From a17112259043dfe6e21f9f25b1a28484765affd8 Mon Sep 17 00:00:00 2001 From: Song Lin Date: Fri, 29 Aug 2025 18:00:38 +0800 Subject: [PATCH 09/15] Add import collections --- Lib/test/test_socket.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index a351c6faefced0..81f87fa10806a6 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7,6 +7,7 @@ from test.support.import_helper import ensure_lazy_imports import _thread as thread import array +import collections import contextlib import errno import gc From 4cac906f02ae0945df97a821a661f52e6095d4fe Mon Sep 17 00:00:00 2001 From: Song Lin Date: Fri, 29 Aug 2025 18:05:07 +0800 Subject: [PATCH 10/15] Rename _query_available_service_to _find_service --- Lib/test/test_socket.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 81f87fa10806a6..6e2135ff59fab9 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -202,7 +202,8 @@ def _have_socket_hyperv(): s.close() return True -def _query_available_service(expected_protocols, services_file = '/etc/services'): +def _find_service(expected_protocols, + services_file = '/etc/services'): if not os.path.exists(services_file): return None services_found = dict() @@ -1307,7 +1308,7 @@ def testGetServBy(self): except OSError: pass else: - service = _query_available_service(['tcp']) + service = _find_service(['tcp']) if service is None: self.skipTest('No available TCP service found.') port = socket.getservbyname(service, 'tcp') From 6c1c7adedfe1ea95838b576f1d73b3af79eb4dd5 Mon Sep 17 00:00:00 2001 From: Song Lin <124190229+ggqlq@users.noreply.github.com> Date: Fri, 29 Aug 2025 18:07:23 +0800 Subject: [PATCH 11/15] Update Lib/test/test_socket.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_socket.py | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 6e2135ff59fab9..ac064299e3093d 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -206,32 +206,21 @@ def _find_service(expected_protocols, services_file = '/etc/services'): if not os.path.exists(services_file): return None - services_found = dict() + expected_protocols = set(expected_protocols) + services = collections.defaultdict(set) with open(services_file, 'r') as f: - for line in f: - line = line.strip() + for line in map(str.strip, f): if line.startswith('#'): - # Skip comment line. continue tokens = line.split() - if len(tokens) < 2: - continue - if '/' not in tokens[1]: - continue - try: - _, entry_protocol = tokens[1].split('/') - except: + if len(tokens) < 2 or '/' not in tokens[1]: continue - entry_name = tokens[0] - if entry_name not in services_found: - services_found[entry_name] = [entry_protocol] - else: - services_found[entry_name].append(entry_protocol) - for protocol in expected_protocols: - if protocol not in services_found[entry_name]: - break - else: - return entry_name + service_name = tokens[0] + _, service_protocol = tokens[1].split('/', maxsplit=1) + service_protocols = services[service_name] + service_protocols.add(service_protocol) + if service_protocols <= expected_protocols: + return service_name return None @contextlib.contextmanager From f3bcc3ed4ad84967cc747f287c75c41475758928 Mon Sep 17 00:00:00 2001 From: Song Lin <124190229+ggqlq@users.noreply.github.com> Date: Fri, 29 Aug 2025 18:14:27 +0800 Subject: [PATCH 12/15] Update Lib/test/test_socket.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index ac064299e3093d..f662096578f90f 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -203,7 +203,7 @@ def _have_socket_hyperv(): return True def _find_service(expected_protocols, - services_file = '/etc/services'): + services_file='/etc/services'): if not os.path.exists(services_file): return None expected_protocols = set(expected_protocols) From 170da89e9a251986726e36903fb822badbeb4d02 Mon Sep 17 00:00:00 2001 From: Song Lin Date: Fri, 29 Aug 2025 18:17:15 +0800 Subject: [PATCH 13/15] Make sure top-level functions are separated by two blank lines --- Lib/test/test_socket.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index f662096578f90f..be35e232e3f58d 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -89,6 +89,7 @@ def wrapper(*args, **kwds): return decorator + def get_cid(): if fcntl is None: return None @@ -102,6 +103,7 @@ def get_cid(): else: return struct.unpack("I", r)[0] + def _have_socket_can(): """Check whether CAN sockets are supported on this host.""" try: @@ -112,6 +114,7 @@ def _have_socket_can(): s.close() return True + def _have_socket_can_isotp(): """Check whether CAN ISOTP sockets are supported on this host.""" try: @@ -122,6 +125,7 @@ def _have_socket_can_isotp(): s.close() return True + def _have_socket_can_j1939(): """Check whether CAN J1939 sockets are supported on this host.""" try: @@ -132,6 +136,7 @@ def _have_socket_can_j1939(): s.close() return True + def _have_socket_rds(): """Check whether RDS sockets are supported on this host.""" try: @@ -142,6 +147,7 @@ def _have_socket_rds(): s.close() return True + def _have_socket_alg(): """Check whether AF_ALG sockets are supported on this host.""" try: @@ -152,6 +158,7 @@ def _have_socket_alg(): s.close() return True + def _have_socket_qipcrtr(): """Check whether AF_QIPCRTR sockets are supported on this host.""" try: @@ -162,6 +169,7 @@ def _have_socket_qipcrtr(): s.close() return True + def _have_socket_vsock(): """Check whether AF_VSOCK sockets are supported on this host.""" cid = get_cid() @@ -202,6 +210,7 @@ def _have_socket_hyperv(): s.close() return True + def _find_service(expected_protocols, services_file='/etc/services'): if not os.path.exists(services_file): @@ -223,6 +232,7 @@ def _find_service(expected_protocols, return service_name return None + @contextlib.contextmanager def socket_setdefaulttimeout(timeout): old_timeout = socket.getdefaulttimeout() From c61508bb85ee211946ba7baa59657b809b23896b Mon Sep 17 00:00:00 2001 From: Song Lin Date: Fri, 29 Aug 2025 18:30:51 +0800 Subject: [PATCH 14/15] Revert "Make sure top-level functions are separated by two blank lines" This reverts commit 170da89e9a251986726e36903fb822badbeb4d02. --- Lib/test/test_socket.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index be35e232e3f58d..f662096578f90f 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -89,7 +89,6 @@ def wrapper(*args, **kwds): return decorator - def get_cid(): if fcntl is None: return None @@ -103,7 +102,6 @@ def get_cid(): else: return struct.unpack("I", r)[0] - def _have_socket_can(): """Check whether CAN sockets are supported on this host.""" try: @@ -114,7 +112,6 @@ def _have_socket_can(): s.close() return True - def _have_socket_can_isotp(): """Check whether CAN ISOTP sockets are supported on this host.""" try: @@ -125,7 +122,6 @@ def _have_socket_can_isotp(): s.close() return True - def _have_socket_can_j1939(): """Check whether CAN J1939 sockets are supported on this host.""" try: @@ -136,7 +132,6 @@ def _have_socket_can_j1939(): s.close() return True - def _have_socket_rds(): """Check whether RDS sockets are supported on this host.""" try: @@ -147,7 +142,6 @@ def _have_socket_rds(): s.close() return True - def _have_socket_alg(): """Check whether AF_ALG sockets are supported on this host.""" try: @@ -158,7 +152,6 @@ def _have_socket_alg(): s.close() return True - def _have_socket_qipcrtr(): """Check whether AF_QIPCRTR sockets are supported on this host.""" try: @@ -169,7 +162,6 @@ def _have_socket_qipcrtr(): s.close() return True - def _have_socket_vsock(): """Check whether AF_VSOCK sockets are supported on this host.""" cid = get_cid() @@ -210,7 +202,6 @@ def _have_socket_hyperv(): s.close() return True - def _find_service(expected_protocols, services_file='/etc/services'): if not os.path.exists(services_file): @@ -232,7 +223,6 @@ def _find_service(expected_protocols, return service_name return None - @contextlib.contextmanager def socket_setdefaulttimeout(timeout): old_timeout = socket.getdefaulttimeout() From e6b1b79dda355ce73f6f211e027abdf6a5e61397 Mon Sep 17 00:00:00 2001 From: Song Lin Date: Fri, 29 Aug 2025 18:34:04 +0800 Subject: [PATCH 15/15] Make sure two blank lines around _fund_service function --- Lib/test/test_socket.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index f662096578f90f..34d9ca05e04ca6 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -202,6 +202,7 @@ def _have_socket_hyperv(): s.close() return True + def _find_service(expected_protocols, services_file='/etc/services'): if not os.path.exists(services_file): @@ -223,6 +224,7 @@ def _find_service(expected_protocols, return service_name return None + @contextlib.contextmanager def socket_setdefaulttimeout(timeout): old_timeout = socket.getdefaulttimeout()