PyO3/pyo3 · error
Cannot get `__mro__` from object.
Error message
Cannot get `__mro__` from object.
What it means
pyo3's `PyTypeMethods::mro()` retrieves the type's method resolution order. Under the limited API or PyPy, where `tp_mro` is not directly accessible, it falls back to reading the `__mro__` attribute via `getattr` and panics with this message if that attribute cannot be fetched. This happens when the object is not a real Python type exposing `__mro__`.
Source
Thrown at src/types/typeobject.rs:221
Ok(result == 1)
}
/// Checks whether `self` is a subclass of type `T`.
///
/// Equivalent to the Python expression `issubclass(self, T)`, if the type
/// `T` is known at compile time.
fn is_subclass_of<T>(&self) -> PyResult<bool>
where
T: PyTypeInfo,
{
self.is_subclass(&T::type_object(self.py()))
}
fn mro(&self) -> Bound<'py, PyTuple> {
#[cfg(any(Py_LIMITED_API, PyPy))]
let mro = self
.getattr(intern!(self.py(), "__mro__"))
.expect("Cannot get `__mro__` from object.")
.extract()
.expect("Unexpected type in `__mro__` attribute.");
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
let mro = unsafe {
use crate::ffi_ptr_ext::FfiPtrExt;
(*self.as_type_ptr())
.tp_mro
.assume_borrowed(self.py())
.to_owned()
.cast_into_unchecked()
};
mro
}
fn bases(&self) -> Bound<'py, PyTuple> {
#[cfg(any(Py_LIMITED_API, PyPy))]View on GitHub (pinned to ac9b6899d3)
Solutions
- Ensure the object is a genuine Python type (`type(x)`, heap type, etc.) before calling `.mro()`
- Guard with a check that `__mro__` exists, or handle it via `getattr_opt`-style lookup instead
- Build against CPython without Py_LIMITED_API to use the direct `tp_mro` path
Example fix
// before
let mro = ty.mro(); // panics if __mro__ missing under limited API/PyPy
// after
let mro = ty.getattr("__mro__").ok()
.map(|a| a.extract::<Bound<PyTuple>>().expect("__mro__ not a tuple")); Defensive patterns
Strategy: type-guard
Validate before calling
// Python-side or Rust-side check before calling mro()
if !obj.is_instance_of::<crate::types::PyType>() {
return Err("mro() requires a real Python type");
} Type guard
fn is_real_type(obj: &Bound<'_, PyAny>) -> bool {
obj.downcast::<crate::types::PyType>().is_ok()
} Try / catch
// use non-panicking access instead
let mro = ty.getattr("__mro__")
.and_then(|a| a.downcast_into::<PyTuple>())
.map_err(|e| format!("no usable __mro__: {e}"))?; Prevention
- Only call `.mro()` on objects obtained via `PyType` / `type(ptr)` paths
- Test abi3/PyPy configurations in CI — attribute fallbacks only run there
- Prefer `downcast`/`getattr` APIs over `.expect`-based accessors in user code
When it happens
Trigger: Calling `.mro()` on a `PyTypeMethods` receiver built from a type pointer that has no `__mro__` attribute, while compiled with `Py_LIMITED_API` or on PyPy (the `tp_mro` fast path is cfg'd out).
Common situations: Running under PyPy or a limited-API/abi3 build where code that worked on CPython (direct `tp_mro` access) now goes through `getattr("__mro__")` and the wrapped object is not a proper type object.
Related errors
- Unexpected type in `__mro__` attribute.
- Cannot get `__bases__` from object.
- Unexpected type in `__bases__` attribute.
- Neither abi3 or abi3t features are enabled
- Cannot target an abi3t version below {MINIMUM_SUPPORTED_VERS
AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05).
Data as JSON: /api/errors/aa25567cde8f60ad.
Report an issue: GitHub.