Skip to content

Commit 2042b4c

Browse files
committed
fix deadlock when initializing enum via into_pyobject() (#5928)
* fix deadlock when initializing enum via `into_pyobject()` * newsfragment * fix `unreachable_code` failure
1 parent 0157247 commit 2042b4c

3 files changed

Lines changed: 30 additions & 4 deletions

File tree

newsfragments/5928.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix deadlock in `.into_pyobject()` implementation for C-like `#[pyclass]` enums.

pyo3-macros-backend/src/pyclass.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,10 +1143,15 @@ fn impl_simple_enum(
11431143
Self::#variant_idents => #i,
11441144
)*
11451145
};
1146-
#[allow(unreachable_code)]
1147-
SINGLETON[idx].get_or_try_init(py, || {
1148-
#pyo3_path::Py::new(py, self)
1149-
}).map(|obj| ::std::clone::Clone::clone(obj.bind(py)))
1146+
#[allow(unreachable_code)] // May happen if all variants are disabled by cfg attributes
1147+
{
1148+
// Ensure that the type object is initialized before we attempt to initialize any of the singleton instances,
1149+
// to avoid deadlocking.
1150+
<Self as #pyo3_path::impl_::pyclass::PyClassImpl>::lazy_type_object().get_or_try_init(py)?;
1151+
SINGLETON[idx].get_or_try_init(py, || {
1152+
#pyo3_path::Py::new(py, self)
1153+
}).map(|obj| ::std::clone::Clone::clone(obj.bind(py)))
1154+
}
11501155
}
11511156
}
11521157
}

tests/test_enum.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,26 @@ fn test_enum_class_attr() {
2222
})
2323
}
2424

25+
#[test]
26+
fn test_enum_intopyobject() {
27+
// Variant of the above that goes via `.into_pyobject()`
28+
// - regression test for https://github.com/PyO3/pyo3/issues/5927
29+
//
30+
#[pyclass(eq, eq_int, from_py_object)]
31+
#[derive(Debug, PartialEq, Eq, Clone)]
32+
pub enum MyEnum {
33+
A,
34+
}
35+
36+
Python::attach(|py| {
37+
let var = MyEnum::A.into_pyobject(py).unwrap();
38+
// NB important to call this after `into_pyobject` - this ensures
39+
// that `.into_pyobject()` has to lazily initialize the enum type object
40+
let my_enum = py.get_type::<MyEnum>();
41+
py_assert!(py, my_enum var, "my_enum.A == var");
42+
});
43+
}
44+
2545
#[test]
2646
fn test_enum_eq_enum() {
2747
Python::attach(|py| {

0 commit comments

Comments
 (0)