Skip to content

gh-155717: Determine multiprocessing's default start method lazily - #159254

Merged
gpshead merged 1 commit into
python:mainfrom
gpshead:gh-155717-lazy-default-context
Oct 12, 2026
Merged

gpshead merged 1 commit into
python:mainfrom
gpshead:gh-155717-lazy-default-context

Conversation

@gpshead

@gpshead gpshead commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

gh-155827 made import multiprocessing check whether the temporary directory is writeable, to choose between forkserver and spawn as the default start method. Doing that at import time is a startup regression, reported by Leandro Damascena in #155717 (comment): the import now also loads multiprocessing.util, subprocess, tempfile, shutil and random, even in programs that never start a process. Spawned children, the fork server and the resource tracker pay it too, as each imports multiprocessing.

This keeps the behaviour of gh-155827 but determines the default start method the first time it is needed, as that comment suggested.

Changes

flowchart TB
    subgraph before["Before (gh-155827)"]
        direction TB
        b1["import multiprocessing"] --> b2["import multiprocessing.util,<br>subprocess, tempfile, shutil, random"]
        b2 --> b3{"can a temporary file<br>be created?"}
        b3 -- "yes" --> b4["default = forkserver"]
        b3 -- "no" --> b5["default = spawn"]
        b3 -- "no usable temp<br>directory at all" --> b6["import fails with<br>FileNotFoundError"]
        b4 --> b7["first get_context() /<br>Process.start()"]
        b5 --> b7
        b7 --> b8["use the default<br>chosen at import"]
    end
    subgraph after["After (this PR)"]
        direction TB
        a1["import multiprocessing"] --> a2["no extra imports,<br>no default chosen yet"]
        a2 --> a3["first get_context() /<br>Process.start()"]
        a3 --> a4["import multiprocessing.util<br>and tempfile"]
        a4 --> a5{"can a temporary file<br>be created?"}
        a5 -- "yes" --> a6["default = forkserver"]
        a5 -- "no, or no usable<br>temp directory at all" --> a7["default = spawn"]
        a6 --> a8["cache and use<br>the default"]
        a7 --> a8
    end
Loading

The diagram shows POSIX platforms that support forkserver, other than macOS. Windows and macOS always default to spawn and never run the check; that is unchanged.

Measurements

Linux x86_64, -O3 non-PGO build, whole-process wall time, median of 140 interleaved runs. "Before gh-155827" uses the Lib/multiprocessing package from the parent of that commit.

Before gh-155827 main This PR
import multiprocessing 24.3 ms 35.9 ms 23.8 ms
from multiprocessing import cpu_count; cpu_count() 24.1 ms 36.0 ms 23.9 ms
modules in sys.modules after the import 67 101 67

Behaviour notes

  • The check now reflects the temporary directory configuration at first use instead of at import.
  • A process whose start method is set explicitly, which includes spawn and forkserver children of a parent using the default, does not run the check unless it asks for the default: get_all_start_methods(), or get_context() while no start method is set.
  • DefaultContext._default_context is now a functools.cached_property. It can still be read, assigned, deleted and patched like the plain attribute it was, and DefaultContext(context) still accepts an explicit default.

Backports

This needs backporting to 3.15: the 3.15 backport of gh-155827 (gh-155887) was merged on 2026-10-10, so the 3.15 branch currently has the import-time check, including the FileNotFoundError on import when there is no usable temporary directory. The 3.14 backport (gh-155886) is still open and should pick this up before it is merged.

There is no new NEWS entry: the entry from gh-155827 is still in Misc/NEWS.d/next/ on both main and 3.15 and still describes the user-visible change. If a release containing gh-155827 goes out before this lands on that branch, this needs its own NEWS entry there.

Authored using Claude, reviewed by me.

pythongh-155827 made `import multiprocessing` check whether the temporary
directory is writeable in order to choose between "forkserver" and
"spawn" as the default start method. That check ran at import time and
pulled in `multiprocessing.util`, `subprocess`, `tempfile`, `shutil`
and `random`, noticeably slowing down the import for every program,
including ones that never start a process and every spawned child,
the fork server and the resource tracker.

