Skip to content

Commit 6be8064

Browse files
Prevent building in GIL-less environment (#4327)
* Prevent building in GIL-less environment * Add change log * add "yet" to phrasing * Add testing to build script * add link to issue * Fix formatting issues --------- Co-authored-by: David Hewitt <[email protected]>
1 parent 90c4799 commit 6be8064

File tree

4 files changed

+41
-4
lines changed

4 files changed

+41
-4
lines changed

newsfragments/4327.packaging.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This PR lets PyO3 checks `Py_GIL_DISABLED` build flag and prevents `pyo3-ffi` crate building against GIL-less Python,
2+
unless
3+
explicitly opt using the `UNSAFE_PYO3_BUILD_FREE_THREADED` environment flag.

noxfile.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from functools import lru_cache
1010
from glob import glob
1111
from pathlib import Path
12-
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple
12+
from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple
1313

1414
import nox
1515
import nox.command
@@ -655,6 +655,14 @@ def test_version_limits(session: nox.Session):
655655
config_file.set("PyPy", "3.11")
656656
_run_cargo(session, "check", env=env, expect_error=True)
657657

658+
# Python build with GIL disabled should fail building
659+
config_file.set("CPython", "3.13", build_flags=["Py_GIL_DISABLED"])
660+
_run_cargo(session, "check", env=env, expect_error=True)
661+
662+
# Python build with GIL disabled should pass with env flag on
663+
env["UNSAFE_PYO3_BUILD_FREE_THREADED"] = "1"
664+
_run_cargo(session, "check", env=env)
665+
658666

659667
@nox.session(name="check-feature-powerset", venv_backend="none")
660668
def check_feature_powerset(session: nox.Session):
@@ -919,14 +927,17 @@ class _ConfigFile:
919927
def __init__(self, config_file) -> None:
920928
self._config_file = config_file
921929

922-
def set(self, implementation: str, version: str) -> None:
930+
def set(
931+
self, implementation: str, version: str, build_flags: Iterable[str] = ()
932+
) -> None:
923933
"""Set the contents of this config file to the given implementation and version."""
924934
self._config_file.seek(0)
925935
self._config_file.truncate(0)
926936
self._config_file.write(
927937
f"""\
928938
implementation={implementation}
929939
version={version}
940+
build_flags={','.join(build_flags)}
930941
suppress_build_script_link_lines=true
931942
"""
932943
)

pyo3-build-config/src/impl_.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,7 @@ pub enum BuildFlag {
996996
Py_DEBUG,
997997
Py_REF_DEBUG,
998998
Py_TRACE_REFS,
999+
Py_GIL_DISABLED,
9991000
COUNT_ALLOCS,
10001001
Other(String),
10011002
}
@@ -1016,6 +1017,7 @@ impl FromStr for BuildFlag {
10161017
"Py_DEBUG" => Ok(BuildFlag::Py_DEBUG),
10171018
"Py_REF_DEBUG" => Ok(BuildFlag::Py_REF_DEBUG),
10181019
"Py_TRACE_REFS" => Ok(BuildFlag::Py_TRACE_REFS),
1020+
"Py_GIL_DISABLED" => Ok(BuildFlag::Py_GIL_DISABLED),
10191021
"COUNT_ALLOCS" => Ok(BuildFlag::COUNT_ALLOCS),
10201022
other => Ok(BuildFlag::Other(other.to_owned())),
10211023
}
@@ -1039,10 +1041,11 @@ impl FromStr for BuildFlag {
10391041
pub struct BuildFlags(pub HashSet<BuildFlag>);
10401042

10411043
impl BuildFlags {
1042-
const ALL: [BuildFlag; 4] = [
1044+
const ALL: [BuildFlag; 5] = [
10431045
BuildFlag::Py_DEBUG,
10441046
BuildFlag::Py_REF_DEBUG,
10451047
BuildFlag::Py_TRACE_REFS,
1048+
BuildFlag::Py_GIL_DISABLED,
10461049
BuildFlag::COUNT_ALLOCS,
10471050
];
10481051

pyo3-ffi/build.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use pyo3_build_config::{
44
cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config,
55
InterpreterConfig, PythonVersion,
66
},
7-
warn, PythonImplementation,
7+
warn, BuildFlag, PythonImplementation,
88
};
9+
use std::ops::Not;
910

1011
/// Minimum Python version PyO3 supports.
1112
struct SupportedVersions {
@@ -120,6 +121,24 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
120121
Ok(())
121122
}
122123

124+
fn ensure_gil_enabled(interpreter_config: &InterpreterConfig) -> Result<()> {
125+
let gil_enabled = interpreter_config
126+
.build_flags
127+
.0
128+
.contains(&BuildFlag::Py_GIL_DISABLED)
129+
.not();
130+
ensure!(
131+
gil_enabled || std::env::var("UNSAFE_PYO3_BUILD_FREE_THREADED").map_or(false, |os_str| os_str == "1"),
132+
"the Python interpreter was built with the GIL disabled, which is not yet supported by PyO3\n\
133+
= help: see https://github.com/PyO3/pyo3/issues/4265 for more information\n\
134+
= help: please check if an updated version of PyO3 is available. Current version: {}\n\
135+
= help: set UNSAFE_PYO3_BUILD_FREE_THREADED=1 to suppress this check and build anyway for free-threaded Python",
136+
std::env::var("CARGO_PKG_VERSION").unwrap()
137+
);
138+
139+
Ok(())
140+
}
141+
123142
fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> {
124143
if let Some(pointer_width) = interpreter_config.pointer_width {
125144
// Try to check whether the target architecture matches the python library
@@ -185,6 +204,7 @@ fn configure_pyo3() -> Result<()> {
185204

186205
ensure_python_version(&interpreter_config)?;
187206
ensure_target_pointer_width(&interpreter_config)?;
207+
ensure_gil_enabled(&interpreter_config)?;
188208

189209
// Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var.
190210
interpreter_config.to_cargo_dep_env()?;

0 commit comments

Comments
 (0)