]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
python:tests: Fix ProcessPoolExecutor with Python 3.14
authorAndreas Schneider <asn@samba.org>
Tue, 9 Dec 2025 09:35:25 +0000 (10:35 +0100)
committerAndreas Schneider <asn@cryptomilk.org>
Tue, 9 Dec 2025 12:54:46 +0000 (12:54 +0000)
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 <asn@samba.org>
Reviewed-by: Alexander Bokovoy <ab@samba.org>
python/samba/tests/docs.py
python/samba/tests/krb5/lockout_tests.py

index 336299a4facfcc4e4a141824b0042ecf4975bb0f..e5e358e817a2afdda396e1d19cc3b8f09521766d 100644 (file)
@@ -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 = []
 
index e33d9acb4a87f24e506f0301e4badc1f87354630..5e909640e0b7d5757fc71f4465c409dbeb3a82b3 100755 (executable)
@@ -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,