|
117 | 117 | //! [`Rc`]: std::rc::Rc
|
118 | 118 | //! [`Py`]: crate::Py
|
119 | 119 | use crate::conversion::IntoPyObject;
|
120 |
| -#[cfg(any(doc, not(Py_3_10)))] |
121 | 120 | use crate::err::PyErr;
|
122 | 121 | use crate::err::{self, PyResult};
|
123 | 122 | use crate::ffi_ptr_ext::FfiPtrExt;
|
@@ -649,30 +648,34 @@ impl<'py> Python<'py> {
|
649 | 648 | };
|
650 | 649 | let locals = locals.unwrap_or(globals);
|
651 | 650 |
|
652 |
| - #[cfg(not(Py_3_10))] |
653 |
| - { |
654 |
| - // If `globals` don't provide `__builtins__`, most of the code will fail if Python |
655 |
| - // version is <3.10. That's probably not what user intended, so insert `__builtins__` |
656 |
| - // for them. |
657 |
| - // |
658 |
| - // See also: |
659 |
| - // - https://github.com/python/cpython/pull/24564 (the same fix in CPython 3.10) |
660 |
| - // - https://github.com/PyO3/pyo3/issues/3370 |
661 |
| - let builtins_s = crate::intern!(self, "__builtins__").as_ptr(); |
662 |
| - let has_builtins = unsafe { ffi::PyDict_Contains(globals.as_ptr(), builtins_s) }; |
663 |
| - if has_builtins == -1 { |
664 |
| - return Err(PyErr::fetch(self)); |
665 |
| - } |
666 |
| - if has_builtins == 0 { |
667 |
| - // Inherit current builtins. |
668 |
| - let builtins = unsafe { ffi::PyEval_GetBuiltins() }; |
669 |
| - |
670 |
| - // `PyDict_SetItem` doesn't take ownership of `builtins`, but `PyEval_GetBuiltins` |
671 |
| - // seems to return a borrowed reference, so no leak here. |
672 |
| - if unsafe { ffi::PyDict_SetItem(globals.as_ptr(), builtins_s, builtins) } == -1 { |
673 |
| - return Err(PyErr::fetch(self)); |
| 651 | + // If `globals` don't provide `__builtins__`, most of the code will fail if Python |
| 652 | + // version is <3.10. That's probably not what user intended, so insert `__builtins__` |
| 653 | + // for them. |
| 654 | + // |
| 655 | + // See also: |
| 656 | + // - https://github.com/python/cpython/pull/24564 (the same fix in CPython 3.10) |
| 657 | + // - https://github.com/PyO3/pyo3/issues/3370 |
| 658 | + let builtins_s = crate::intern!(self, "__builtins__"); |
| 659 | + let has_builtins = globals.contains(builtins_s)?; |
| 660 | + if !has_builtins { |
| 661 | + crate::sync::with_critical_section(globals, || { |
| 662 | + // check if another thread set __builtins__ while this thread was blocked on the critical section |
| 663 | + let has_builtins = globals.contains(builtins_s)?; |
| 664 | + if !has_builtins { |
| 665 | + // Inherit current builtins. |
| 666 | + let builtins = unsafe { ffi::PyEval_GetBuiltins() }; |
| 667 | + |
| 668 | + // `PyDict_SetItem` doesn't take ownership of `builtins`, but `PyEval_GetBuiltins` |
| 669 | + // seems to return a borrowed reference, so no leak here. |
| 670 | + if unsafe { |
| 671 | + ffi::PyDict_SetItem(globals.as_ptr(), builtins_s.as_ptr(), builtins) |
| 672 | + } == -1 |
| 673 | + { |
| 674 | + return Err(PyErr::fetch(self)); |
| 675 | + } |
674 | 676 | }
|
675 |
| - } |
| 677 | + Ok(()) |
| 678 | + })?; |
676 | 679 | }
|
677 | 680 |
|
678 | 681 | let code_obj = unsafe {
|
@@ -1018,4 +1021,46 @@ mod tests {
|
1018 | 1021 | assert!(matches!(namespace.get_item("__builtins__"), Ok(Some(..))));
|
1019 | 1022 | })
|
1020 | 1023 | }
|
| 1024 | + |
| 1025 | + #[cfg(feature = "macros")] |
| 1026 | + #[test] |
| 1027 | + fn test_py_run_inserts_globals_2() { |
| 1028 | + #[crate::pyclass(crate = "crate")] |
| 1029 | + #[derive(Clone)] |
| 1030 | + struct CodeRunner { |
| 1031 | + code: CString, |
| 1032 | + } |
| 1033 | + |
| 1034 | + impl CodeRunner { |
| 1035 | + fn reproducer(&mut self, py: Python<'_>) -> PyResult<()> { |
| 1036 | + let variables = PyDict::new(py); |
| 1037 | + variables.set_item("cls", Py::new(py, self.clone())?)?; |
| 1038 | + |
| 1039 | + py.run(self.code.as_c_str(), Some(&variables), None)?; |
| 1040 | + Ok(()) |
| 1041 | + } |
| 1042 | + } |
| 1043 | + |
| 1044 | + #[crate::pymethods(crate = "crate")] |
| 1045 | + impl CodeRunner { |
| 1046 | + fn func(&mut self, py: Python<'_>) -> PyResult<()> { |
| 1047 | + py.import("math")?; |
| 1048 | + Ok(()) |
| 1049 | + } |
| 1050 | + } |
| 1051 | + |
| 1052 | + let mut runner = CodeRunner { |
| 1053 | + code: CString::new( |
| 1054 | + r#" |
| 1055 | +cls.func() |
| 1056 | +"# |
| 1057 | + .to_string(), |
| 1058 | + ) |
| 1059 | + .unwrap(), |
| 1060 | + }; |
| 1061 | + |
| 1062 | + Python::with_gil(|py| { |
| 1063 | + runner.reproducer(py).unwrap(); |
| 1064 | + }); |
| 1065 | + } |
1021 | 1066 | }
|
0 commit comments