From: Andreas Schneider Date: Tue, 9 Dec 2025 09:35:25 +0000 (+0100) Subject: python:tests: Fix ProcessPoolExecutor with Python 3.14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6b4a372fdbae8e26fa3016ca301b9253828a1645;p=thirdparty%2Fsamba.git python:tests: Fix ProcessPoolExecutor with Python 3.14 REASON: Exception: Exception: Traceback (most recent call last): File "/home/asn/workspace/prj/oss/samba/asn-fix/bin/python/samba/tests/krb5/lockout_tests.py", line 858, in test_lockout_transaction_bad_pwd_samr_aes self.do_lockout_transaction(connect_samr_aes, correct_pw=False) ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/asn/workspace/prj/oss/samba/asn-fix/bin/python/samba/tests/krb5/lockout_tests.py", line 990, in do_lockout_transaction connect_future = executor.submit( connect_fn, ...<7 lines>... workstation=user_creds.get_workstation(), dn=str(user_dn)) File "/usr/lib64/python3.14/concurrent/futures/process.py", line 816, in submit self._adjust_process_count() ~~~~~~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib64/python3.14/concurrent/futures/process.py", line 775, in _adjust_process_count self._spawn_process() ~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib64/python3.14/concurrent/futures/process.py", line 793, in _spawn_process p.start() ~~~~~~~^^ File "/usr/lib64/python3.14/multiprocessing/process.py", line 121, in start self._popen = self._Popen(self) ~~~~~~~~~~~^^^^^^ File "/usr/lib64/python3.14/multiprocessing/context.py", line 300, in _Popen return Popen(process_obj) File "/usr/lib64/python3.14/multiprocessing/popen_forkserver.py", line 35, in __init__ super().__init__(process_obj) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ File "/usr/lib64/python3.14/multiprocessing/popen_fork.py", line 20, in __init__ self._launch(process_obj) ~~~~~~~~~~~~^^^^^^^^^^^^^ File "/usr/lib64/python3.14/multiprocessing/popen_forkserver.py", line 42, in _launch prep_data = spawn.get_preparation_data(process_obj._name) File "/usr/lib64/python3.14/multiprocessing/spawn.py", line 164, in get_preparation_data _check_not_importing_main() ~~~~~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib64/python3.14/multiprocessing/spawn.py", line 140, in _check_not_importing_main raise RuntimeError(''' ...<16 lines>... ''') RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase. This probably means that you are not using fork to start your child processes and you have forgotten to use the proper idiom in the main module: if __name__ == '__main__': freeze_support() ... The "freeze_support()" line can be omitted if the program is not going to be frozen to produce an executable. To fix this issue, refer to the "Safe importing of main module" section in https://docs.python.org/3/library/multiprocessing.html In Python 3.14, the default multiprocessing start method changed from fork to forkserver/spawn. When using ProcessPoolExecutor, the code needs to either: 1. Explicitly set the start method to fork (the simplest fix for this case) 2. Or ensure proper if __name__ == '__main__': guards (not practical for test code run by a test framework) The fix is to explicitly use the fork start method when creating the ProcessPoolExecutor. This can be done by passing a mp_context parameter: Signed-off-by: Andreas Schneider Reviewed-by: Alexander Bokovoy --- diff --git a/python/samba/tests/docs.py b/python/samba/tests/docs.py index 336299a4fac..e5e358e817a 100644 --- a/python/samba/tests/docs.py +++ b/python/samba/tests/docs.py @@ -291,7 +291,10 @@ class SmbDotConfTests(TestCase): failset = set() - with concurrent.futures.ProcessPoolExecutor(max_workers=get_max_worker_count()) as executor: + mp_context = multiprocessing.get_context('fork') + with concurrent.futures.ProcessPoolExecutor( + max_workers=get_max_worker_count(), + mp_context=mp_context) as executor: result_futures = [] for tuples in self.defaults: @@ -341,7 +344,10 @@ class SmbDotConfTests(TestCase): failset = set() - with concurrent.futures.ProcessPoolExecutor(max_workers=get_max_worker_count()) as executor: + mp_context = multiprocessing.get_context('fork') + with concurrent.futures.ProcessPoolExecutor( + max_workers=get_max_worker_count(), + mp_context=mp_context) as executor: result_futures = [] for tuples in self.defaults: @@ -410,7 +416,10 @@ class SmbDotConfTests(TestCase): failset = set() - with concurrent.futures.ProcessPoolExecutor(max_workers=get_max_worker_count()) as executor: + mp_context = multiprocessing.get_context('fork') + with concurrent.futures.ProcessPoolExecutor( + max_workers=get_max_worker_count(), + mp_context=mp_context) as executor: result_futures1 = [] result_futures2 = [] diff --git a/python/samba/tests/krb5/lockout_tests.py b/python/samba/tests/krb5/lockout_tests.py index e33d9acb4a8..5e909640e0b 100755 --- a/python/samba/tests/krb5/lockout_tests.py +++ b/python/samba/tests/krb5/lockout_tests.py @@ -26,6 +26,7 @@ os.environ['PYTHONUNBUFFERED'] = '1' from concurrent import futures from enum import Enum from functools import partial +import multiprocessing from multiprocessing import Pipe import time @@ -985,7 +986,9 @@ class LockoutTests(KdcTgsBaseTests): password = password[:-1] # Prepare to connect to the server. - with futures.ProcessPoolExecutor(max_workers=1) as executor: + mp_context = multiprocessing.get_context('fork') + with futures.ProcessPoolExecutor(max_workers=1, + mp_context=mp_context) as executor: our_pipe, their_pipe = Pipe(duplex=True) connect_future = executor.submit( connect_fn, @@ -1099,7 +1102,9 @@ class LockoutTests(KdcTgsBaseTests): user_dn = ldb.Dn(samdb, str(user_creds.get_dn())) # Prepare to connect to the server with an invalid password. - with futures.ProcessPoolExecutor(max_workers=1) as executor: + mp_context = multiprocessing.get_context('fork') + with futures.ProcessPoolExecutor(max_workers=1, + mp_context=mp_context) as executor: our_pipe, their_pipe = Pipe(duplex=True) connect_future = executor.submit( connect_fn, @@ -1200,7 +1205,9 @@ class LockoutTests(KdcTgsBaseTests): # simultaneous requests. Only three of those attempts should get # through before the account is locked out. num_attempts = self.lockout_threshold + 1 - with futures.ProcessPoolExecutor(max_workers=num_attempts) as executor: + mp_context = multiprocessing.get_context('fork') + with futures.ProcessPoolExecutor(max_workers=num_attempts, + mp_context=mp_context) as executor: connect_futures = [] our_pipes = [] for i in range(num_attempts): @@ -1293,7 +1300,9 @@ class LockoutTests(KdcTgsBaseTests): password = user_creds.get_password() # Prepare to connect to the server with a valid password. - with futures.ProcessPoolExecutor(max_workers=1) as executor: + mp_context = multiprocessing.get_context('fork') + with futures.ProcessPoolExecutor(max_workers=1, + mp_context=mp_context) as executor: our_pipe, their_pipe = Pipe(duplex=True) connect_future = executor.submit( connect_fn,