Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent building in GIL-less environment #4327

Merged
merged 6 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions newsfragments/4327.packaging.md
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from functools import lru_cache
from glob import glob
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.command
Expand Down Expand Up @@ -647,6 +647,14 @@ def test_version_limits(session: nox.Session):
config_file.set("PyPy", "3.11")
_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")
def check_feature_powerset(session: nox.Session):
Expand Down Expand Up @@ -907,14 +915,17 @@ class _ConfigFile:
def __init__(self, config_file) -> None:
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."""
self._config_file.seek(0)
self._config_file.truncate(0)
self._config_file.write(
f"""\
implementation={implementation}
version={version}
build_flags={','.join(build_flags)}
suppress_build_script_link_lines=true
"""
)
Expand Down
5 changes: 4 additions & 1 deletion pyo3-build-config/src/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ pub enum BuildFlag {
Py_DEBUG,
Py_REF_DEBUG,
Py_TRACE_REFS,
Py_GIL_DISABLED,
COUNT_ALLOCS,
Other(String),
}
Expand All @@ -1016,6 +1017,7 @@ impl FromStr for BuildFlag {
"Py_DEBUG" => Ok(BuildFlag::Py_DEBUG),
"Py_REF_DEBUG" => Ok(BuildFlag::Py_REF_DEBUG),
"Py_TRACE_REFS" => Ok(BuildFlag::Py_TRACE_REFS),
"Py_GIL_DISABLED" => Ok(BuildFlag::Py_GIL_DISABLED),
"COUNT_ALLOCS" => Ok(BuildFlag::COUNT_ALLOCS),
other => Ok(BuildFlag::Other(other.to_owned())),
}
Expand All @@ -1039,10 +1041,11 @@ impl FromStr for BuildFlag {
pub struct BuildFlags(pub HashSet<BuildFlag>);

impl BuildFlags {
const ALL: [BuildFlag; 4] = [
const ALL: [BuildFlag; 5] = [
BuildFlag::Py_DEBUG,
BuildFlag::Py_REF_DEBUG,
BuildFlag::Py_TRACE_REFS,
BuildFlag::Py_GIL_DISABLED,
BuildFlag::COUNT_ALLOCS,
];

Expand Down
22 changes: 21 additions & 1 deletion pyo3-ffi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use pyo3_build_config::{
cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config,
InterpreterConfig, PythonVersion,
},
warn, PythonImplementation,
warn, BuildFlag, PythonImplementation,
};
use std::ops::Not;

/// Minimum Python version PyO3 supports.
struct SupportedVersions {
Expand Down Expand Up @@ -120,6 +121,24 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
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\
davidhewitt marked this conversation as resolved.
Show resolved Hide resolved
= 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<()> {
if let Some(pointer_width) = interpreter_config.pointer_width {
// Try to check whether the target architecture matches the python library
Expand Down Expand Up @@ -185,6 +204,7 @@ fn configure_pyo3() -> Result<()> {

ensure_python_version(&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.
interpreter_config.to_cargo_dep_env()?;
Expand Down
Loading