The default start method is now determined the first time it is needed
instead: on the first `get_context()`, `get_start_method()`,
`get_all_start_methods()` or default `Process` start.
`multiprocessing.util` goes back to importing `tempfile` only where it
is used.

Also include the fix from pythongh-158410 by Vyron Vasileiadis, along with a
test adapted from that PR: when `tempfile` finds no usable temporary
directory at all, the check now reports that instead of raising
`FileNotFoundError`, so that "spawn" is chosen.

The regression was reported, and the lazy approach suggested, by
Leandro Damascena in
python#155717 (comment)

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>
@gpshead gpshead added skip news stdlib Standard Library Python modules in the Lib/ directory type-bug An unexpected behavior, bug, or error labels Oct 12, 2026
Comment thread Lib/multiprocessing/util.py
@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #35101014 | 📁 Comparing 2737e92 against main (e29cce5)

  🔍 Preview build  

1 file changed
± library/multiprocessing.html

Comment thread Doc/library/multiprocessing.rst
Comment thread Lib/multiprocessing/util.py
Comment thread Lib/multiprocessing/util.py
@gpshead
gpshead marked this pull request as ready for review October 12, 2026 20:17
@gpshead gpshead self-assigned this Oct 12, 2026
@leandrodamascena

Copy link
Copy Markdown

Thanks a lot for picking this up so quickly, and for going with the lazy approach!

I ran this PR through the same Lambda benchmarks (x86_64, 1024 MB, PGO+LTO), comparing it with current main:

main this PR change
import multiprocessing 114.5 ms 64.3 ms −50.2 ms (−44%)
import + get_start_method() 114.5 ms 114.4 ms no change
starting and joining one Process 339.3 ms 330.2 ms −9.1 ms (−6%)

The import regression is completely gone, and the cost only shows up once the default start method is actually needed, as expected. Starting a real process even got faster than on main, since the fork server, the resource tracker and spawned children no longer repeat the default detection.

LGTM from the benchmark side. Thanks again!

@gpshead gpshead added the needs backport to 3.15 bugs and security fixes label Oct 12, 2026
@gpshead
gpshead merged commit 48ddda1 into python:main Oct 12, 2026
62 checks passed
@miss-islington-app

Copy link
Copy Markdown

Thanks @gpshead for the PR 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖

@miss-islington-app

Copy link
Copy Markdown

Sorry, @gpshead, I could not cleanly backport this to 3.15 due to a conflict.

Please backport manually with cherry_picker, see the devguide for more information.

cherry_picker 48ddda1987a17fa96d74409b9eccb7379f61816f 3.15

@bedevere-app

bedevere-app Bot commented Oct 12, 2026

Copy link
Copy Markdown

GH-159271 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 bugs and security fixes label Oct 12, 2026
gpshead added a commit to miss-islington/cpython that referenced this pull request Oct 12, 2026
…hod lazily (pythonGH-159254)

pythongh-155827 made `import multiprocessing` check whether the temporary
directory is writeable in order to choose between "forkserver" and
"spawn" as the default start method. That check ran at import time and
pulled in `multiprocessing.util`, `subprocess`, `tempfile`, `shutil`
and `random`, noticeably slowing down the import for every program,
including ones that never start a process and every spawned child,
the fork server and the resource tracker.

The default start method is now determined the first time it is needed
instead: on the first `get_context()`, `get_start_method()`,
`get_all_start_methods()` or default `Process` start.
`multiprocessing.util` goes back to importing `tempfile` only where it
is used.

Also include the fix from pythongh-158410 by Vyron Vasileiadis, along with a
test adapted from that PR: when `tempfile` finds no usable temporary
directory at all, the check now reports that instead of raising
`FileNotFoundError`, so that "spawn" is chosen.

The regression was reported, and the lazy approach suggested, by
Leandro Damascena in
python#155717 (comment)

(cherry picked from commit 48ddda1)

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>
@bedevere-bot

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 FreeBSD15 3.x (tier-3) has failed when building commit 48ddda1.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1238/builds/10102) and take a look at the build logs.
  4. Check if the failure is related to this commit (48ddda1) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1238/builds/10102

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd15/build/Lib/test/support/__init__.py", line 977, in gc_collect
    gc.collect()
    ~~~~~~~~~~^^
