diff --git a/pyo3-derive-backend/src/method.rs b/pyo3-derive-backend/src/method.rs
index 20b06c61195..aa02a90be97 100644
--- a/pyo3-derive-backend/src/method.rs
+++ b/pyo3-derive-backend/src/method.rs
@@ -63,7 +63,7 @@ impl<'a> FnSpec<'a> {
         let is_mut = self
             .self_
             .expect("impl_borrow_self is called for non-self fn");
-        crate::utils::borrow_self(is_mut, true)
+        crate::utils::borrow_self(is_mut)
     }
 
     /// Parser function signature and function attributes
diff --git a/pyo3-derive-backend/src/module.rs b/pyo3-derive-backend/src/module.rs
index bd9d601adee..d6180c5b230 100644
--- a/pyo3-derive-backend/src/module.rs
+++ b/pyo3-derive-backend/src/module.rs
@@ -221,13 +221,15 @@ fn function_c_wrapper(name: &Ident, spec: &method::FnSpec<'_>) -> TokenStream {
             const _LOCATION: &'static str = concat!(stringify!(#name), "()");
 
             let _py = pyo3::Python::assume_gil_acquired();
-            let _pool = pyo3::GILPool::new(_py);
-            let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
-            let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
+            pyo3::run_callback(_py, || {
+                let _pool = pyo3::GILPool::new(_py);
+                let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
+                let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
 
-            #body
+                #body
 
-            pyo3::callback::cb_obj_convert(_py, _result)
+                pyo3::callback::convert(_py, _result)
+            })
         }
     }
 }
diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs
index d590d2b2752..729384c0e98 100644
--- a/pyo3-derive-backend/src/pymethod.rs
+++ b/pyo3-derive-backend/src/pymethod.rs
@@ -83,10 +83,7 @@ pub fn impl_wrap_pyslf(
     };
     let slf = quote! {
         let _cell = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
-        let _slf: #self_ty = match std::convert::TryFrom::try_from(_cell) {
-            Ok(_slf) => _slf,
-            Err(e) => return pyo3::PyErr::from(e).restore_and_null(_py),
-        };
+        let _slf: #self_ty = std::convert::TryFrom::try_from(_cell)?;
     };
     impl_wrap_common(cls, spec, noargs, slf, body)
 }
@@ -109,13 +106,11 @@ fn impl_wrap_common(
                 const _LOCATION: &'static str = concat!(
                     stringify!(#cls), ".", stringify!(#python_name), "()");
                 let _py = pyo3::Python::assume_gil_acquired();
-                let _pool = pyo3::GILPool::new(_py);
-                #slf
-                let _result = {
-                    pyo3::derive_utils::IntoPyResult::into_py_result(#body)
-                };
-
-                pyo3::callback::cb_obj_convert(_py, _result)
+                pyo3::run_callback(_py, || {
+                    let _pool = pyo3::GILPool::new(_py);
+                    #slf
+                    pyo3::callback::convert(_py, #body)
+                })
             }
         }
     } else {
@@ -130,14 +125,16 @@ fn impl_wrap_common(
                 const _LOCATION: &'static str = concat!(
                     stringify!(#cls), ".", stringify!(#python_name), "()");
                 let _py = pyo3::Python::assume_gil_acquired();
-                let _pool = pyo3::GILPool::new(_py);
-                #slf
-                let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
-                let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
+                pyo3::run_callback(_py, || {
+                    let _pool = pyo3::GILPool::new(_py);
+                    #slf
+                    let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
+                    let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
 
-                #body
+                    #body
 
-                pyo3::callback::cb_obj_convert(_py, _result)
+                    pyo3::callback::convert(_py, _result)
+                })
             }
         }
     }
@@ -159,15 +156,17 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
         {
             const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
             let _py = pyo3::Python::assume_gil_acquired();
-            let _pool = pyo3::GILPool::new(_py);
-            let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
-            #borrow_self
-            let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
-            let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
+            pyo3::run_callback(_py, || {
+                let _pool = pyo3::GILPool::new(_py);
+                let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
+                #borrow_self
+                let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
+                let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
 
-            #body
+                #body
 
-            pyo3::callback::cb_obj_convert(_py, _result)
+                pyo3::callback::convert(_py, _result)
+            })
         }
     }
 }
@@ -195,16 +194,16 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
 
             const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
             let _py = pyo3::Python::assume_gil_acquired();
-            let _pool = pyo3::GILPool::new(_py);
-            let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
-            let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
+            pyo3::run_callback(_py, || {
+                let _pool = pyo3::GILPool::new(_py);
+                let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
+                let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
 
-            #body
+                #body
 
-            match _result.and_then(|init| pyo3::PyClassInitializer::from(init).create_cell(_py)) {
-                Ok(slf) => slf as _,
-                Err(e) => e.restore_and_null(_py),
-            }
+                let cell = pyo3::PyClassInitializer::from(_result?).create_cell(_py)?;
+                Ok(cell as *mut pyo3::ffi::PyObject)
+            })
         }
     }
 }
@@ -227,14 +226,16 @@ pub fn impl_wrap_class(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
         {
             const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
             let _py = pyo3::Python::assume_gil_acquired();
-            let _pool = pyo3::GILPool::new(_py);
-            let _cls = pyo3::types::PyType::from_type_ptr(_py, _cls as *mut pyo3::ffi::PyTypeObject);
-            let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
-            let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
+            pyo3::run_callback(_py, || {
+                let _pool = pyo3::GILPool::new(_py);
+                let _cls = pyo3::types::PyType::from_type_ptr(_py, _cls as *mut pyo3::ffi::PyTypeObject);
+                let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
+                let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
 
-            #body
+                #body
 
-            pyo3::callback::cb_obj_convert(_py, _result)
+                pyo3::callback::convert(_py, _result)
+            })
         }
     }
 }
@@ -257,13 +258,15 @@ pub fn impl_wrap_static(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
         {
             const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
             let _py = pyo3::Python::assume_gil_acquired();
-            let _pool = pyo3::GILPool::new(_py);
-            let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
-            let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
+            pyo3::run_callback(_py, || {
+                let _pool = pyo3::GILPool::new(_py);
+                let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
+                let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
 
-            #body
+                #body
 
-            pyo3::callback::cb_obj_convert(_py, _result)
+                pyo3::callback::convert(_py, _result)
+            })
         }
     }
 }
@@ -305,7 +308,7 @@ pub(crate) fn impl_wrap_getter(
         PropertyType::Function(spec) => (spec.python_name.clone(), impl_call_getter(&spec)?),
     };
 
