The optional GIL rollout depends on extension compatibility
The Steering Council intends to accept PEP 703 through experimental, supported and default stages. Its conditions make ecosystem compatibility part of the technical work.
Removing CPython's global interpreter lock can make shared-memory parallel execution possible while creating a second family of extension binaries. The interpreter change and the distribution problem are inseparable for users who choose Python because of its native libraries. The Steering Council's July notice addresses that dependency directly. A successful prototype is a starting point for a transition, not evidence that every package can already participate.
In its July 28 notice, the council announces its intent to accept PEP 703 while continuing to work on acceptance details. The proposed progression has an experimental build, then a supported optional build, followed eventually by a default without the GIL. Each step depends on experience and community support. The notice also retains the possibility of abandoning the change if the disruption outweighs the gain.
I care about this proposal because Python's extension libraries are usually the reason I use it. An interpreter that permits more parallel execution but cannot load the required libraries would leave the main application constraint unresolved. The council is treating that constraint as part of the rollout.
Experimental status creates time for API work
The notice proposes introducing the no-GIL configuration as experimental. Core developers would support the experiment without expecting the whole community to support it immediately. Distributors are specifically discouraged from making that experimental configuration their default interpreter.
This distinction prevents an early build from becoming a compatibility obligation before its requirements are understood. Extension authors need to learn which assumptions fail under concurrent execution. Core developers may need to design additional C and Python APIs as that work proceeds. Publishing a build does not finish either process.
The council also wants changes needed for no-GIL support to work in GIL-enabled builds. That requirement matters to maintainers supporting a shared codebase. A source change that solves a concurrency problem in one configuration but cannot run in the ordinary configuration would force more branching and maintenance.
Backward compatibility with older interpreter versions remains a separate concern in the notice. An API can work in both new build configurations and still be unavailable in an older Python release. The maintenance problem therefore includes both build configuration and interpreter version. Treating those as the same dimension would obscure what extension authors actually have to support.
The July revision of PEP 703 still describes the proposal as a draft and names --disable-gil as the configuration option. It identifies object-header changes as the reason no-GIL builds are not ABI compatible with ordinary CPython or its stable ABI. Extensions require appropriate binaries even when their Python-facing API remains unchanged.
Rebuilding an extension does not establish thread safety
The PEP distinguishes binary compatibility from source assumptions. A native extension may rely on the GIL to protect its own global variables or object state. Recompiling it against a different object header does not add that missing synchronization. Threads executing the extension concurrently can reach state that previously depended on interpreter-wide mutual exclusion.
Borrowed references create another compatibility issue. A pointer obtained from a container does not necessarily keep the pointed-to object alive. With concurrent mutation, another thread can remove the object before the extension increments its reference count. The PEP proposes owned-reference alternatives for affected operations so lifetime acquisition can occur safely.
That does not make every borrowed-reference call invalid. The July text explicitly preserves borrowed-reference APIs because many uses remain safe. A reference into an immutable tuple has different lifetime constraints from one obtained through a dictionary another thread can mutate. Correct migration requires understanding the ownership relationship at the call site.
Memory allocation has its own requirements. The proposal requires Python objects to use the appropriate object allocation APIs and prohibits using those APIs for unrelated buffers. Custom allocator wrappers must delegate to the underlying allocator. These rules support the interpreter's concurrent memory-management strategy, so they cannot be reduced to a preference for one allocation function spelling.
There are Python-level consequences as well. The PEP says destruction of some objects can be delayed by deferred or biased reference counting. Code depending on precisely when a finalizer runs may therefore observe a difference even when it contains no native extension source. The compatibility investigation extends to lifetime behavior exposed through Python objects.
Supported status requires useful combinations of packages
The council's middle stage begins when community support makes production use viable. The no-GIL build would become supported while remaining optional, and the project would set a target for making it the default. That progression makes the available package ecosystem a condition for the next stage.
Package support is not a single percentage that settles every application. An application needs its own collection of dependencies to work together. A missing extension can prevent adoption even if many unrelated packages are available. This makes distribution work relevant to the practical value of the interpreter.
The PEP describes a coordination problem in that distribution work. Extension authors may wait for adoption before producing binaries, while users wait for compatible extensions before trying the interpreter. Gross proposes working with Anaconda to provide a compatible interpreter and packages through conda channels, concentrating some of the initial build effort.
That proposal does not eliminate the longer-term need for normal package distribution support. It offers a way to make a useful collection available while broader adoption develops. The distinction matters because a demonstration environment and a generally supported application dependency set solve different parts of the transition.
I find the refusal to make the experiment a distributor default sensible. Users should be able to investigate parallel execution without having an interpreter configuration selected for them before their dependencies support it. That also gives package maintainers time to identify necessary changes with evidence from their own code.
The performance evidence has a defined baseline
The July PEP reports execution overhead separately for programs using one thread and multiple threads. It gives five or six percent for one thread and seven or eight percent for multiple threads on the two reported processor families. The baseline includes an immortal-object implementation. Those are the proposal's measurements for specified configurations, not a universal cost of disabling the GIL.
Biased reference counting and per-object locking contribute to the reported overhead. The PEP also says multithreaded applications limit specialization of a given bytecode for thread-safety reasons. Allowing multiple cores to execute Python can offset overhead for suitable workloads, but that does not imply a gain for a program whose useful work remains sequential.
The proposal distinguishes its reference implementations too. A version based on Python 3.12 alpha 4 is useful for investigating execution overhead and as an implementation reference. The version based on Python 3.9.10 is described as more useful for real-world extension compatibility and multithreaded scaling. Their different roles help explain why a single prototype result cannot establish every adoption claim.
For the alpha-based implementation, the PEP also records a temporary workaround that immortalizes some objects in programs spawning multiple threads. It does not present that workaround as the final lifetime implementation. This qualification matters when interpreting an experiment involving memory retention. The behavior of a particular reference build can reflect an incomplete implementation as well as the intended design.
The default is a later decision with continuing costs
The council wants to avoid a permanent split between GIL and no-GIL configurations. Two common build modes increase the number of combinations that maintainers must test and debug. Leaving the choice optional indefinitely would preserve that cost even after the interpreter work matured.
At the same time, the notice says the default cannot change simply because core developers have finished their part. The community needs workable APIs and time to support them. These requirements place pressure in different directions. Compatibility argues for a gradual rollout, while maintaining duplicate configurations argues against an unlimited transition.
The notice therefore keeps timelines provisional and calls for repeated evaluation. It also asks that no-GIL-specific code remain identifiable while reversal is possible. That is an implementation requirement with an operational purpose. If the project concludes the experiment is too disruptive, removing its changes should remain feasible without having to disentangle them from unrelated interpreter work.
The technical question now includes the path between configurations. Shared-memory parallelism needs thread-safe objects and extensions, but broad adoption also needs a distribution arrangement in which users can obtain the libraries their programs require. The July notice makes those dependencies explicit before the project commits to changing its default interpreter behavior.