ResourceWarning: unclosed <socket.socket fd=8, family=2, type=1, proto=6, laddr=('127.0.0.1', 14009), raddr=('127.0.0.1', 29304)>
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x1e7c00e68710>:
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd15/build/Lib/asyncio/selector_events.py", line 882, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=8>
k


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd15/build/Lib/test/support/__init__.py", line 977, in gc_collect
    gc.collect()
    ~~~~~~~~~~^^
ResourceWarning: unclosed <socket.socket fd=7, family=2, type=1, proto=6, laddr=('127.0.0.1', 10737), raddr=('127.0.0.1', 24261)>
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x27e476395310>:
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd15/build/Lib/asyncio/selector_events.py", line 882, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=7>
k

@bedevere-bot

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 FreeBSD16 3.x (tier-3) has failed when building commit 48ddda1.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1857/builds/919) and take a look at the build logs.
  4. Check if the failure is related to this commit (48ddda1) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1857/builds/919

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd16/build/Lib/test/support/__init__.py", line 977, in gc_collect
    gc.collect()
    ~~~~~~~~~~^^
ResourceWarning: unclosed <socket.socket fd=9, family=2, type=1, proto=6, laddr=('127.0.0.1', 37609), raddr=('127.0.0.1', 56030)>
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x12741044fad0>:
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd16/build/Lib/asyncio/selector_events.py", line 882, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=9>
k


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd16/build/Lib/test/support/__init__.py", line 977, in gc_collect
    gc.collect()
    ~~~~~~~~~~^^
ResourceWarning: unclosed <socket.socket fd=8, family=2, type=1, proto=6, laddr=('127.0.0.1', 23541), raddr=('127.0.0.1', 27090)>
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x3ddf9af98710>:
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd16/build/Lib/asyncio/selector_events.py", line 882, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=8>
k

@bedevere-bot

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 Android 3.x (tier-3) has failed when building commit 48ddda1.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1591/builds/5458) and take a look at the build logs.
  4. Check if the failure is related to this commit (48ddda1) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1591/builds/5458

Summary of the results of the build (if available):

Click to see traceback logs
remote: Enumerating objects: 18, done.        
remote: Counting objects:   9% (1/11)        
remote: Counting objects:  18% (2/11)        
remote: Counting objects:  27% (3/11)        
remote: Counting objects:  36% (4/11)        
remote: Counting objects:  45% (5/11)        
remote: Counting objects:  54% (6/11)        
remote: Counting objects:  63% (7/11)        
remote: Counting objects:  72% (8/11)        
remote: Counting objects:  81% (9/11)        
remote: Counting objects:  90% (10/11)        
remote: Counting objects: 100% (11/11)        
remote: Counting objects: 100% (11/11), done.        
remote: Compressing objects:  10% (1/10)        
remote: Compressing objects:  20% (2/10)        
remote: Compressing objects:  30% (3/10)        
remote: Compressing objects:  40% (4/10)        
remote: Compressing objects:  50% (5/10)        
remote: Compressing objects:  60% (6/10)        
remote: Compressing objects:  70% (7/10)        
remote: Compressing objects:  80% (8/10)        
remote: Compressing objects:  90% (9/10)        
remote: Compressing objects: 100% (10/10)        
remote: Compressing objects: 100% (10/10), done.        
remote: Total 18 (delta 1), reused 1 (delta 1), pack-reused 7 (from 1)        
From https://github.com/python/cpython
 * branch                    main       -> FETCH_HEAD
Note: switching to '48ddda1987a17fa96d74409b9eccb7379f61816f'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 48ddda1987a gh-155717: Determine multiprocessing's default start method lazily (GH-159254)
Switched to and reset branch 'main'

configure: WARNING: no system libmpdec found; falling back to pure-Python version for the decimal module

In function ‘bytearray_resize_storage’,
    inlined from ‘bytearray_resize_lock_held’ at ../../Objects/bytearrayobject.c:339:9,
    inlined from ‘bytearray_append_impl’ at ../../Objects/bytearrayobject.c:2201:9,
    inlined from ‘bytearray_append’ at ../../Objects/clinic/bytearrayobject.c.h:1239:20:
