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 <mail@davidhewitt.dev>
This commit is contained in:
parent
90c4799951
commit
6be80647cb
|
@ -0,0 +1,3 @@
|
||||||
|
This PR lets PyO3 checks `Py_GIL_DISABLED` build flag and prevents `pyo3-ffi` crate building against GIL-less Python,
|
||||||
|
unless
|
||||||
|
explicitly opt using the `UNSAFE_PYO3_BUILD_FREE_THREADED` environment flag.
|
15
noxfile.py
15
noxfile.py
|
@ -9,7 +9,7 @@ import tempfile
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple
|
from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple
|
||||||
|
|
||||||
import nox
|
import nox
|
||||||
import nox.command
|
import nox.command
|
||||||
|
@ -655,6 +655,14 @@ def test_version_limits(session: nox.Session):
|
||||||
config_file.set("PyPy", "3.11")
|
config_file.set("PyPy", "3.11")
|
||||||
_run_cargo(session, "check", env=env, expect_error=True)
|
_run_cargo(session, "check", env=env, expect_error=True)
|
||||||
|
|
||||||
|
# Python build with GIL disabled should fail building
|
||||||
|
config_file.set("CPython", "3.13", build_flags=["Py_GIL_DISABLED"])
|
||||||
|
_run_cargo(session, "check", env=env, expect_error=True)
|
||||||
|
|
||||||
|
# Python build with GIL disabled should pass with env flag on
|
||||||
|
env["UNSAFE_PYO3_BUILD_FREE_THREADED"] = "1"
|
||||||
|
_run_cargo(session, "check", env=env)
|
||||||
|
|
||||||
|
|
||||||
@nox.session(name="check-feature-powerset", venv_backend="none")
|
@nox.session(name="check-feature-powerset", venv_backend="none")
|
||||||
def check_feature_powerset(session: nox.Session):
|
def check_feature_powerset(session: nox.Session):
|
||||||
|
@ -919,7 +927,9 @@ class _ConfigFile:
|
||||||
def __init__(self, config_file) -> None:
|
def __init__(self, config_file) -> None:
|
||||||
self._config_file = config_file
|
self._config_file = config_file
|
||||||
|
|
||||||
def set(self, implementation: str, version: str) -> None:
|
def set(
|
||||||
|
self, implementation: str, version: str, build_flags: Iterable[str] = ()
|
||||||
|
) -> None:
|
||||||
"""Set the contents of this config file to the given implementation and version."""
|
"""Set the contents of this config file to the given implementation and version."""
|
||||||
self._config_file.seek(0)
|
self._config_file.seek(0)
|
||||||
self._config_file.truncate(0)
|
self._config_file.truncate(0)
|
||||||
|
@ -927,6 +937,7 @@ class _ConfigFile:
|
||||||
f"""\
|
f"""\
|
||||||
implementation={implementation}
|
implementation={implementation}
|
||||||
version={version}
|
version={version}
|
||||||
|
build_flags={','.join(build_flags)}
|
||||||
suppress_build_script_link_lines=true
|
suppress_build_script_link_lines=true
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
|
@ -996,6 +996,7 @@ pub enum BuildFlag {
|
||||||
Py_DEBUG,
|
Py_DEBUG,
|
||||||
Py_REF_DEBUG,
|
Py_REF_DEBUG,
|
||||||
Py_TRACE_REFS,
|
Py_TRACE_REFS,
|
||||||
|
Py_GIL_DISABLED,
|
||||||
COUNT_ALLOCS,
|
COUNT_ALLOCS,
|
||||||
Other(String),
|
Other(String),
|
||||||
}
|
}
|
||||||
|
@ -1016,6 +1017,7 @@ impl FromStr for BuildFlag {
|
||||||
"Py_DEBUG" => Ok(BuildFlag::Py_DEBUG),
|
"Py_DEBUG" => Ok(BuildFlag::Py_DEBUG),
|
||||||
"Py_REF_DEBUG" => Ok(BuildFlag::Py_REF_DEBUG),
|
"Py_REF_DEBUG" => Ok(BuildFlag::Py_REF_DEBUG),
|
||||||
"Py_TRACE_REFS" => Ok(BuildFlag::Py_TRACE_REFS),
|
"Py_TRACE_REFS" => Ok(BuildFlag::Py_TRACE_REFS),
|
||||||
|
"Py_GIL_DISABLED" => Ok(BuildFlag::Py_GIL_DISABLED),
|
||||||
"COUNT_ALLOCS" => Ok(BuildFlag::COUNT_ALLOCS),
|
"COUNT_ALLOCS" => Ok(BuildFlag::COUNT_ALLOCS),
|
||||||
other => Ok(BuildFlag::Other(other.to_owned())),
|
other => Ok(BuildFlag::Other(other.to_owned())),
|
||||||
}
|
}
|
||||||
|
@ -1039,10 +1041,11 @@ impl FromStr for BuildFlag {
|
||||||
pub struct BuildFlags(pub HashSet<BuildFlag>);
|
pub struct BuildFlags(pub HashSet<BuildFlag>);
|
||||||
|
|
||||||
impl BuildFlags {
|
impl BuildFlags {
|
||||||
const ALL: [BuildFlag; 4] = [
|
const ALL: [BuildFlag; 5] = [
|
||||||
BuildFlag::Py_DEBUG,
|
BuildFlag::Py_DEBUG,
|
||||||
BuildFlag::Py_REF_DEBUG,
|
BuildFlag::Py_REF_DEBUG,
|
||||||
BuildFlag::Py_TRACE_REFS,
|
BuildFlag::Py_TRACE_REFS,
|
||||||
|
BuildFlag::Py_GIL_DISABLED,
|
||||||
BuildFlag::COUNT_ALLOCS,
|
BuildFlag::COUNT_ALLOCS,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,9 @@ use pyo3_build_config::{
|
||||||
cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config,
|
cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config,
|
||||||
InterpreterConfig, PythonVersion,
|
InterpreterConfig, PythonVersion,
|
||||||
},
|
},
|
||||||
warn, PythonImplementation,
|
warn, BuildFlag, PythonImplementation,
|
||||||
};
|
};
|
||||||
|
use std::ops::Not;
|
||||||
|
|
||||||
/// Minimum Python version PyO3 supports.
|
/// Minimum Python version PyO3 supports.
|
||||||
struct SupportedVersions {
|
struct SupportedVersions {
|
||||||
|
@ -120,6 +121,24 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_gil_enabled(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
|
let gil_enabled = interpreter_config
|
||||||
|
.build_flags
|
||||||
|
.0
|
||||||
|
.contains(&BuildFlag::Py_GIL_DISABLED)
|
||||||
|
.not();
|
||||||
|
ensure!(
|
||||||
|
gil_enabled || std::env::var("UNSAFE_PYO3_BUILD_FREE_THREADED").map_or(false, |os_str| os_str == "1"),
|
||||||
|
"the Python interpreter was built with the GIL disabled, which is not yet supported by PyO3\n\
|
||||||
|
= help: see https://github.com/PyO3/pyo3/issues/4265 for more information\n\
|
||||||
|
= help: please check if an updated version of PyO3 is available. Current version: {}\n\
|
||||||
|
= help: set UNSAFE_PYO3_BUILD_FREE_THREADED=1 to suppress this check and build anyway for free-threaded Python",
|
||||||
|
std::env::var("CARGO_PKG_VERSION").unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> {
|
fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
if let Some(pointer_width) = interpreter_config.pointer_width {
|
if let Some(pointer_width) = interpreter_config.pointer_width {
|
||||||
// Try to check whether the target architecture matches the python library
|
// Try to check whether the target architecture matches the python library
|
||||||
|
@ -185,6 +204,7 @@ fn configure_pyo3() -> Result<()> {
|
||||||
|
|
||||||
ensure_python_version(&interpreter_config)?;
|
ensure_python_version(&interpreter_config)?;
|
||||||
ensure_target_pointer_width(&interpreter_config)?;
|
ensure_target_pointer_width(&interpreter_config)?;
|
||||||
|
ensure_gil_enabled(&interpreter_config)?;
|
||||||
|
|
||||||
// Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var.
|
// Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var.
|
||||||
interpreter_config.to_cargo_dep_env()?;
|
interpreter_config.to_cargo_dep_env()?;
|
||||||
|
|
Loading…
Reference in New Issue