-    let borrow_self = crate::utils::borrow_self(false, true);
+    let borrow_self = crate::utils::borrow_self(false);
     Ok(quote! {
         unsafe extern "C" fn __wrap(
             _slf: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut pyo3::ffi::PyObject
@@ -313,16 +316,12 @@ pub(crate) fn impl_wrap_getter(
             const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
 
             let _py = pyo3::Python::assume_gil_acquired();
-            let _pool = pyo3::GILPool::new(_py);
-            let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
-            #borrow_self
-
-            let result = pyo3::derive_utils::IntoPyResult::into_py_result(#getter_impl);
-
-            match result {
-                Ok(val) => pyo3::IntoPyPointer::into_ptr(pyo3::IntoPy::<PyObject>::into_py(val, _py)),
-                Err(e) => e.restore_and_null(_py),
-            }
+            pyo3::run_callback(_py, || {
+                let _pool = pyo3::GILPool::new(_py);
+                let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
+                #borrow_self
+                pyo3::callback::convert(_py, #getter_impl)
+            })
         }
     })
 }
@@ -344,9 +343,9 @@ fn impl_call_setter(spec: &FnSpec) -> syn::Result<TokenStream> {
 
     let name = &spec.name;
     let fncall = if py_arg.is_some() {
-        quote!(pyo3::derive_utils::IntoPyResult::into_py_result(_slf.#name(_py, _val)))
+        quote!(pyo3::derive_utils::IntoPyResult::into_py_result(_slf.#name(_py, _val))?;)
     } else {
-        quote!(pyo3::derive_utils::IntoPyResult::into_py_result(_slf.#name(_val)))
+        quote!(pyo3::derive_utils::IntoPyResult::into_py_result(_slf.#name(_val))?;)
     };
 
     Ok(fncall)
@@ -360,12 +359,12 @@ pub(crate) fn impl_wrap_setter(
     let (python_name, setter_impl) = match property_type {
         PropertyType::Descriptor(field) => {
             let name = field.ident.as_ref().unwrap();
-            (name.unraw(), quote!({ _slf.#name = _val; Ok(()) }))
+            (name.unraw(), quote!(_slf.#name = _val;))
         }
         PropertyType::Function(spec) => (spec.python_name.clone(), impl_call_setter(&spec)?),
     };
 
-    let borrow_self = crate::utils::borrow_self(true, false);
+    let borrow_self = crate::utils::borrow_self(true);
     Ok(quote! {
         #[allow(unused_mut)]
         unsafe extern "C" fn __wrap(
@@ -374,21 +373,14 @@ pub(crate) fn impl_wrap_setter(
         {
             const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
             let _py = pyo3::Python::assume_gil_acquired();
-            let _pool = pyo3::GILPool::new(_py);
-            let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
-            #borrow_self
-            let _value = _py.from_borrowed_ptr(_value);
-
-            let _result = match pyo3::FromPyObject::extract(_value) {
-                Ok(_val) => {
-                    #setter_impl
-                }
-                Err(e) => Err(e)
-            };
-            match _result {
-                Ok(_) => 0,
-                Err(e) => e.restore_and_minus1(_py),
-            }
+            pyo3::run_callback(_py, || {
+                let _pool = pyo3::GILPool::new(_py);
+                let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
+                #borrow_self
+                let _value = _py.from_borrowed_ptr::<pyo3::types::PyAny>(_value);
+                let _val = pyo3::FromPyObject::extract(_value)?;
+                pyo3::callback::convert(_py, {#setter_impl})
+            })
         }
     })
 }
@@ -462,22 +454,19 @@ fn impl_arg_params_(spec: &FnSpec<'_>, body: TokenStream, into_result: TokenStre
         let mut _args = _args;
         let mut _kwargs = _kwargs;
 
-        // Workaround to use the question mark operator without rewriting everything
-        let _result = (|| {
-            let (_args, _kwargs) = pyo3::derive_utils::parse_fn_args(
-                Some(_LOCATION),
-                PARAMS,
-                _args,
-                _kwargs,
-                #accept_args,
-                #accept_kwargs,
-                &mut output
-            )?;
-
-            #(#param_conversion)*
-
-            #into_result(#body)
-        })();
+        let (_args, _kwargs) = pyo3::derive_utils::parse_fn_args(
+            Some(_LOCATION),
+            PARAMS,
+            _args,
+            _kwargs,
+            #accept_args,
+            #accept_kwargs,
+            &mut output
+        )?;
+
+        #(#param_conversion)*
+
+        let _result = #into_result(#body);
     }
 }
 
diff --git a/pyo3-derive-backend/src/utils.rs b/pyo3-derive-backend/src/utils.rs
index 32b66b25352..d051a63409e 100644
--- a/pyo3-derive-backend/src/utils.rs
+++ b/pyo3-derive-backend/src/utils.rs
@@ -4,25 +4,14 @@ use proc_macro2::TokenStream;
 use quote::quote;
 use std::fmt::Display;
 
-pub(crate) fn borrow_self(is_mut: bool, return_null: bool) -> TokenStream {
-    let ret = if return_null {
-        quote! { restore_and_null }
-    } else {
-        quote! { restore_and_minus1 }
-    };
+pub(crate) fn borrow_self(is_mut: bool) -> TokenStream {
     if is_mut {
         quote! {
-            let mut _slf = match _slf.try_borrow_mut() {
-                Ok(ref_) => ref_,
-                Err(e) => return pyo3::PyErr::from(e).#ret(_py),
-            };
+            let mut _slf = _slf.try_borrow_mut()?;
         }
     } else {
         quote! {
-            let _slf = match _slf.try_borrow() {
-                Ok(ref_) => ref_,
-                Err(e) => return pyo3::PyErr::from(e).#ret(_py),
-            };
+            let _slf = _slf.try_borrow()?;
         }
     }
 }
diff --git a/src/callback.rs b/src/callback.rs
index edeca66c2bd..68603d301f0 100644
--- a/src/callback.rs
+++ b/src/callback.rs
@@ -7,86 +7,88 @@ use crate::exceptions::OverflowError;
 use crate::ffi::{self, Py_hash_t};
 use crate::IntoPyPointer;
 use crate::{IntoPy, PyObject, Python};
+use std::isize;
 use std::os::raw::c_int;
-use std::{isize, ptr};
 
-/// Convert the result of callback function into the appropriate return value.
-///
-/// Used by PyO3 macros.
-pub trait CallbackConverter {
-    type Source;
-    type Result: Copy;
-    const ERR_VALUE: Self::Result;
+/// A type which can be the return type of a python C-API callback
+pub trait PyCallbackOutput: Copy {
+    /// The error value to return to python if the callback raised an exception
+    const ERR_VALUE: Self;
+}
 
-    fn convert(s: Self::Source, py: Python) -> Self::Result;
+impl PyCallbackOutput for *mut ffi::PyObject {
+    const ERR_VALUE: Self = std::ptr::null_mut();
+}
 
-    #[inline]
-    fn convert_result(py: Python, value: PyResult<Self::Source>) -> Self::Result {
-        match value {
-            Ok(val) => Self::convert(val, py),
-            Err(e) => {
-                e.restore(py);
-                Self::ERR_VALUE
-            }
-        }
-    }
+impl PyCallbackOutput for libc::c_int {
+    const ERR_VALUE: Self = -1;
+}
+
+impl PyCallbackOutput for ffi::Py_ssize_t {
+    const ERR_VALUE: Self = -1;
 }
 
-pub struct PyObjectCallbackConverter<T>(pub std::marker::PhantomData<T>);
+impl PyCallbackOutput for () {
+    const ERR_VALUE: Self = ();
+}
 
-impl<T> CallbackConverter for PyObjectCallbackConverter<T>
+/// Convert the result of callback function into the appropriate return value.
+pub trait IntoPyCallbackOutput<Target> {
+    fn convert(self, py: Python) -> PyResult<Target>;
+}
+
+impl<T, U> IntoPyCallbackOutput<U> for PyResult<T>
 where
-    T: IntoPy<PyObject>,
+    T: IntoPyCallbackOutput<U>,
 {
-    type Source = T;
-    type Result = *mut ffi::PyObject;
-    const ERR_VALUE: Self::Result = ptr::null_mut();
-
-    fn convert(s: Self::Source, py: Python) -> Self::Result {
-        s.into_py(py).into_ptr()
+    fn convert(self, py: Python) -> PyResult<U> {
+        self.and_then(|t| t.convert(py))
     }
 }
 
-pub struct BoolCallbackConverter;
-
-impl CallbackConverter for BoolCallbackConverter {
-    type Source = bool;
-    type Result = c_int;
-    const ERR_VALUE: Self::Result = -1;
-
-    #[inline]
-    fn convert(s: Self::Source, _py: Python) -> Self::Result {
-        s as c_int
+impl<T> IntoPyCallbackOutput<*mut ffi::PyObject> for T
+where
+    T: IntoPy<PyObject>,
+{
+    fn convert(self, py: Python) -> PyResult<*mut ffi::PyObject> {
+        Ok(self.into_py(py).into_ptr())
     }
 }
 
-pub struct LenResultConverter;
+impl IntoPyCallbackOutput<Self> for *mut ffi::PyObject {
+    fn convert(self, _: Python) -> PyResult<Self> {
+        Ok(self)
+    }
+}
 
-impl CallbackConverter for LenResultConverter {
-    type Source = usize;
-    type Result = isize;
-    const ERR_VALUE: Self::Result = -1;
+impl IntoPyCallbackOutput<libc::c_int> for () {
+    fn convert(self, _: Python) -> PyResult<libc::c_int> {
+        Ok(0)
+    }
+}
 
-    fn convert(val: Self::Source, py: Python) -> Self::Result {
-        if val <= (isize::MAX as usize) {
-            val as isize
-        } else {
-            OverflowError::py_err(()).restore(py);
-            -1
-        }
+impl IntoPyCallbackOutput<libc::c_int> for bool {
+    fn convert(self, _: Python) -> PyResult<libc::c_int> {
+        Ok(self as c_int)
     }
 }
 
-pub struct UnitCallbackConverter;
+impl IntoPyCallbackOutput<()> for () {
+    fn convert(self, _: Python) -> PyResult<()> {
+        Ok(())
+    }
+}
 
-impl CallbackConverter for UnitCallbackConverter {
-    type Source = ();
-    type Result = c_int;
-    const ERR_VALUE: Self::Result = -1;
+pub struct LenCallbackOutput(pub usize);
 
+impl IntoPyCallbackOutput<ffi::Py_ssize_t> for LenCallbackOutput {
     #[inline]
-    fn convert(_s: Self::Source, _py: Python) -> Self::Result {
-        0
+    fn convert(self, _py: Python) -> PyResult<ffi::Py_ssize_t> {
+        if self.0 <= (isize::MAX as usize) {
+            Ok(self.0 as isize)
+        } else {
+            Err(OverflowError::py_err(()))
+        }
     }
 }
 
@@ -115,50 +117,49 @@ wrapping_cast!(i32, Py_hash_t);
 wrapping_cast!(isize, Py_hash_t);
 wrapping_cast!(i64, Py_hash_t);
 
-pub struct HashConverter<T>(pub std::marker::PhantomData<T>);
+pub struct HashCallbackOutput<T>(pub T);
 
-impl<T> CallbackConverter for HashConverter<T>
+impl<T> IntoPyCallbackOutput<Py_hash_t> for HashCallbackOutput<T>
 where
     T: WrappingCastTo<Py_hash_t>,
 {
-    type Source = T;
-    type Result = Py_hash_t;
-    const ERR_VALUE: Self::Result = -1;
-
     #[inline]
-    fn convert(val: T, _py: Python) -> Py_hash_t {
-        let hash = val.wrapping_cast();
+    fn convert(self, _py: Python) -> PyResult<Py_hash_t> {
+        let hash = self.0.wrapping_cast();
         if hash == -1 {
-            -2
+            Ok(-2)
         } else {
-            hash
+            Ok(hash)
         }
     }
 }
 
-// Short hands methods for macros
+#[doc(hidden)]
 #[inline]
-pub fn cb_convert<C, T>(_c: C, py: Python, value: PyResult<T>) -> C::Result
+pub fn convert<T, U>(py: Python, value: T) -> PyResult<U>
 where
-    C: CallbackConverter<Source = T>,
+    T: IntoPyCallbackOutput<U>,
 {
-    C::convert_result(py, value)
+    value.convert(py)
 }
 
-// Same as cb_convert(PyObjectCallbackConverter<T>, py, value)
+#[doc(hidden)]
 #[inline]
-pub fn cb_obj_convert<T: IntoPy<PyObject>>(
-    py: Python,
-    value: PyResult<T>,
-) -> <PyObjectCallbackConverter<T> as CallbackConverter>::Result {
-    PyObjectCallbackConverter::<T>::convert_result(py, value)
+pub fn callback_error<T>() -> T
+where
+    T: PyCallbackOutput,
+{
+    T::ERR_VALUE
 }
 
-#[inline]
-pub unsafe fn cb_err<C>(_c: C, py: Python, err: impl Into<crate::PyErr>) -> C::Result
+#[doc(hidden)]
+pub fn run_callback<T, F>(py: Python, callback: F) -> T
 where
-    C: CallbackConverter,
+    F: FnOnce() -> PyResult<T>,
+    T: PyCallbackOutput,
 {
-    err.into().restore(py);
-    C::ERR_VALUE
+    callback().unwrap_or_else(|e| {
+        e.restore(py);
+        T::ERR_VALUE
+    })
 }
diff --git a/src/class/basic.rs b/src/class/basic.rs
index 722d5a31c69..2f1a16b59d8 100644
--- a/src/class/basic.rs
+++ b/src/class/basic.rs
@@ -8,11 +8,11 @@
 //! Parts of the documentation are copied from the respective methods from the
 //! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html)
 
-use crate::callback::{BoolCallbackConverter, HashConverter, PyObjectCallbackConverter};
+use crate::callback::HashCallbackOutput;
 use crate::class::methods::PyMethodDef;
 use crate::{
-    exceptions, ffi, FromPyObject, IntoPy, IntoPyPointer, ObjectProtocol, PyAny, PyClass, PyErr,
-    PyObject, PyResult, Python,
+    callback, exceptions, ffi, run_callback, FromPyObject, GILPool, IntoPy, ObjectProtocol, PyAny,
+    PyCell, PyClass, PyErr, PyObject, PyResult, Python,
 };
 use std::os::raw::c_int;
 
@@ -219,27 +219,23 @@ where
             T: for<'p> PyObjectGetAttrProtocol<'p>,
         {
             let py = Python::assume_gil_acquired();
-            let _pool = crate::GILPool::new(py);
-
-            // Behave like python's __getattr__ (as opposed to __getattribute__) and check
-            // for existing fields and methods first
-            let existing = ffi::PyObject_GenericGetAttr(slf, arg);
-            if existing.is_null() {
-                // PyObject_HasAttr also tries to get an object and clears the error if it fails
-                ffi::PyErr_Clear();
-            } else {
-                return existing;
-            }
-
-            let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
-            let arg = py.from_borrowed_ptr::<crate::PyAny>(arg);
-            call_ref_with_converter!(
-                slf,
-                PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData),
-                py,
-                __getattr__,
-                arg
-            )
+            run_callback(py, || {
+                let _pool = GILPool::new(py);
+
+                // Behave like python's __getattr__ (as opposed to __getattribute__) and check
+                // for existing fields and methods first
+                let existing = ffi::PyObject_GenericGetAttr(slf, arg);
+                if existing.is_null() {
+                    // PyObject_HasAttr also tries to get an object and clears the error if it fails
+                    ffi::PyErr_Clear();
+                } else {
+                    return Ok(existing);
+                }
+
+                let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
+                let arg = py.from_borrowed_ptr::<PyAny>(arg);
+                callback::convert(py, call_ref!(slf, __getattr__, arg))
+            })
         }
         Some(wrap::<T>)
     }
@@ -357,11 +353,7 @@ where
     T: for<'p> PyObjectStrProtocol<'p>,
 {
     fn tp_str() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyObjectStrProtocol,
-            T::__str__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyObjectStrProtocol, T::__str__)
     }
 }
 
@@ -381,11 +373,7 @@ where
     T: for<'p> PyObjectReprProtocol<'p>,
 {
     fn tp_repr() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyObjectReprProtocol,
-            T::__repr__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyObjectReprProtocol, T::__repr__)
     }
 }
 
@@ -447,8 +435,8 @@ where
         py_unary_func!(
             PyObjectHashProtocol,
             T::__hash__,
-            HashConverter::<isize>(std::marker::PhantomData),
-            ffi::Py_hash_t
+            ffi::Py_hash_t,
+            HashCallbackOutput
         )
     }
 }
@@ -469,12 +457,7 @@ where
     T: for<'p> PyObjectBoolProtocol<'p>,
 {
     fn nb_bool() -> Option<ffi::inquiry> {
-        py_unary_func!(
-            PyObjectBoolProtocol,
-            T::__bool__,
-            BoolCallbackConverter,
-            c_int
-        )
+        py_unary_func!(PyObjectBoolProtocol, T::__bool__, c_int)
     }
 }
 
@@ -503,26 +486,17 @@ where
             T: for<'p> PyObjectRichcmpProtocol<'p>,
         {
             let py = Python::assume_gil_acquired();
-            let _pool = crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
-            let arg = py.from_borrowed_ptr::<PyAny>(arg);
-
-            match slf.try_borrow() {
-                Ok(borrowed_slf) => {
-                    let res = match extract_op(op) {
-                        Ok(op) => match arg.extract() {
-                            Ok(arg) => borrowed_slf.__richcmp__(arg, op).into(),
-                            Err(e) => Err(e),
-                        },
-                        Err(e) => Err(e),
-                    };
-                    match res {
-                        Ok(val) => val.into_py(py).into_ptr(),
-                        Err(e) => e.restore_and_null(py),
-                    }
-                }
-                Err(e) => PyErr::from(e).restore_and_null(py),
-            }
+            run_callback(py, || {
+                let _pool = GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
+                let arg = py.from_borrowed_ptr::<PyAny>(arg);
+
+                let borrowed_slf = slf.try_borrow()?;
+                let op = extract_op(op)?;
+                let arg = arg.extract()?;
+                let result = borrowed_slf.__richcmp__(arg, op).into();
+                callback::convert(py, result)
+            })
         }
         Some(wrap::<T>)
     }
diff --git a/src/class/buffer.rs b/src/class/buffer.rs
index 54e496a11a0..2177974fb09 100644
--- a/src/class/buffer.rs
+++ b/src/class/buffer.rs
@@ -4,9 +4,9 @@
 //!
 //! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
 //! c-api
-use crate::callback::UnitCallbackConverter;
 use crate::err::PyResult;
-use crate::{ffi, PyClass, PyRefMut};
+use crate::gil::GILPool;
+use crate::{callback, ffi, run_callback, PyCell, PyClass, PyRefMut, Python};
 use std::os::raw::c_int;
 
 /// Buffer protocol interface
@@ -91,14 +91,13 @@ where
         where
             T: for<'p> PyBufferGetBufferProtocol<'p>,
         {
-            let py = crate::Python::assume_gil_acquired();
-            let _pool = crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
-            let result = slf
-                .try_borrow_mut()
-                .map_err(crate::PyErr::from)
-                .and_then(|slf_mut| T::bf_getbuffer(slf_mut, arg1, arg2).into());
-            crate::callback::cb_convert(UnitCallbackConverter, py, result)
+            let py = Python::assume_gil_acquired();
+            run_callback(py, || {
+                let _pool = GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
+                let result = T::bf_getbuffer(slf.try_borrow_mut()?, arg1, arg2).into();
+                callback::convert(py, result)
+            })
         }
         Some(wrap::<T>)
     }
@@ -127,14 +126,13 @@ where
         where
             T: for<'p> PyBufferReleaseBufferProtocol<'p>,
         {
-            let py = crate::Python::assume_gil_acquired();
-            let _pool = crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
-            let result = slf
-                .try_borrow_mut()
-                .map_err(crate::PyErr::from)
-                .and_then(|slf_mut| T::bf_releasebuffer(slf_mut, arg1).into());
-            crate::callback::cb_convert(UnitCallbackConverter, py, result);
+            let py = Python::assume_gil_acquired();
+            run_callback(py, || {
+                let _pool = GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
+                let result = T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).into();
+                crate::callback::convert(py, result)
+            })
         }
         Some(wrap::<T>)
     }
diff --git a/src/class/descr.rs b/src/class/descr.rs
index 61c2e51c0d8..fb36b72a23e 100644
--- a/src/class/descr.rs
+++ b/src/class/descr.rs
@@ -5,7 +5,6 @@
 //! [Python information](
 //! https://docs.python.org/3/reference/datamodel.html#implementing-descriptors)
 
-use crate::callback::{PyObjectCallbackConverter, UnitCallbackConverter};
 use crate::class::methods::PyMethodDef;
 use crate::err::PyResult;
 use crate::types::{PyAny, PyType};
@@ -84,11 +83,7 @@ where
     T: for<'p> PyDescrGetProtocol<'p>,
 {
     fn tp_descr_get() -> Option<ffi::descrgetfunc> {
-        py_ternary_func!(
-            PyDescrGetProtocol,
-            T::__get__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_ternary_func!(PyDescrGetProtocol, T::__get__)
     }
 }
 
@@ -108,7 +103,7 @@ where
     T: for<'p> PyDescrSetProtocol<'p>,
 {
     fn tp_descr_set() -> Option<ffi::descrsetfunc> {
-        py_ternary_func!(PyDescrSetProtocol, T::__set__, UnitCallbackConverter, c_int)
+        py_ternary_func!(PyDescrSetProtocol, T::__set__, c_int)
     }
 }
 
diff --git a/src/class/iter.rs b/src/class/iter.rs
index 8d7522e3f6d..0d011d2e529 100644
--- a/src/class/iter.rs
+++ b/src/class/iter.rs
@@ -2,10 +2,9 @@
 //! Python Iterator Interface.
 //! Trait and support implementation for implementing iterators
 
-use crate::callback::{CallbackConverter, PyObjectCallbackConverter};
+use crate::callback::IntoPyCallbackOutput;
 use crate::err::PyResult;
 use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, PyRefMut, Python};
-use std::ptr;
 
 /// Python Iterator Interface.
 ///
@@ -77,11 +76,7 @@ where
 {
     #[inline]
     fn tp_iter() -> Option<ffi::getiterfunc> {
-        py_unary_refmut_func!(
-            PyIterIterProtocol,
-            T::__iter__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_refmut_func!(PyIterIterProtocol, T::__iter__)
     }
 }
 
@@ -104,31 +99,20 @@ where
 {
     #[inline]
     fn tp_iternext() -> Option<ffi::iternextfunc> {
-        py_unary_refmut_func!(
-            PyIterNextProtocol,
-            T::__next__,
-            IterNextConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_refmut_func!(PyIterNextProtocol, T::__next__, IterNextConverter)
     }
 }
 
-struct IterNextConverter<T>(std::marker::PhantomData<T>);
+struct IterNextConverter<T>(Option<T>);
 
-impl<T> CallbackConverter for IterNextConverter<T>
+impl<T> IntoPyCallbackOutput<*mut ffi::PyObject> for IterNextConverter<T>
 where
     T: IntoPy<PyObject>,
 {
-    type Source = Option<T>;
-    type Result = *mut ffi::PyObject;
-    const ERR_VALUE: Self::Result = ptr::null_mut();
-
-    fn convert(val: Self::Source, py: Python) -> Self::Result {
-        match val {
-            Some(val) => val.into_py(py).into_ptr(),
-            None => unsafe {
-                ffi::PyErr_SetNone(ffi::PyExc_StopIteration);
-                ptr::null_mut()
-            },
+    fn convert(self, py: Python) -> PyResult<*mut ffi::PyObject> {
+        match self.0 {
+            Some(val) => Ok(val.into_py(py).into_ptr()),
+            None => Err(crate::exceptions::StopIteration::py_err(())),
         }
     }
 }
diff --git a/src/class/macros.rs b/src/class/macros.rs
index a7de4c9b60d..8d26cd0323e 100644
--- a/src/class/macros.rs
+++ b/src/class/macros.rs
@@ -3,51 +3,44 @@
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_unary_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv: expr) => {
-        py_unary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject);
-    };
-    // Use call_ref! by default
-    ($trait:ident, $class:ident :: $f:ident, $conv: expr, $ret_type:ty) => {
-        py_unary_func!(
-            $trait,
-            $class::$f,
-            $conv,
-            $ret_type,
-            call_ref_with_converter
-        );
-    };
-    ($trait: ident,
-     $class:ident :: $f:ident,
-     $conv: expr,
-     $ret_type: ty,
-     $call: ident
-    ) => {{
+    ($trait: ident, $class:ident :: $f:ident, $call:ident, $ret_type: ty  $(, $conv:expr)?) => {{
         unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> $ret_type
         where
             T: for<'p> $trait<'p>,
         {
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
-            $call!(slf, $conv, py, $f)
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
+                $crate::callback::convert(py, $call!(slf, $f)$(.map($conv))?)
+            })
         }
         Some(wrap::<$class>)
     }};
+    // Use call_ref! by default
+    ($trait:ident, $class:ident :: $f:ident, $ret_type:ty $(, $conv:expr)?) => {
+        py_unary_func!($trait, $class::$f, call_ref, $ret_type $(, $conv)?);
+    };
+    ($trait:ident, $class:ident :: $f:ident $(, $conv:expr)?) => {
+        py_unary_func!($trait, $class::$f, call_ref, *mut $crate::ffi::PyObject $(, $conv)?);
+    };
 }
 
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_unary_refmut_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
+    ($trait:ident, $class:ident :: $f:ident $(, $conv:expr)?) => {{
         unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> *mut $crate::ffi::PyObject
         where
             T: for<'p> $trait<'p>,
         {
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
-            let res = $class::$f(slf.borrow_mut()).into();
-            $crate::callback::cb_convert($conv, py, res)
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
+                let res = $class::$f(slf.borrow_mut()).into();
+                $crate::callback::convert(py, res $(.map($conv))?)
+            })
         }
         Some(wrap::<$class>)
     }};
@@ -56,51 +49,48 @@ macro_rules! py_unary_refmut_func {
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_len_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
-        unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> $crate::ffi::Py_ssize_t
-        where
-            T: for<'p> $trait<'p>,
-        {
-            let py = Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
-            let result = call_ref!(slf, $f);
-            $crate::callback::cb_convert($conv, py, result)
-        }
-        Some(wrap::<$class>)
-    }};
+    ($trait:ident, $class:ident :: $f:ident) => {
+        py_unary_func!(
+            $trait,
+            $class::$f,
+            $crate::ffi::Py_ssize_t,
+            $crate::callback::LenCallbackOutput
+        )
+    };
 }
 
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_binary_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {
-        py_binary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject)
-    };
     // Use call_ref! by default
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr, $return:ty) => {{
-        py_binary_func!($trait, $class::$f, $conv, $return, call_ref_with_converter)
-    }};
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr, $return:ty, $call:ident) => {{
+    ($trait:ident, $class:ident :: $f:ident, $return:ty, $call:ident $(, $conv:expr)?) => {{
         unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject, arg: *mut ffi::PyObject) -> $return
         where
             T: for<'p> $trait<'p>,
         {
             use $crate::ObjectProtocol;
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
-            let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg);
-            $call!(slf, $conv, py, $f, arg)
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
+                let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg);
+                $crate::callback::convert(py, $call!(slf, $f, arg)$(.map($conv))?)
+            })
         }
         Some(wrap::<$class>)
     }};
+    ($trait:ident, $class:ident :: $f:ident, $return:ty $(, $conv:expr)?) => {
+        py_binary_func!($trait, $class::$f, $return, call_ref $(, $conv)?)
+    };
+    ($trait:ident, $class:ident :: $f:ident $(, $conv:expr)?) => {
+        py_binary_func!($trait, $class::$f, *mut $crate::ffi::PyObject $(, $conv)?)
+    };
 }
 
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_binary_num_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
+    ($trait:ident, $class:ident :: $f:ident) => {{
         unsafe extern "C" fn wrap<T>(
             lhs: *mut ffi::PyObject,
             rhs: *mut ffi::PyObject,
@@ -110,18 +100,14 @@ macro_rules! py_binary_num_func {
         {
             use $crate::ObjectProtocol;
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let lhs = py.from_borrowed_ptr::<$crate::PyAny>(lhs);
-            let rhs = py.from_borrowed_ptr::<$crate::PyAny>(rhs);
-
-            let result = match lhs.extract() {
-                Ok(lhs) => match rhs.extract() {
-                    Ok(rhs) => $class::$f(lhs, rhs).into(),
-                    Err(e) => Err(e.into()),
-                },
-                Err(e) => Err(e.into()),
-            };
-            $crate::callback::cb_convert($conv, py, result)
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let lhs = py.from_borrowed_ptr::<$crate::PyAny>(lhs);
+                let rhs = py.from_borrowed_ptr::<$crate::PyAny>(rhs);
+
+                let result = $class::$f(lhs.extract()?, rhs.extract()?).into();
+                $crate::callback::convert(py, result)
+            })
         }
         Some(wrap::<$class>)
     }};
@@ -130,7 +116,7 @@ macro_rules! py_binary_num_func {
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_binary_reverse_num_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
+    ($trait:ident, $class:ident :: $f:ident) => {{
         unsafe extern "C" fn wrap<T>(
             lhs: *mut ffi::PyObject,
             rhs: *mut ffi::PyObject,
@@ -140,11 +126,16 @@ macro_rules! py_binary_reverse_num_func {
         {
             use $crate::ObjectProtocol;
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            // Swap lhs <-> rhs
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(rhs);
-            let arg = py.from_borrowed_ptr::<$crate::PyAny>(lhs);
-            call_ref_with_converter!(slf, $conv, py, $f, arg)
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                // Swap lhs <-> rhs
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(rhs);
+                let arg = py.from_borrowed_ptr::<$crate::PyAny>(lhs);
+                $crate::callback::convert(
+                    py,
+                    $class::$f(&*slf.try_borrow()?, arg.extract()?).into(),
+                )
+            })
         }
         Some(wrap::<$class>)
     }};
@@ -165,17 +156,14 @@ macro_rules! py_binary_self_func {
             use $crate::ObjectProtocol;
 
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf_ = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
-            let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg);
-            let result = call_mut!(slf_, $f, arg);
-            match result {
-                Ok(_) => {
-                    ffi::Py_INCREF(slf);
-                    slf
-                }
-                Err(e) => e.restore_and_null(py),
-            }
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf_ = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
+                let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg);
+                call_mut!(slf_, $f, arg)?;
+                ffi::Py_INCREF(slf);
+                Ok(slf)
+            })
         }
         Some(wrap::<$class>)
     }};
@@ -185,15 +173,10 @@ macro_rules! py_binary_self_func {
 #[doc(hidden)]
 macro_rules! py_ssizearg_func {
     // Use call_ref! by default
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {
-        py_ssizearg_func!(
-            $trait,
-            $class::$f,
-            $conv,
-            call_ref_with_converter
-        )
+    ($trait:ident, $class:ident :: $f:ident) => {
+        py_ssizearg_func!($trait, $class::$f, call_ref)
     };
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr, $call:ident) => {{
+    ($trait:ident, $class:ident :: $f:ident, $call:ident) => {{
         unsafe extern "C" fn wrap<T>(
             slf: *mut ffi::PyObject,
             arg: $crate::ffi::Py_ssize_t,
@@ -202,9 +185,11 @@ macro_rules! py_ssizearg_func {
             T: for<'p> $trait<'p>,
         {
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
-            $call!(slf, $conv, py, $f ;arg.into())
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
+                $crate::callback::convert(py, $call!(slf, $f; arg.into()))
+            })
         }
         Some(wrap::<$class>)
     }};
@@ -213,10 +198,7 @@ macro_rules! py_ssizearg_func {
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_ternary_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {
-        py_ternary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject);
-    };
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr, $return_type:ty) => {{
+    ($trait:ident, $class:ident :: $f:ident, $return_type:ty) => {{
         unsafe extern "C" fn wrap<T>(
             slf: *mut $crate::ffi::PyObject,
             arg1: *mut $crate::ffi::PyObject,
@@ -228,22 +210,31 @@ macro_rules! py_ternary_func {
             use $crate::ObjectProtocol;
 
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
-            let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1);
-            let arg2 = py.from_borrowed_ptr::<$crate::PyAny>(arg2);
-
-            call_ref_with_converter!(slf, $conv, py, $f, arg1, arg2)
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
+                let arg1 = py
+                    .from_borrowed_ptr::<$crate::types::PyAny>(arg1)
+                    .extract()?;
+                let arg2 = py
+                    .from_borrowed_ptr::<$crate::types::PyAny>(arg2)
+                    .extract()?;
+
+                $crate::callback::convert(py, slf.try_borrow()?.$f(arg1, arg2).into())
+            })
         }
 
         Some(wrap::<T>)
     }};
+    ($trait:ident, $class:ident :: $f:ident) => {
+        py_ternary_func!($trait, $class::$f, *mut $crate::ffi::PyObject);
+    };
 }
 
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_ternary_num_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
+    ($trait:ident, $class:ident :: $f:ident) => {{
         unsafe extern "C" fn wrap<T>(
             arg1: *mut $crate::ffi::PyObject,
             arg2: *mut $crate::ffi::PyObject,
@@ -255,22 +246,21 @@ macro_rules! py_ternary_num_func {
             use $crate::ObjectProtocol;
 
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1);
-            let arg2 = py.from_borrowed_ptr::<$crate::PyAny>(arg2);
-            let arg3 = py.from_borrowed_ptr::<$crate::PyAny>(arg3);
-
-            let result = match arg1.extract() {
-                Ok(arg1) => match arg2.extract() {
-                    Ok(arg2) => match arg3.extract() {
-                        Ok(arg3) => $class::$f(arg1, arg2, arg3).into(),
-                        Err(e) => Err(e.into()),
-                    },
-                    Err(e) => Err(e.into()),
-                },
-                Err(e) => Err(e.into()),
-            };
-            $crate::callback::cb_convert($conv, py, result)
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let arg1 = py
+                    .from_borrowed_ptr::<$crate::types::PyAny>(arg1)
+                    .extract()?;
+                let arg2 = py
+                    .from_borrowed_ptr::<$crate::types::PyAny>(arg2)
+                    .extract()?;
+                let arg3 = py
+                    .from_borrowed_ptr::<$crate::types::PyAny>(arg3)
+                    .extract()?;
+
+                let result = $class::$f(arg1, arg2, arg3).into();
+                $crate::callback::convert(py, result)
+            })
         }
 
         Some(wrap::<T>)
@@ -280,7 +270,7 @@ macro_rules! py_ternary_num_func {
 #[macro_export]
 #[doc(hidden)]
 macro_rules! py_ternary_reverse_num_func {
-    ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
+    ($trait:ident, $class:ident :: $f:ident) => {{
         unsafe extern "C" fn wrap<T>(
             arg1: *mut $crate::ffi::PyObject,
             arg2: *mut $crate::ffi::PyObject,
@@ -291,12 +281,16 @@ macro_rules! py_ternary_reverse_num_func {
         {
             use $crate::ObjectProtocol;
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            // Swap lhs <-> rhs
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(arg2);
-            let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1);
-            let arg2 = py.from_borrowed_ptr::<$crate::PyAny>(arg3);
-            call_ref_with_converter!(slf, $conv, py, $f, arg1, arg2)
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                // Swap lhs <-> rhs
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(arg2);
+                let slf = slf.try_borrow()?;
+                let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1);
+                let arg2 = py.from_borrowed_ptr::<$crate::PyAny>(arg3);
+                let result = $class::$f(&*slf, arg1.extract()?, arg2.extract()?).into();
+                $crate::callback::convert(py, result)
+            })
         }
         Some(wrap::<$class>)
     }};
@@ -319,17 +313,14 @@ macro_rules! py_dummy_ternary_self_func {
             use $crate::ObjectProtocol;
 
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf_ = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
-            let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg1);
-            let result = call_mut!(slf_, $f, arg);
-            match result {
-                Ok(_) => {
-                    ffi::Py_INCREF(slf);
-                    slf
-                }
-                Err(e) => e.restore_and_null(py),
-            }
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf_cell = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
+                let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1);
+                call_mut!(slf_cell, $f, arg1)?;
+                ffi::Py_INCREF(slf);
+                Ok(slf)
+            })
         }
         Some(wrap::<$class>)
     }};