../../Objects/bytearrayobject.c:259:13: warning: ‘memmove’ specified bound 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=]
  259 |             memmove(self->ob_bytes + moved, old_start + moved, remaining);
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../../Include/internal/pycore_dict.h:11,
                 from ../../Objects/typeobject.c:8:
In function ‘Py_DECREF_MORTAL’,
    inlined from ‘PyStackRef_CLOSE’ at ../../Include/internal/pycore_stackref.h:703:9,
    inlined from ‘_PyThreadState_PopCStackRef’ at ../../Include/internal/pycore_stackref.h:811:5,
    inlined from ‘vectorcall_maybe’ at ../../Objects/typeobject.c:3123:9:
../../Include/internal/pycore_object.h:448:8: warning: array subscript 0 is outside array bounds of ‘PyObject[0]’ {aka ‘struct _object[]’} [-Warray-bounds]
  448 |     if (--op->ob_refcnt == 0) {
      |        ^

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

  0  201k    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  201k  100  201k    0     0   313k      0 --:--:-- --:--:-- --:--:-- 65.7M
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

100 43710  100 43710    0     0  91398      0 --:--:-- --:--:-- --:--:-- 91398
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 6268k  100 6268k    0     0  10.2M      0 --:--:-- --:--:-- --:--:-- 50.1M
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

100 1699k  100 1699k    0     0  3583k      0 --:--:-- --:--:-- --:--:-- 3583k
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

100  629k  100  629k    0     0  1281k      0 --:--:-- --:--:-- --:--:-- 1281k
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

 14  511k   14 78303    0     0   155k      0  0:00:03 --:--:--  0:00:03  155k
100  511k  100  511k    0     0  1035k      0 --:--:-- --:--:-- --:--:--  212M
configure: WARNING: no system libmpdec found; falling back to pure-Python version for the decimal module

../../Python/fileutils.c:507:1: warning: unused function 'decode_current_locale' [-Wunused-function]
  507 | decode_current_locale(const char* arg, wchar_t **wstr, size_t *wlen,
      | ^~~~~~~~~~~~~~~~~~~~~
../../Python/fileutils.c:849:1: warning: unused function 'encode_current_locale' [-Wunused-function]
  849 | encode_current_locale(const wchar_t *text, char **str, size_t *output_length,
      | ^~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
../../Modules/_localemodule.c:195:1: warning: unused function 'is_all_ascii' [-Wunused-function]
  195 | is_all_ascii(const char *str)
      | ^~~~~~~~~~~~
1 warning generated.
../../Modules/pwdmodule.c:69:16: warning: unused variable 'pwd_db_mutex' [-Wunused-variable]
   69 | static PyMutex pwd_db_mutex = {0};
      |                ^~~~~~~~~~~~
1 warning generated.
../../Modules/_hacl/Lib_Memzero0.c:66:6: warning: "Your platform does not support any safe implementation of memzero -- consider a pull request!" [-W#warnings]
   66 |     #warning "Your platform does not support any safe implementation of memzero -- consider a pull request!"
      |      ^
1 warning generated.
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:282:5: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  282 |     CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |     ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:289:7: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  289 |       CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |       ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:435:5: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  435 |     CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |     ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:442:7: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  442 |       CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |       ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:548:5: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  548 |     CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |     ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:557:7: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  557 |       CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |       ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:579:7: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  579 |       CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |       ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:590:9: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  590 |         CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |         ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:687:11: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  687 |           CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |           ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:732:5: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  732 |     CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |     ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:758:7: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  758 |       CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |       ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:769:9: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  769 |         CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |         ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:782:11: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  782 |           CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |           ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:927:5: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  927 |     CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |     ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:940:7: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  940 |       CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |       ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:957:5: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  957 |     CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
      |     ^
../../Modules/expat/xmltok_impl.c:115:5: note: expanded from macro 'CHECK_NMSTRT_CASES'
  115 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:964:7: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
  964 |       CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |       ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:1199:7: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
 1199 |       CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |       ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
In file included from ../../Modules/expat/xmltok.c:326:
../../Modules/expat/xmltok_impl.c:1219:11: warning: fallthrough annotation in unreachable code [-Wunreachable-code-fallthrough]
 1219 |           CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
      |           ^
../../Modules/expat/xmltok_impl.c:86:5: note: expanded from macro 'CHECK_NAME_CASES'
   86 |     EXPAT_FALLTHROUGH;                                                         \
      |     ^
../../Modules/expat/fallthrough.h:47:33: note: expanded from macro 'EXPAT_FALLTHROUGH'
   47 | #      define EXPAT_FALLTHROUGH __attribute__((fallthrough))
      |                                 ^
19 warnings generated.

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  8676  100  8676    0     0  37521      0 --:--:-- --:--:-- --:--:-- 37558
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  2786  100  2786    0     0   8573      0 --:--:-- --:--:-- --:--:--  8598
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 48462  100 48462    0     0   141k      0 --:--:-- --:--:-- --:--:--  142k
adb: device offline

Expanding reachable commits in commit graph: 68466
Expanding reachable commits in commit graph: 140529
Expanding reachable commits in commit graph: 162768, done.

gpshead added a commit that referenced this pull request Oct 12, 2026
…zily (GH-159254) (GH-159271)

gh-155827 made `import multiprocessing` check whether the temporary
directory is writeable in order to choose between "forkserver" and
"spawn" as the default start method. That check ran at import time and
pulled in `multiprocessing.util`, `subprocess`, `tempfile`, `shutil`
and `random`, noticeably slowing down the import for every program,
including ones that never start a process and every spawned child,
the fork server and the resource tracker.

The default start method is now determined the first time it is needed
instead: on the first `get_context()`, `get_start_method()`,
`get_all_start_methods()` or default `Process` start.
`multiprocessing.util` goes back to importing `tempfile` only where it
is used.

Also include the fix from gh-158410 by Vyron Vasileiadis, along with a
test adapted from that PR: when `tempfile` finds no usable temporary
directory at all, the check now reports that instead of raising
`FileNotFoundError`, so that "spawn" is chosen.

The regression was reported, and the lazy approach suggested, by
Leandro Damascena in
#155717 (comment)

(cherry picked from commit 48ddda1)

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>

* gh-155717: Reword the NEWS entry for the default start method change

Say that the change applies when there is no writable temporary
directory and that starting a process previously failed there.

---------

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>
gpshead added a commit that referenced this pull request Oct 13, 2026
…ly filesystems (GH-155827) (#155886)

* gh-155717: use `spawn` as the default start method for read-only filesystems (GH-155827)

The "forkserver" start method (the default start method on non-Windows systems)
requires the ability to write temporary files, which is not possible if TMPDIR
is read-only (e.g., k8s containers mounted with `readOnlyRootFilesystem=True`).

On such filesystems, the default start method changes from "forkserver" to "spawn".
(cherry picked from commit c144799)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>

* Update Lib/test/_test_multiprocessing.py

* [3.14] gh-155717: Determine multiprocessing's default start method lazily (GH-159254)

gh-155827 made `import multiprocessing` check whether the temporary
directory is writeable in order to choose between "forkserver" and
"spawn" as the default start method. That check ran at import time and
pulled in `multiprocessing.util`, `subprocess`, `tempfile`, `shutil`
and `random`, noticeably slowing down the import for every program,
including ones that never start a process and every spawned child,
the fork server and the resource tracker.

The default start method is now determined the first time it is needed
instead: on the first `get_context()`, `get_start_method()`,
`get_all_start_methods()` or default `Process` start.
`multiprocessing.util` goes back to importing `tempfile` only where it
is used.

Also include the fix from gh-158410 by Vyron Vasileiadis, along with a
test adapted from that PR: when `tempfile` finds no usable temporary
directory at all, the check now reports that instead of raising
`FileNotFoundError`, so that "spawn" is chosen.

The regression was reported, and the lazy approach suggested, by
Leandro Damascena in
#155717 (comment)

(cherry picked from commit 48ddda1)

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>

* gh-155717: Reword the NEWS entry for the default start method change

Say that the change applies when there is no writable temporary
directory and that starting a process previously failed there.

---------

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip news stdlib Standard Library Python modules in the Lib/ directory type-bug An unexpected behavior, bug, or error

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants