From 26ccc1ab378be67212720b0defa6d63f23db913a Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Mon, 15 Nov 2021 15:45:01 +0000 Subject: [PATCH] macros: fix panic in __get__ implementation --- CHANGELOG.md | 1 + pyo3-macros-backend/src/pymethod.rs | 23 ++++++++++++++++++++--- tests/test_proto_methods.rs | 18 +++++++++++++++--- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b7bb34b9db..2a416212b92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix use of `catch_unwind` in `allow_threads` which can cause fatal crashes. [#1989](https://github.com/PyO3/pyo3/pull/1989) - Fix build failure on PyPy when abi3 features are activated. [#1991](https://github.com/PyO3/pyo3/pull/1991) - Fix mingw platform detection. [#1993](https://github.com/PyO3/pyo3/pull/1993) +- Fix panic in `__get__` implementation when accessing descriptor on type object. [#1997](https://github.com/PyO3/pyo3/pull/1997) ## [0.15.0] - 2021-11-03 diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index eb8611dc961..13a49f92734 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -483,8 +483,8 @@ const __HASH__: SlotDef = SlotDef::new("Py_tp_hash", "hashfunc") const __RICHCMP__: SlotDef = SlotDef::new("Py_tp_richcompare", "richcmpfunc") .extract_error_mode(ExtractErrorMode::NotImplemented) .arguments(&[Ty::Object, Ty::CompareOp]); -const __GET__: SlotDef = - SlotDef::new("Py_tp_descr_get", "descrgetfunc").arguments(&[Ty::Object, Ty::Object]); +const __GET__: SlotDef = SlotDef::new("Py_tp_descr_get", "descrgetfunc") + .arguments(&[Ty::MaybeNullObject, Ty::MaybeNullObject]); const __ITER__: SlotDef = SlotDef::new("Py_tp_iter", "getiterfunc"); const __NEXT__: SlotDef = SlotDef::new("Py_tp_iternext", "iternextfunc").return_conversion( TokenGenerator(|| quote! { ::pyo3::class::iter::IterNextOutput::<_, _> }), @@ -606,6 +606,7 @@ fn pyproto(method_name: &str) -> Option<&'static SlotDef> { #[derive(Clone, Copy)] enum Ty { Object, + MaybeNullObject, NonNullObject, CompareOp, Int, @@ -617,7 +618,7 @@ enum Ty { impl Ty { fn ffi_type(self) -> TokenStream { match self { - Ty::Object => quote! { *mut ::pyo3::ffi::PyObject }, + Ty::Object | Ty::MaybeNullObject => quote! { *mut ::pyo3::ffi::PyObject }, Ty::NonNullObject => quote! { ::std::ptr::NonNull<::pyo3::ffi::PyObject> }, Ty::Int | Ty::CompareOp => quote! { ::std::os::raw::c_int }, Ty::PyHashT => quote! { ::pyo3::ffi::Py_hash_t }, @@ -645,6 +646,22 @@ impl Ty { ); extract_object(cls, arg.ty, ident, extract) } + Ty::MaybeNullObject => { + let extract = handle_error( + extract_error_mode, + py, + quote! { + #py.from_borrowed_ptr::<::pyo3::PyAny>( + if #ident.is_null() { + ::pyo3::ffi::Py_None() + } else { + #ident + } + ).extract() + }, + ); + extract_object(cls, arg.ty, ident, extract) + } Ty::NonNullObject => { let extract = handle_error( extract_error_mode, diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index 0b6c30d43af..ad1ce58fd48 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -548,11 +548,23 @@ fn descr_getset() { r#" class Class: counter = Counter() + +# access via type +counter = Class.counter +assert counter.count == 1 + +# access with instance directly +assert Counter.__get__(counter, Class()).count == 2 + +# access via instance c = Class() -c.counter # count += 1 -assert c.counter.count == 2 -c.counter = Counter() assert c.counter.count == 3 + +# __set__ +c.counter = Counter() +assert c.counter.count == 4 + +# __delete__ del c.counter assert c.counter.count == 1 "#