@@ -348,25 +339,23 @@ macro_rules! py_func_set {
             use $crate::ObjectProtocol;
 
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
-
-            let result = if value.is_null() {
-                Err($crate::PyErr::new::<exceptions::NotImplementedError, _>(
-                    format!(
-                        "Subscript deletion not supported by {:?}",
-                        stringify!($generic)
-                    ),
-                ))
-            } else {
-                let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
-                let value = py.from_borrowed_ptr::<$crate::PyAny>(value);
-                call_mut!(slf, $fn_set, name, value)
-            };
-            match result {
-                Ok(_) => 0,
-                Err(e) => e.restore_and_minus1(py),
-            }
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
+
+                if value.is_null() {
+                    Err($crate::PyErr::new::<exceptions::NotImplementedError, _>(
+                        format!(
+                            "Subscript deletion not supported by {:?}",
+                            stringify!($generic)
+                        ),
+                    ))
+                } else {
+                    let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
+                    let value = py.from_borrowed_ptr::<$crate::PyAny>(value);
+                    crate::callback::convert(py, call_mut!(slf, $fn_set, name, value))
+                }
+            })
         }
 
         Some(wrap::<$generic>)
@@ -386,22 +375,21 @@ macro_rules! py_func_del {
             use $crate::ObjectProtocol;
 
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-
-            let result = if value.is_null() {
-                let slf = py.from_borrowed_ptr::<$crate::PyCell<U>>(slf);
-                let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
-
-                call_mut!(slf, $fn_del, name)
-            } else {
-                Err(PyErr::new::<exceptions::NotImplementedError, _>(
-                    "Subscript assignment not supported",
-                ))
-            };
-            match result {
-                Ok(_) => 0,
-                Err(e) => e.restore_and_minus1(py),
-            }
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+
+                if value.is_null() {
+                    let slf = py.from_borrowed_ptr::<$crate::PyCell<U>>(slf);
+                    let name = py
+                        .from_borrowed_ptr::<$crate::types::PyAny>(name)
+                        .extract()?;
+                    $crate::callback::convert(py, slf.try_borrow_mut()?.$fn_del(name).into())
+                } else {
+                    Err(PyErr::new::<exceptions::NotImplementedError, _>(
+                        "Subscript assignment not supported",
+                    ))
+                }
+            })
         }
 
         Some(wrap::<$generic>)
