PyO3/pyo3 · error

Cannot get `__bases__` from object.

Error message

Cannot get `__bases__` from object.

What it means

`PyTypeMethods::bases()` returns the type's direct base classes. Under the limited API or PyPy (where `tp_bases` is not accessible), it reads the `__bases__` attribute and panics with this message if `getattr` fails — i.e. the wrapped object does not expose `__bases__`, meaning it is not a proper Python type.

Source

Thrown at src/types/typeobject.rs:242

        #[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))]
        let bases = self
            .getattr(intern!(self.py(), "__bases__"))
            .expect("Cannot get `__bases__` from object.")
            .extract()
            .expect("Unexpected type in `__bases__` attribute.");

        #[cfg(not(any(Py_LIMITED_API, PyPy)))]
        let bases = unsafe {
            use crate::ffi_ptr_ext::FfiPtrExt;
            (*self.as_type_ptr())
                .tp_bases
                .assume_borrowed(self.py())
                .to_owned()
                .cast_into_unchecked()
        };

        bases
    }
}

#[cfg(test)]

View on GitHub (pinned to ac9b6899d3)

Solutions

  1. Verify the object is a Python type (`isinstance(obj, type)`) before calling `.bases()`
  2. Use `getattr("__bases__")` with error handling instead of the panicking accessor
  3. Compile against CPython without Py_LIMITED_API to use the direct `tp_bases` field

Example fix

// before
let bases = ty.bases(); // panics if __bases__ missing under limited API/PyPy
// after
let bases = ty.getattr("__bases__").ok()
    .map(|a| a.extract::<Bound<PyTuple>>().expect("__bases__ not a tuple"));
Defensive patterns

Strategy: type-guard

Validate before calling

if !obj.is_instance_of::<crate::types::PyType>() {
    return Err("bases() requires a real Python type");
}

Type guard

fn is_real_type(obj: &Bound<'_, PyAny>) -> bool {
    obj.downcast::<crate::types::PyType>().is_ok()
}

Try / catch

let bases = ty.getattr("__bases__")
    .and_then(|a| a.downcast_into::<PyTuple>())
    .map_err(|e| format!("no usable __bases__: {e}"))?;

Prevention

When it happens

Trigger: Calling `.bases()` on a `PyTypeMethods` receiver whose underlying object lacks a `__bases__` attribute, while compiled with `Py_LIMITED_API` or running on PyPy.

Common situations: abi3/limited-API builds or PyPy runs where code passes a non-type object (e.g. an instance or a builtin wrapper) where a real class was expected.

Related errors


AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05). Data as JSON: /api/errors/45641a7b6233d5b6. Report an issue: GitHub.