From e6ddbdc3eafac12c2b9ee7bd4a75fb68de62e075 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sat, 14 May 2022 22:12:47 +0100 Subject: [PATCH] add regression test for PEP 587 --- CHANGELOG.md | 1 + Cargo.toml | 1 + pyo3-ffi/src/cpython/pylifecycle.rs | 2 +- tests/test_pep_587.rs | 78 +++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 tests/test_pep_587.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index cb5203170cd..493081df7dd 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`. [#2370](https://github.com/PyO3/pyo3/pull/2370) ### 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..00b613e742f --- /dev/null +++ b/tests/test_pep_587.rs @@ -0,0 +1,78 @@ +#![cfg(not(any(PyPy, Py_LIMITED_API)))] + +use pyo3::ffi; +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; + + // Set program_name as regression test for #2370 + #[cfg(all(Py_3_10, windows))] + { + use libc::wchar_t; + use std::ffi::OsStr; + use std::os::windows::ffi::OsStrExt; + let mut value: Vec = OsStr::new("some_test\0").encode_wide().collect(); + + unsafe { + ffi::PyConfig_SetString( + &mut config, + &mut config.program_name, + value.as_ptr(), + ); + } + } + #[cfg(all(Py_3_10, unix))] + { + 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. This will initialize + // importlib. And if the custom importlib bytecode was registered above, + // our extension module will get imported and initialized. + 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"); + } +}