@@ -421,71 +409,43 @@ macro_rules! py_func_set_del {
             use $crate::ObjectProtocol;
 
             let py = $crate::Python::assume_gil_acquired();
-            let _pool = $crate::GILPool::new(py);
-            let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
-            let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
-
-            let result = if value.is_null() {
-                call_mut!(slf, $fn_del, name)
-            } else {
-                let value = py.from_borrowed_ptr::<$crate::PyAny>(value);
-                call_mut!(slf, $fn_set, name, value)
-            };
-            match result {
-                Ok(_) => 0,
-                Err(e) => e.restore_and_minus1(py),
-            }
+            $crate::run_callback(py, || {
+                let _pool = $crate::GILPool::new(py);
+                let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
+                let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
+
+                let result = if value.is_null() {
+                    call_mut!(slf, $fn_del, name)
+                } else {
+                    let value = py.from_borrowed_ptr::<$crate::PyAny>(value);
+                    call_mut!(slf, $fn_set, name, value)
+                };
+                $crate::callback::convert(py, result)
+            })
         }
         Some(wrap::<$generic>)
     }};
 }
 
 macro_rules! _call_impl {
-    ($slf: ident, $fn: ident $(; $args: expr)*) => { $slf.$fn($($args,)*).into() };
-    ($slf: ident, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => {
-        match $raw_arg.extract() {
-            Ok(arg) => _call_impl!($slf, $fn $(,$raw_args)* $(;$args)* ;arg),
-            Err(e) => Err(e.into()),
-        }
+    ($slf: expr, $fn: ident $(; $args: expr)*) => {
+        $slf.$fn($($args,)*).into()
+    };
+    ($slf: expr, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => {
+        _call_impl!($slf, $fn $(,$raw_args)* $(;$args)* ;$raw_arg.extract()?)
     };
 }
 
 /// Call `slf.try_borrow()?.$fn(...)`
 macro_rules! call_ref {
     ($slf: expr, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => {
-        match $slf.try_borrow() {
-            Ok(slf) => _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*),
-            Err(e) => Err(e.into()),
-        }
-    };
-}
-
-/// Call `slf.try_borrow()?.$fn(...)` and returns the result using the given CallbackConverter
-macro_rules! call_ref_with_converter {
-    ($slf: expr, $conv: expr, $py: ident, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => {
-        match $slf.try_borrow() {
-            Ok(slf) => $crate::callback::cb_convert($conv, $py, _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*)),
-            Err(e) => $crate::callback::cb_err($conv, $py, e)
-        }
+        _call_impl!($slf.try_borrow()?, $fn $(,$raw_args)* $(;$args)*)
     };
 }
 
 /// Call `slf.try_borrow_mut()?.$fn(...)`
 macro_rules! call_mut {
     ($slf: expr, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => {
-        match $slf.try_borrow_mut() {
-            Ok(mut slf) => _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*),
-            Err(e) => Err(e.into()),
-        }
-    };
-}
-
-/// Call `slf.try_borrow_mut()?.$fn(...)` and returns the result using the given CallbackConverter
-macro_rules! call_mut_with_converter {
-    ($slf: expr, $conv: expr, $py: ident, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => {
-        match $slf.try_borrow_mut() {
-            Ok(mut slf) => $crate::callback::cb_convert($conv, $py, _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*)),
-            Err(e) => $crate::callback::cb_err($conv, $py, e)
-        }
+        _call_impl!($slf.try_borrow_mut()?, $fn $(,$raw_args)* $(;$args)*)
     };
 }
diff --git a/src/class/mapping.rs b/src/class/mapping.rs
index 3937313461d..4d82a9dd5fc 100644
--- a/src/class/mapping.rs
+++ b/src/class/mapping.rs
@@ -3,10 +3,9 @@
 //! Python Mapping Interface
 //! Trait and support implementation for implementing mapping support
 
-use crate::callback::{LenResultConverter, PyObjectCallbackConverter};
 use crate::class::methods::PyMethodDef;
 use crate::err::{PyErr, PyResult};
-use crate::{exceptions, ffi, FromPyObject, IntoPy, PyClass, PyObject, Python};
+use crate::{exceptions, ffi, FromPyObject, IntoPy, PyClass, PyObject};
 
 /// Mapping interface
 #[allow(unused_variables)]
@@ -141,7 +140,7 @@ where
 {
     #[inline]
     fn mp_length() -> Option<ffi::lenfunc> {
-        py_len_func!(PyMappingLenProtocol, T::__len__, LenResultConverter)
+        py_len_func!(PyMappingLenProtocol, T::__len__)
     }
 }
 
@@ -164,11 +163,7 @@ where
 {
     #[inline]
     fn mp_subscript() -> Option<ffi::binaryfunc> {
-        py_binary_func!(
-            PyMappingGetItemProtocol,
-            T::__getitem__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_func!(PyMappingGetItemProtocol, T::__getitem__)
     }
 }
 
diff --git a/src/class/number.rs b/src/class/number.rs
index b845d2e0683..207a5b3a6a0 100644
--- a/src/class/number.rs
+++ b/src/class/number.rs
@@ -3,7 +3,6 @@
 //! Python Number Interface
 //! Trait and support implementation for implementing number protocol
 
-use crate::callback::PyObjectCallbackConverter;
 use crate::class::basic::PyObjectProtocolImpl;
 use crate::class::methods::PyMethodDef;
 use crate::err::PyResult;
@@ -761,11 +760,7 @@ where
     T: for<'p> PyNumberAddProtocol<'p>,
 {
     fn nb_add() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberAddProtocol,
-            T::__add__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberAddProtocol, T::__add__)
     }
 }
 
@@ -787,11 +782,7 @@ where
     T: for<'p> PyNumberSubProtocol<'p>,
 {
     fn nb_subtract() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberSubProtocol,
-            T::__sub__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberSubProtocol, T::__sub__)
     }
 }
 
