diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c34af3836b5..b0bfb0b31e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,7 +90,7 @@ jobs: "3.8", "3.9", "3.10", - "3.11.0-alpha.6", + "3.11-dev", "pypy-3.7", "pypy-3.8", "pypy-3.9" diff --git a/CHANGELOG.md b/CHANGELOG.md index 9649464bc09..42a6a68401c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added the internal `IntoPyResult` trait to give better error messages when function return types do not implement `IntoPy`. [#2326](https://github.com/PyO3/pyo3/pull/2326) - Add `PyDictKeys`, `PyDictValues` and `PyDictItems` Rust types to represent `dict_keys`, `dict_values` and `dict_items` types. [#2358](https://github.com/PyO3/pyo3/pull/2358) - Add an experimental `generate-import-lib` feature to support auto-generating non-abi3 python import libraries for Windows targets. [#2364](https://github.com/PyO3/pyo3/pull/2364) +- Add FFI definition `Py_ExitStatusException`. [#2374](https://github.com/PyO3/pyo3/pull/2374) ### Changed diff --git a/Cargo.toml b/Cargo.toml index 182234f5076..90edab33547 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ send_wrapper = "0.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.61" rayon = "1.0.2" +widestring = "0.5.1" [build-dependencies] pyo3-build-config = { path = "pyo3-build-config", version = "0.16.4", features = ["resolve-config"] } diff --git a/pyo3-ffi/src/cpython/pylifecycle.rs b/pyo3-ffi/src/cpython/pylifecycle.rs index 3f1f16e26a4..08c47881e03 100644 --- a/pyo3-ffi/src/cpython/pylifecycle.rs +++ b/pyo3-ffi/src/cpython/pylifecycle.rs @@ -23,7 +23,7 @@ extern "C" { pub fn Py_RunMain() -> c_int; - // skipped Py_ExitStatusException + pub fn Py_ExitStatusException(status: PyStatus) -> !; // skipped _Py_RestoreSignals diff --git a/tests/test_pep_587.rs b/tests/test_pep_587.rs new file mode 100644 index 00000000000..ca5c3f65ea2 --- /dev/null +++ b/tests/test_pep_587.rs @@ -0,0 +1,60 @@ +#![cfg(all(Py_3_8, not(any(PyPy, Py_LIMITED_API))))] + +use pyo3::ffi; + +#[cfg(Py_3_10)] +use widestring::WideCString; + +#[test] +fn test_default_interpreter() { + macro_rules! ensure { + ($py_call:expr) => {{ + let status = $py_call; + unsafe { + if ffi::PyStatus_Exception(status) != 0 { + ffi::Py_ExitStatusException(status); + } + } + }}; + } + + let mut preconfig = unsafe { std::mem::zeroed() }; + + unsafe { ffi::PyPreConfig_InitPythonConfig(&mut preconfig) }; + preconfig.utf8_mode = 1; + + ensure!(unsafe { ffi::Py_PreInitialize(&preconfig) }); + + let mut config = unsafe { std::mem::zeroed() }; + unsafe { ffi::PyConfig_InitPythonConfig(&mut config) }; + + // Require manually calling _Py_InitializeMain to exercise more ffi code + config._init_main = 0; + + #[cfg(Py_3_10)] + unsafe { + ffi::PyConfig_SetBytesString( + &mut config, + &mut config.program_name, + "some_test\0".as_ptr().cast(), + ); + } + + ensure!(unsafe { ffi::Py_InitializeFromConfig(&config) }); + + // The GIL is held. + assert_eq!(unsafe { ffi::PyGILState_Check() }, 1); + + // Now proceed with the Python main initialization. + ensure!(unsafe { ffi::_Py_InitializeMain() }); + + // The GIL is held after finishing initialization. + assert_eq!(unsafe { ffi::PyGILState_Check() }, 1); + + // Confirm program name set above was picked up correctly + #[cfg(Py_3_10)] + { + let program_name = unsafe { WideCString::from_ptr_str(ffi::Py_GetProgramName().cast()) }; + assert_eq!(program_name.to_string().unwrap(), "some_test"); + } +}