From 39ffe29fac26b550f875e8c5b30b572238c3ac45 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Mon, 28 Oct 2024 13:50:00 -0600 Subject: [PATCH] rename supports_free_threaded to gil_used --- guide/src/free-threading.md | 10 +++++----- newsfragments/4588.added.md | 4 ++-- pyo3-macros-backend/src/attributes.rs | 4 ++-- pyo3-macros-backend/src/module.rs | 28 +++++++++++---------------- pytests/src/awaitable.rs | 2 +- pytests/src/buf_and_str.rs | 2 +- pytests/src/comparisons.rs | 2 +- pytests/src/datetime.rs | 2 +- pytests/src/enums.rs | 2 +- pytests/src/free_threaded_mod.rs | 2 +- pytests/src/lib.rs | 2 +- pytests/src/misc.rs | 2 +- pytests/src/objstore.rs | 2 +- pytests/src/othermod.rs | 2 +- pytests/src/path.rs | 2 +- pytests/src/pyclasses.rs | 2 +- pytests/src/pyfunctions.rs | 2 +- pytests/src/sequence.rs | 2 +- pytests/src/subclassing.rs | 2 +- src/impl_/pymodule.rs | 27 ++++++++++---------------- src/types/module.rs | 14 +++++++------- tests/test_module.rs | 6 ++---- tests/ui/invalid_pymodule_args.stderr | 2 +- 23 files changed, 55 insertions(+), 70 deletions(-) diff --git a/guide/src/free-threading.md b/guide/src/free-threading.md index 2d96b018ad9..fe364077cfb 100644 --- a/guide/src/free-threading.md +++ b/guide/src/free-threading.md @@ -44,7 +44,7 @@ with no side-effects or defining an immutable Python class, will likely work "out of the box" on the free-threaded build. All that will be necessary is to annotate Python modules declared by rust code in your project to declare that they support free-threaded Python, for example by declaring the module with -`#[pymodule(supports_free_threaded = true)]`. +`#[pymodule(gil_used = false)]`. At a low-level, annotating a module sets the `Py_MOD_GIL` slot on modules defined by an extension to `Py_MOD_GIL_NOT_USED`, which allows the interpreter @@ -62,15 +62,15 @@ GIL to remain disabled by setting the `PYTHON_GIL=0` as an environment variable or passing `-Xgil=0` when starting Python (`0` means the GIL is turned off). If you are sure that all data structures exposed in a `PyModule` are -thread-safe, then pass `supports_free_threaded = true` as a parameter to the +thread-safe, then pass `gil_used = false` as a parameter to the `pymodule` procedural macro declaring the module or call -`PyModule::supports_free_threaded` on a `PyModule` instance. For example: +`PyModule::gil_used` on a `PyModule` instance. For example: ```rust use pyo3::prelude::*; /// This module supports free-threaded Python -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] fn my_extension(m: &Bound<'_, PyModule>) -> PyResult<()> { // add members to the module that you know are thread-safe Ok(()) @@ -85,7 +85,7 @@ use pyo3::prelude::*; # #[allow(dead_code)] fn register_child_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> { let child_module = PyModule::new(parent_module.py(), "child_module")?; - child_module.supports_free_threaded(true)?; + child_module.gil_used(false)?; parent_module.add_submodule(&child_module) } diff --git a/newsfragments/4588.added.md b/newsfragments/4588.added.md index f42453d33d9..42b5b8e219a 100644 --- a/newsfragments/4588.added.md +++ b/newsfragments/4588.added.md @@ -1,3 +1,3 @@ * It is now possible to declare that a module supports the free-threaded build - by either calling `PyModule::supports_free_threaded` or passing - `supports_free_threaded = true` as a parameter to the `pymodule` proc macro. + by either calling `PyModule::gil_used` or passing + `gil_used = false` as a parameter to the `pymodule` proc macro. diff --git a/pyo3-macros-backend/src/attributes.rs b/pyo3-macros-backend/src/attributes.rs index 1c9c5b1ec53..6fe75e44302 100644 --- a/pyo3-macros-backend/src/attributes.rs +++ b/pyo3-macros-backend/src/attributes.rs @@ -44,7 +44,7 @@ pub mod kw { syn::custom_keyword!(transparent); syn::custom_keyword!(unsendable); syn::custom_keyword!(weakref); - syn::custom_keyword!(supports_free_threaded); + syn::custom_keyword!(gil_used); } fn take_int(read: &mut &str, tracker: &mut usize) -> String { @@ -309,7 +309,7 @@ pub type RenameAllAttribute = KeywordAttribute; pub type TextSignatureAttribute = KeywordAttribute; pub type SubmoduleAttribute = kw::submodule; -pub type FreeThreadedAttribute = KeywordAttribute; +pub type GILUsedAttribute = KeywordAttribute; impl Parse for KeywordAttribute { fn parse(input: ParseStream<'_>) -> Result { diff --git a/pyo3-macros-backend/src/module.rs b/pyo3-macros-backend/src/module.rs index 8fd52582e26..62ff30613a0 100644 --- a/pyo3-macros-backend/src/module.rs +++ b/pyo3-macros-backend/src/module.rs @@ -2,7 +2,7 @@ use crate::{ attributes::{ - self, kw, take_attributes, take_pyo3_options, CrateAttribute, FreeThreadedAttribute, + self, kw, take_attributes, take_pyo3_options, CrateAttribute, GILUsedAttribute, ModuleAttribute, NameAttribute, SubmoduleAttribute, }, get_doc, @@ -29,7 +29,7 @@ pub struct PyModuleOptions { name: Option, module: Option, submodule: Option, - supports_free_threaded: Option, + gil_used: Option, } impl Parse for PyModuleOptions { @@ -73,8 +73,8 @@ impl PyModuleOptions { submodule, " (it is implicitly always specified for nested modules)" ), - PyModulePyO3Option::SupportsFreeThreaded(supports_free_threaded) => { - set_option!(supports_free_threaded) + PyModulePyO3Option::GILUsed(gil_used) => { + set_option!(gil_used) } } } @@ -353,10 +353,7 @@ pub fn pymodule_module_impl( ctx, module_def, options.submodule.is_some(), - options - .supports_free_threaded - .map(|op| op.value.value) - .unwrap_or(false), + options.gil_used.map(|op| op.value.value).unwrap_or(true), ); Ok(quote!( @@ -401,10 +398,7 @@ pub fn pymodule_function_impl( ctx, quote! { MakeDef::make_def() }, false, - options - .supports_free_threaded - .map(|op| op.value.value) - .unwrap_or(false), + options.gil_used.map(|op| op.value.value).unwrap_or(true), ); // Module function called with optional Python<'_> marker as first arg, followed by the module. @@ -450,7 +444,7 @@ fn module_initialization( ctx: &Ctx, module_def: TokenStream, is_submodule: bool, - supports_free_threaded: bool, + gil_used: bool, ) -> TokenStream { let Ctx { pyo3_path, .. } = ctx; let pyinit_symbol = format!("PyInit_{}", name); @@ -472,7 +466,7 @@ fn module_initialization( #[doc(hidden)] #[export_name = #pyinit_symbol] pub unsafe extern "C" fn __pyo3_init() -> *mut #pyo3_path::ffi::PyObject { - #pyo3_path::impl_::trampoline::module_init(|py| _PYO3_DEF.make_module(py, #supports_free_threaded)) + #pyo3_path::impl_::trampoline::module_init(|py| _PYO3_DEF.make_module(py, #gil_used)) } }); } @@ -619,7 +613,7 @@ enum PyModulePyO3Option { Crate(CrateAttribute), Name(NameAttribute), Module(ModuleAttribute), - SupportsFreeThreaded(FreeThreadedAttribute), + GILUsed(GILUsedAttribute), } impl Parse for PyModulePyO3Option { @@ -633,8 +627,8 @@ impl Parse for PyModulePyO3Option { input.parse().map(PyModulePyO3Option::Module) } else if lookahead.peek(attributes::kw::submodule) { input.parse().map(PyModulePyO3Option::Submodule) - } else if lookahead.peek(attributes::kw::supports_free_threaded) { - input.parse().map(PyModulePyO3Option::SupportsFreeThreaded) + } else if lookahead.peek(attributes::kw::gil_used) { + input.parse().map(PyModulePyO3Option::GILUsed) } else { Err(lookahead.error()) } diff --git a/pytests/src/awaitable.rs b/pytests/src/awaitable.rs index 4dd0313c56c..fb04c33ed05 100644 --- a/pytests/src/awaitable.rs +++ b/pytests/src/awaitable.rs @@ -78,7 +78,7 @@ impl FutureAwaitable { } } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn awaitable(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; diff --git a/pytests/src/buf_and_str.rs b/pytests/src/buf_and_str.rs index 3442c121a78..15230a5e153 100644 --- a/pytests/src/buf_and_str.rs +++ b/pytests/src/buf_and_str.rs @@ -47,7 +47,7 @@ fn return_memoryview(py: Python<'_>) -> PyResult> { PyMemoryView::from(&bytes) } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn buf_and_str(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_function(wrap_pyfunction!(return_memoryview, m)?)?; diff --git a/pytests/src/comparisons.rs b/pytests/src/comparisons.rs index 8cc1b728a5e..4ed79e42790 100644 --- a/pytests/src/comparisons.rs +++ b/pytests/src/comparisons.rs @@ -112,7 +112,7 @@ impl OrderedDefaultNe { } } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn comparisons(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; diff --git a/pytests/src/datetime.rs b/pytests/src/datetime.rs index 1fc8beb42e8..5162b3508a5 100644 --- a/pytests/src/datetime.rs +++ b/pytests/src/datetime.rs @@ -203,7 +203,7 @@ impl TzClass { } } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn datetime(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(make_date, m)?)?; m.add_function(wrap_pyfunction!(get_date_tuple, m)?)?; diff --git a/pytests/src/enums.rs b/pytests/src/enums.rs index c18a839deb1..8652321700a 100644 --- a/pytests/src/enums.rs +++ b/pytests/src/enums.rs @@ -4,7 +4,7 @@ use pyo3::{ wrap_pyfunction, Bound, PyResult, }; -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn enums(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; diff --git a/pytests/src/free_threaded_mod.rs b/pytests/src/free_threaded_mod.rs index 5ef7715775b..e0b44616749 100644 --- a/pytests/src/free_threaded_mod.rs +++ b/pytests/src/free_threaded_mod.rs @@ -5,7 +5,7 @@ fn add_two(x: usize) -> usize { x + 2 } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn free_threaded_mod(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(add_two, m)?)?; Ok(()) diff --git a/pytests/src/lib.rs b/pytests/src/lib.rs index 736d870f6cf..8a467fe3554 100644 --- a/pytests/src/lib.rs +++ b/pytests/src/lib.rs @@ -18,7 +18,7 @@ pub mod pyfunctions; pub mod sequence; pub mod subclassing; -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] fn pyo3_pytests(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(awaitable::awaitable))?; #[cfg(not(Py_LIMITED_API))] diff --git a/pytests/src/misc.rs b/pytests/src/misc.rs index bc42ee81db8..e44d1aa0ecf 100644 --- a/pytests/src/misc.rs +++ b/pytests/src/misc.rs @@ -32,7 +32,7 @@ fn get_item_and_run_callback(dict: Bound<'_, PyDict>, callback: Bound<'_, PyAny> Ok(()) } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn misc(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(issue_219, m)?)?; m.add_function(wrap_pyfunction!(get_type_fully_qualified_name, m)?)?; diff --git a/pytests/src/objstore.rs b/pytests/src/objstore.rs index acf288e36b0..8e729052992 100644 --- a/pytests/src/objstore.rs +++ b/pytests/src/objstore.rs @@ -18,7 +18,7 @@ impl ObjStore { } } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn objstore(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::() } diff --git a/pytests/src/othermod.rs b/pytests/src/othermod.rs index 394e9e013da..0de912d7d04 100644 --- a/pytests/src/othermod.rs +++ b/pytests/src/othermod.rs @@ -28,7 +28,7 @@ fn double(x: i32) -> i32 { x * 2 } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn othermod(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(double, m)?)?; diff --git a/pytests/src/path.rs b/pytests/src/path.rs index d3f875ba165..b52c038ed34 100644 --- a/pytests/src/path.rs +++ b/pytests/src/path.rs @@ -11,7 +11,7 @@ fn take_pathbuf(path: PathBuf) -> PathBuf { path } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn path(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(make_path, m)?)?; m.add_function(wrap_pyfunction!(take_pathbuf, m)?)?; diff --git a/pytests/src/pyclasses.rs b/pytests/src/pyclasses.rs index ed9b78c5d4e..3af08c053cc 100644 --- a/pytests/src/pyclasses.rs +++ b/pytests/src/pyclasses.rs @@ -104,7 +104,7 @@ impl ClassWithDict { } } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn pyclasses(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; diff --git a/pytests/src/pyfunctions.rs b/pytests/src/pyfunctions.rs index abb3cc739d0..024641d3d2e 100644 --- a/pytests/src/pyfunctions.rs +++ b/pytests/src/pyfunctions.rs @@ -67,7 +67,7 @@ fn args_kwargs<'py>( (args, kwargs) } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn pyfunctions(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(none, m)?)?; m.add_function(wrap_pyfunction!(simple, m)?)?; diff --git a/pytests/src/sequence.rs b/pytests/src/sequence.rs index b3f38f7db58..175f5fba8aa 100644 --- a/pytests/src/sequence.rs +++ b/pytests/src/sequence.rs @@ -16,7 +16,7 @@ fn vec_to_vec_pystring(vec: Vec>) -> Vec vec } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn sequence(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(vec_to_vec_i32, m)?)?; m.add_function(wrap_pyfunction!(array_to_array_i32, m)?)?; diff --git a/pytests/src/subclassing.rs b/pytests/src/subclassing.rs index 842d11a2151..0f00e74c19d 100644 --- a/pytests/src/subclassing.rs +++ b/pytests/src/subclassing.rs @@ -17,7 +17,7 @@ impl Subclassable { } } -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] pub fn subclassing(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; Ok(()) diff --git a/src/impl_/pymodule.rs b/src/impl_/pymodule.rs index 30c72c3e998..08b1ead7584 100644 --- a/src/impl_/pymodule.rs +++ b/src/impl_/pymodule.rs @@ -45,7 +45,7 @@ pub struct ModuleDef { /// Initialized module object, cached to avoid reinitialization. module: GILOnceCell>, /// Whether or not the module supports running without the GIL - supports_free_threaded: AtomicBool, + gil_used: AtomicBool, } /// Wrapper to enable initializer to be used in const fns. @@ -90,16 +90,12 @@ impl ModuleDef { ))] interpreter: AtomicI64::new(-1), module: GILOnceCell::new(), - supports_free_threaded: AtomicBool::new(false), + gil_used: AtomicBool::new(true), } } /// Builds a module using user given initializer. Used for [`#[pymodule]`][crate::pymodule]. #[cfg_attr(any(Py_LIMITED_API, not(Py_GIL_DISABLED)), allow(unused_variables))] - pub fn make_module( - &'static self, - py: Python<'_>, - supports_free_threaded: bool, - ) -> PyResult> { + pub fn make_module(&'static self, py: Python<'_>, gil_used: bool) -> PyResult> { // Check the interpreter ID has not changed, since we currently have no way to guarantee // that static data is not reused across interpreters. // @@ -147,14 +143,14 @@ impl ModuleDef { }; #[cfg(all(not(Py_LIMITED_API), Py_GIL_DISABLED))] { - let gil_used = { - if supports_free_threaded { - ffi::Py_MOD_GIL_NOT_USED - } else { + let gil_used_ptr = { + if gil_used { ffi::Py_MOD_GIL_USED + } else { + ffi::Py_MOD_GIL_NOT_USED } }; - if unsafe { ffi::PyUnstable_Module_SetGIL(module.as_ptr(), gil_used) } < 0 { + if unsafe { ffi::PyUnstable_Module_SetGIL(module.as_ptr(), gil_used_ptr) } < 0 { return Err(PyErr::fetch(py)); } } @@ -215,11 +211,8 @@ impl PyAddToModule for PyMethodDef { impl PyAddToModule for ModuleDef { fn add_to_module(&'static self, module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_submodule( - self.make_module( - module.py(), - self.supports_free_threaded.load(Ordering::Relaxed), - )? - .bind(module.py()), + self.make_module(module.py(), self.gil_used.load(Ordering::Relaxed))? + .bind(module.py()), ) } } diff --git a/src/types/module.rs b/src/types/module.rs index 46db046b087..1032258a8c8 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -404,10 +404,10 @@ pub trait PyModuleMethods<'py>: crate::sealed::Sealed { /// ```rust /// use pyo3::prelude::*; /// - /// #[pymodule(supports_free_threaded = true)] + /// #[pymodule(gil_used = false)] /// fn my_module(py: Python<'_>, module: &Bound<'_, PyModule>) -> PyResult<()> { /// let submodule = PyModule::new(py, "submodule")?; - /// submodule.supports_free_threaded(true)?; + /// submodule.gil_used(false)?; /// module.add_submodule(&submodule)?; /// Ok(()) /// } @@ -419,7 +419,7 @@ pub trait PyModuleMethods<'py>: crate::sealed::Sealed { /// `Py_MOD_GIL_NOT_USED`. /// /// This is a no-op on the GIL-enabled build. - fn supports_free_threaded(&self, supports_free_threaded: bool) -> PyResult<()>; + fn gil_used(&self, gil_used: bool) -> PyResult<()>; } impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> { @@ -548,12 +548,12 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> { } #[cfg_attr(any(Py_LIMITED_API, not(Py_GIL_DISABLED)), allow(unused_variables))] - fn supports_free_threaded(&self, supports_free_threaded: bool) -> PyResult<()> { + fn gil_used(&self, gil_used: bool) -> PyResult<()> { #[cfg(all(not(Py_LIMITED_API), Py_GIL_DISABLED))] { - let gil_used = match supports_free_threaded { - true => ffi::Py_MOD_GIL_NOT_USED, - false => ffi::Py_MOD_GIL_USED, + let gil_used = match gil_used { + true => ffi::Py_MOD_GIL_USED, + false => ffi::Py_MOD_GIL_NOT_USED, }; match unsafe { ffi::PyUnstable_Module_SetGIL(self.as_ptr(), gil_used) } { c_int::MIN..=-1 => Err(PyErr::fetch(self.py())), diff --git a/tests/test_module.rs b/tests/test_module.rs index d69fe1d3f7b..36d21abe9b0 100644 --- a/tests/test_module.rs +++ b/tests/test_module.rs @@ -37,7 +37,7 @@ fn double(x: usize) -> usize { } /// This module is implemented in Rust. -#[pymodule(supports_free_threaded = true)] +#[pymodule(gil_used = false)] fn module_with_functions(m: &Bound<'_, PyModule>) -> PyResult<()> { #[pyfn(m)] #[pyo3(name = "no_parameters")] @@ -182,9 +182,7 @@ fn test_module_from_code_bound() { .extract() .expect("The value should be able to be converted to an i32"); - adder_mod - .supports_free_threaded(true) - .expect("Disabling the GIL failed"); + adder_mod.gil_used(false).expect("Disabling the GIL failed"); assert_eq!(ret_value, 3); }); diff --git a/tests/ui/invalid_pymodule_args.stderr b/tests/ui/invalid_pymodule_args.stderr index 1144bbf8d8d..23a5109b4cb 100644 --- a/tests/ui/invalid_pymodule_args.stderr +++ b/tests/ui/invalid_pymodule_args.stderr @@ -1,4 +1,4 @@ -error: expected one of: `name`, `crate`, `module`, `submodule`, `supports_free_threaded` +error: expected one of: `name`, `crate`, `module`, `submodule`, `gil_used` --> tests/ui/invalid_pymodule_args.rs:3:12 | 3 | #[pymodule(some_arg)]