@@ -813,11 +804,7 @@ where
     T: for<'p> PyNumberMulProtocol<'p>,
 {
     fn nb_multiply() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberMulProtocol,
-            T::__mul__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberMulProtocol, T::__mul__)
     }
 }
 
@@ -839,11 +826,7 @@ where
     T: for<'p> PyNumberMatmulProtocol<'p>,
 {
     fn nb_matrix_multiply() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberMatmulProtocol,
-            T::__matmul__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberMatmulProtocol, T::__matmul__)
     }
 }
 
@@ -865,11 +848,7 @@ where
     T: for<'p> PyNumberTruedivProtocol<'p>,
 {
     fn nb_true_divide() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberTruedivProtocol,
-            T::__truediv__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberTruedivProtocol, T::__truediv__)
     }
 }
 
@@ -891,11 +870,7 @@ where
     T: for<'p> PyNumberFloordivProtocol<'p>,
 {
     fn nb_floor_divide() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberFloordivProtocol,
-            T::__floordiv__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberFloordivProtocol, T::__floordiv__)
     }
 }
 
@@ -917,11 +892,7 @@ where
     T: for<'p> PyNumberModProtocol<'p>,
 {
     fn nb_remainder() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberModProtocol,
-            T::__mod__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberModProtocol, T::__mod__)
     }
 }
 
@@ -943,11 +914,7 @@ where
     T: for<'p> PyNumberDivmodProtocol<'p>,
 {
     fn nb_divmod() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberDivmodProtocol,
-            T::__divmod__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberDivmodProtocol, T::__divmod__)
     }
 }
 
@@ -969,11 +936,7 @@ where
     T: for<'p> PyNumberPowProtocol<'p>,
 {
     fn nb_power() -> Option<ffi::ternaryfunc> {
-        py_ternary_num_func!(
-            PyNumberPowProtocol,
-            T::__pow__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_ternary_num_func!(PyNumberPowProtocol, T::__pow__)
     }
 }
 
@@ -995,11 +958,7 @@ where
     T: for<'p> PyNumberLShiftProtocol<'p>,
 {
     fn nb_lshift() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberLShiftProtocol,
-            T::__lshift__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberLShiftProtocol, T::__lshift__)
     }
 }
 
@@ -1021,11 +980,7 @@ where
     T: for<'p> PyNumberRShiftProtocol<'p>,
 {
     fn nb_rshift() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberRShiftProtocol,
-            T::__rshift__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberRShiftProtocol, T::__rshift__)
     }
 }
 
@@ -1047,11 +1002,7 @@ where
     T: for<'p> PyNumberAndProtocol<'p>,
 {
     fn nb_and() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberAndProtocol,
-            T::__and__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberAndProtocol, T::__and__)
     }
 }
 
@@ -1073,11 +1024,7 @@ where
     T: for<'p> PyNumberXorProtocol<'p>,
 {
     fn nb_xor() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberXorProtocol,
-            T::__xor__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberXorProtocol, T::__xor__)
     }
 }
 
@@ -1099,11 +1046,7 @@ where
     T: for<'p> PyNumberOrProtocol<'p>,
 {
     fn nb_or() -> Option<ffi::binaryfunc> {
-        py_binary_num_func!(
-            PyNumberOrProtocol,
-            T::__or__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_num_func!(PyNumberOrProtocol, T::__or__)
     }
 }
 
@@ -1426,11 +1369,7 @@ where
     T: for<'p> PyNumberRAddProtocol<'p>,
 {
     fn nb_add_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRAddProtocol,
-            T::__radd__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRAddProtocol, T::__radd__)
     }
 }
 
@@ -1466,11 +1405,7 @@ where
     T: for<'p> PyNumberRSubProtocol<'p>,
 {
     fn nb_sub_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRSubProtocol,
-            T::__rsub__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRSubProtocol, T::__rsub__)
     }
 }
 
@@ -1506,11 +1441,7 @@ where
     T: for<'p> PyNumberRMulProtocol<'p>,
 {
     fn nb_mul_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRMulProtocol,
-            T::__rmul__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRMulProtocol, T::__rmul__)
     }
 }
 
@@ -1546,11 +1477,7 @@ where
     T: for<'p> PyNumberRMatmulProtocol<'p>,
 {
     fn nb_matmul_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRMatmulProtocol,
-            T::__rmatmul__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRMatmulProtocol, T::__rmatmul__)
     }
 }
 
@@ -1586,11 +1513,7 @@ where
     T: for<'p> PyNumberRTruedivProtocol<'p>,
 {
     fn nb_truediv_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRTruedivProtocol,
-            T::__rtruediv__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRTruedivProtocol, T::__rtruediv__)
     }
 }
 
@@ -1626,11 +1549,7 @@ where
     T: for<'p> PyNumberRFloordivProtocol<'p>,
 {
     fn nb_floordiv_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRFloordivProtocol,
-            T::__rfloordiv__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRFloordivProtocol, T::__rfloordiv__)
     }
 }
 
@@ -1666,11 +1585,7 @@ where
     T: for<'p> PyNumberRModProtocol<'p>,
 {
     fn nb_mod_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRModProtocol,
-            T::__rmod__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRModProtocol, T::__rmod__)
     }
 }
 
@@ -1706,11 +1621,7 @@ where
     T: for<'p> PyNumberRDivmodProtocol<'p>,
 {
     fn nb_divmod_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRDivmodProtocol,
-            T::__rdivmod__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRDivmodProtocol, T::__rdivmod__)
     }
 }
 
@@ -1746,11 +1657,7 @@ where
     T: for<'p> PyNumberRPowProtocol<'p>,
 {
     fn nb_pow_fallback() -> Option<ffi::ternaryfunc> {
-        py_ternary_reverse_num_func!(
-            PyNumberRPowProtocol,
-            T::__rpow__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_ternary_reverse_num_func!(PyNumberRPowProtocol, T::__rpow__)
     }
 }
 
@@ -1786,11 +1693,7 @@ where
     T: for<'p> PyNumberRLShiftProtocol<'p>,
 {
     fn nb_lshift_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRLShiftProtocol,
-            T::__rlshift__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRLShiftProtocol, T::__rlshift__)
     }
 }
 
@@ -1826,11 +1729,7 @@ where
     T: for<'p> PyNumberRRShiftProtocol<'p>,
 {
     fn nb_rshift_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRRShiftProtocol,
-            T::__rrshift__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRRShiftProtocol, T::__rrshift__)
     }
 }
 
@@ -1866,11 +1765,7 @@ where
     T: for<'p> PyNumberRAndProtocol<'p>,
 {
     fn nb_and_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRAndProtocol,
-            T::__rand__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRAndProtocol, T::__rand__)
     }
 }
 
@@ -1906,11 +1801,7 @@ where
     T: for<'p> PyNumberRXorProtocol<'p>,
 {
     fn nb_xor_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberRXorProtocol,
-            T::__rxor__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberRXorProtocol, T::__rxor__)
     }
 }
 
@@ -1946,11 +1837,7 @@ where
     T: for<'p> PyNumberROrProtocol<'p>,
 {
     fn nb_or_fallback() -> Option<ffi::binaryfunc> {
-        py_binary_reverse_num_func!(
-            PyNumberROrProtocol,
-            T::__ror__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_reverse_num_func!(PyNumberROrProtocol, T::__ror__)
     }
 }
 
@@ -1973,11 +1860,7 @@ where
 {
     #[inline]
     fn nb_negative() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyNumberNegProtocol,
-            T::__neg__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyNumberNegProtocol, T::__neg__)
     }
 }
 
@@ -1999,11 +1882,7 @@ where
     T: for<'p> PyNumberPosProtocol<'p>,
 {
     fn nb_positive() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyNumberPosProtocol,
-            T::__pos__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyNumberPosProtocol, T::__pos__)
     }
 }
 
@@ -2025,11 +1904,7 @@ where
     T: for<'p> PyNumberAbsProtocol<'p>,
 {
     fn nb_absolute() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyNumberAbsProtocol,
-            T::__abs__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyNumberAbsProtocol, T::__abs__)
     }
 }
 
@@ -2051,11 +1926,7 @@ where
     T: for<'p> PyNumberInvertProtocol<'p>,
 {
     fn nb_invert() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyNumberInvertProtocol,
-            T::__invert__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyNumberInvertProtocol, T::__invert__)
     }
 }
 
@@ -2077,11 +1948,7 @@ where
     T: for<'p> PyNumberIntProtocol<'p>,
 {
     fn nb_int() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyNumberIntProtocol,
-            T::__int__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyNumberIntProtocol, T::__int__)
     }
 }
 
@@ -2103,11 +1970,7 @@ where
     T: for<'p> PyNumberFloatProtocol<'p>,
 {
     fn nb_float() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyNumberFloatProtocol,
-            T::__float__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyNumberFloatProtocol, T::__float__)
     }
 }
 
@@ -2129,11 +1992,7 @@ where
     T: for<'p> PyNumberIndexProtocol<'p>,
 {
     fn nb_index() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyNumberIndexProtocol,
-            T::__index__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyNumberIndexProtocol, T::__index__)
     }
 }
 
diff --git a/src/class/pyasync.rs b/src/class/pyasync.rs
index 3a423ffa554..a43dc57cd85 100644
--- a/src/class/pyasync.rs
+++ b/src/class/pyasync.rs
@@ -8,7 +8,6 @@
 //! [PEP-0492](https://www.python.org/dev/peps/pep-0492/)
 //!
 
-use crate::callback::PyObjectCallbackConverter;
 use crate::class::methods::PyMethodDef;
 use crate::err::PyResult;
 use crate::{ffi, PyClass, PyObject};
@@ -150,11 +149,7 @@ where
 {
     #[inline]
     fn am_await() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyAsyncAwaitProtocol,
-            T::__await__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyAsyncAwaitProtocol, T::__await__)
     }
 }
 
@@ -177,11 +172,7 @@ where
 {
     #[inline]
     fn am_aiter() -> Option<ffi::unaryfunc> {
-        py_unary_func!(
-            PyAsyncAiterProtocol,
-            T::__aiter__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_unary_func!(PyAsyncAiterProtocol, T::__aiter__)
     }
 }
 
@@ -200,30 +191,22 @@ where
 
 mod anext {
     use super::{PyAsyncAnextProtocol, PyAsyncAnextProtocolImpl};
-    use crate::callback::CallbackConverter;
+    use crate::callback::IntoPyCallbackOutput;
+    use crate::err::PyResult;
     use crate::IntoPyPointer;
     use crate::Python;
     use crate::{ffi, IntoPy, PyObject};
-    use std::marker::PhantomData;
-    use std::ptr;
 
-    struct IterANextResultConverter<T>(PhantomData<T>);
+    struct IterANextOutput<T>(Option<T>);
 
-    impl<T> CallbackConverter for IterANextResultConverter<T>
+    impl<T> IntoPyCallbackOutput<*mut ffi::PyObject> for IterANextOutput<T>
     where
         T: IntoPy<PyObject>,
     {
-        type Source = Option<T>;
-        type Result = *mut ffi::PyObject;
-        const ERR_VALUE: Self::Result = ptr::null_mut();
-
-        fn convert(val: Self::Source, py: Python) -> Self::Result {
-            match val {
-                Some(val) => val.into_py(py).into_ptr(),
-                None => unsafe {
-                    ffi::PyErr_SetNone(ffi::PyExc_StopAsyncIteration);
-                    ptr::null_mut()
-                },
+        fn convert(self, py: Python) -> PyResult<*mut ffi::PyObject> {
+            match self.0 {
+                Some(val) => Ok(val.into_py(py).into_ptr()),
+                None => Err(crate::exceptions::StopAsyncIteration::py_err(())),
             }
         }
     }
@@ -237,9 +220,9 @@ mod anext {
             py_unary_func!(
                 PyAsyncAnextProtocol,
                 T::__anext__,
-                IterANextResultConverter::<T::Success>(std::marker::PhantomData),
+                call_mut,
                 *mut crate::ffi::PyObject,
-                call_mut_with_converter
+                IterANextOutput
             )
         }
     }
diff --git a/src/class/sequence.rs b/src/class/sequence.rs
index 498844b7bc4..eeb2360022c 100644
--- a/src/class/sequence.rs
+++ b/src/class/sequence.rs
@@ -3,10 +3,11 @@
 //! Python Sequence Interface
 //! Trait and support implementation for implementing sequence
 
-use crate::callback::{BoolCallbackConverter, LenResultConverter, PyObjectCallbackConverter};
+use crate::conversion::{FromPyObject, IntoPy};
 use crate::err::{PyErr, PyResult};
+use crate::gil::GILPool;
 use crate::objectprotocol::ObjectProtocol;
-use crate::{exceptions, ffi, FromPyObject, IntoPy, PyAny, PyClass, PyObject, Python};
+use crate::{callback, exceptions, ffi, run_callback, PyAny, PyCell, PyClass, PyObject, Python};
 use std::os::raw::c_int;
 
 /// Sequence interface
@@ -176,7 +177,7 @@ where
     T: for<'p> PySequenceLenProtocol<'p>,
 {
     fn sq_length() -> Option<ffi::lenfunc> {
-        py_len_func!(PySequenceLenProtocol, T::__len__, LenResultConverter)
+        py_len_func!(PySequenceLenProtocol, T::__len__)
     }
 }
 
@@ -198,11 +199,7 @@ where
     T: for<'p> PySequenceGetItemProtocol<'p>,
 {
     fn sq_item() -> Option<ffi::ssizeargfunc> {
-        py_ssizearg_func!(
-            PySequenceGetItemProtocol,
-            T::__getitem__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_ssizearg_func!(PySequenceGetItemProtocol, T::__getitem__)
     }
 }
 
@@ -260,31 +257,23 @@ mod sq_ass_item_impl {
                 T: for<'p> PySequenceSetItemProtocol<'p>,
             {
                 let py = Python::assume_gil_acquired();
-                let _pool = crate::GILPool::new(py);
-                let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
-
-                if value.is_null() {
-                    return PyErr::new::<exceptions::NotImplementedError, _>(format!(
-                        "Item deletion is not supported by {:?}",
-                        stringify!(T)
-                    ))
-                    .restore_and_minus1(py);
-                }
-
-                let result = match slf.try_borrow_mut() {
-                    Ok(mut slf) => {
-                        let value = py.from_borrowed_ptr::<PyAny>(value);
-                        match value.extract() {
-                            Ok(value) => slf.__setitem__(key.into(), value).into(),
-                            Err(e) => e.into(),
-                        }
+                run_callback(py, || {
+                    let _pool = GILPool::new(py);
+                    let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
+
+                    if value.is_null() {
+                        return Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
+                            "Item deletion is not supported by {:?}",
+                            stringify!(T)
+                        )));
                     }
-                    Err(e) => Err(PyErr::from(e)),
-                };
-                match result {
-                    Ok(_) => 0,
-                    Err(e) => e.restore_and_minus1(py),
-                }
+
+                    let mut slf = slf.try_borrow_mut()?;
+                    let value = py.from_borrowed_ptr::<PyAny>(value);
+                    let value = value.extract()?;
+                    let result = slf.__setitem__(key.into(), value).into();
+                    callback::convert(py, result)
+                })
             }
             Some(wrap::<T>)
         }
@@ -317,22 +306,21 @@ mod sq_ass_item_impl {
                 T: for<'p> PySequenceDelItemProtocol<'p>,
             {
                 let py = Python::assume_gil_acquired();
-                let _pool = crate::GILPool::new(py);
-                let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
-
-                let result = if value.is_null() {
-                    slf.borrow_mut().__delitem__(key.into()).into()
-                } else {
-                    Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
-                        "Item assignment not supported by {:?}",
-                        stringify!(T)
-                    )))
-                };
-
-                match result {
-                    Ok(_) => 0,
-                    Err(e) => e.restore_and_minus1(py),
-                }
+                run_callback(py, || {
+                    let _pool = GILPool::new(py);
+                    let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
+
+                    let result = if value.is_null() {
+                        slf.borrow_mut().__delitem__(key.into()).into()
+                    } else {
+                        Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
+                            "Item assignment not supported by {:?}",
+                            stringify!(T)
+                        )))
+                    };
+
+                    callback::convert(py, result)
+                })
             }
             Some(wrap::<T>)
         }
@@ -365,25 +353,20 @@ mod sq_ass_item_impl {
                 T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>,
             {
                 let py = Python::assume_gil_acquired();
-                let _pool = crate::GILPool::new(py);
-                let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
+                run_callback(py, || {
+                    let _pool = GILPool::new(py);
+                    let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
 
-                let result = if value.is_null() {
-                    call_mut!(slf, __delitem__; key.into())
-                } else {
-                    let value = py.from_borrowed_ptr::<PyAny>(value);
-                    match slf.try_borrow_mut() {
-                        Ok(mut slf_) => match value.extract() {
-                            Ok(value) => slf_.__setitem__(key.into(), value).into(),
-                            Err(e) => Err(e),
-                        },
-                        Err(e) => Err(e.into()),
-                    }
-                };
-                match result {
-                    Ok(_) => 0,
-                    Err(e) => e.restore_and_minus1(py),
-                }
+                    let result = if value.is_null() {
+                        call_mut!(slf, __delitem__; key.into())
+                    } else {
+                        let value = py.from_borrowed_ptr::<PyAny>(value);
+                        let mut slf_ = slf.try_borrow_mut()?;
+                        let value = value.extract()?;
+                        slf_.__setitem__(key.into(), value).into()
+                    };
+                    callback::convert(py, result)
+                })
             }
             Some(wrap::<T>)
         }
@@ -408,12 +391,7 @@ where
     T: for<'p> PySequenceContainsProtocol<'p>,
 {
     fn sq_contains() -> Option<ffi::objobjproc> {
-        py_binary_func!(
-            PySequenceContainsProtocol,
-            T::__contains__,
-            BoolCallbackConverter,
-            c_int
-        )
+        py_binary_func!(PySequenceContainsProtocol, T::__contains__, c_int)
     }
 }
 
@@ -435,11 +413,7 @@ where
     T: for<'p> PySequenceConcatProtocol<'p>,
 {
     fn sq_concat() -> Option<ffi::binaryfunc> {
-        py_binary_func!(
-            PySequenceConcatProtocol,
-            T::__concat__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_binary_func!(PySequenceConcatProtocol, T::__concat__)
     }
 }
 
@@ -461,11 +435,7 @@ where
     T: for<'p> PySequenceRepeatProtocol<'p>,
 {
     fn sq_repeat() -> Option<ffi::ssizeargfunc> {
-        py_ssizearg_func!(
-            PySequenceRepeatProtocol,
-            T::__repeat__,
-            PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
-        )
+        py_ssizearg_func!(PySequenceRepeatProtocol, T::__repeat__)
     }
 }
 
@@ -490,9 +460,8 @@ where
         py_binary_func!(
             PySequenceInplaceConcatProtocol,
             T::__inplace_concat__,
-            PyObjectCallbackConverter::<T>(std::marker::PhantomData),
-            *mut crate::ffi::PyObject,
-            call_mut_with_converter
+            *mut ffi::PyObject,
+            call_mut
         )
     }
 }
@@ -518,8 +487,7 @@ where
         py_ssizearg_func!(
             PySequenceInplaceRepeatProtocol,
             T::__inplace_repeat__,
-            PyObjectCallbackConverter::<T>(std::marker::PhantomData),
-            call_mut_with_converter
+            call_mut
         )
     }
 }
diff --git a/src/err.rs b/src/err.rs
index cc0dbcd7ab4..2cdfcac32c2 100644
--- a/src/err.rs
+++ b/src/err.rs
@@ -1,15 +1,12 @@
 // Copyright (c) 2017-present PyO3 Project and Contributors
 
-use crate::instance::Py;
-use crate::object::PyObject;
 use crate::type_object::PyTypeObject;
-use crate::types::{PyAny, PyType};
-use crate::AsPyPointer;
-use crate::IntoPyPointer;
-use crate::Python;
-use crate::{exceptions, IntoPy};
-use crate::{ffi, FromPy};
-use crate::{ToBorrowedObject, ToPyObject};
+use crate::types::PyType;
+use crate::{exceptions, ffi};
+use crate::{
+    AsPyPointer, FromPy, IntoPy, IntoPyPointer, Py, PyAny, PyObject, Python, ToBorrowedObject,
+    ToPyObject,
+};
 use libc::c_int;
 use std::ffi::CString;
 use std::io;
diff --git a/src/lib.rs b/src/lib.rs
index 422700ef500..81e84fa82d5 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -133,6 +133,7 @@
 //! }
 //! ```
 
+pub use crate::callback::run_callback;
 pub use crate::class::*;
 pub use crate::conversion::{
     AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto,
diff --git a/src/python.rs b/src/python.rs
index 0d9b479c679..6c4f4d89cb5 100644
--- a/src/python.rs
+++ b/src/python.rs
@@ -9,8 +9,7 @@ use crate::instance::AsPyRef;
 use crate::object::PyObject;
 use crate::type_object::{PyDowncastImpl, PyTypeInfo, PyTypeObject};
 use crate::types::{PyAny, PyDict, PyModule, PyType};
-use crate::AsPyPointer;
-use crate::{FromPyPointer, IntoPyPointer, PyTryFrom};
+use crate::{AsPyPointer, FromPyPointer, IntoPyPointer, PyTryFrom};
 use std::ffi::CString;
 use std::marker::PhantomData;
 use std::os::raw::c_int;