PyO3/pyo3 · error

no class given for a class method

Error message

no class given for a class method

What it means

When generating code for a #[classmethod]-style method, PyO3 checks the `cls` (self) argument. With the default Checked self-conversion policy it must know the target class to emit a Bound::ref_from_ptr(...).cast::<Cls>() type check; the expect panics if no class was provided. Like error 72, this guards pyo3-macros-backend's internal API contract.

Source

Thrown at pyo3-macros-backend/src/method.rs:298

                Some(st.receiver(
                    cls.expect("no class given for Fn with a \"self\" receiver"),
                    error_mode,
                    self_conversion,
                    holders,
                    ctx,
                ))
            }
            FnType::FnClass(span) => {
                let py = syn::Ident::new("py", Span::call_site());
                let slf: Ident = syn::Ident::new("_slf", Span::call_site());
                let pyo3_path = pyo3_path.to_tokens_spanned(*span);
                let class_method_receiver = match class_method_receiver {
                    ClassMethodReceiver::Class => quote! { #slf.cast() },
                    ClassMethodReceiver::Instance => {
                        let type_check = match self_conversion.0 {
                            SelfConversionPolicyInner::Trusted => quote! {},
                            SelfConversionPolicyInner::Checked => {
                                let cls = cls.expect("no class given for a class method");
                                let type_check = error_mode.handle_error(
                                    quote_spanned! { *span =>
                                        #pyo3_path::Bound::ref_from_ptr(#py, &#slf).cast::<#cls>()
                                    },
                                    ctx,
                                );
                                quote! { #type_check; }
                            }
                        };
                        quote! {{
                            #type_check
                            #pyo3_path::ffi::Py_TYPE(#slf).cast()
                        }}
                    }
                };
                let ret = quote_spanned! { *span =>
                    #[allow(clippy::useless_conversion, reason = "#[classmethod] accepts anything which implements `From<&Bound<PyType>>`")]
                    ::core::convert::Into::into(

View on GitHub (pinned to ac9b6899d3)

Solutions

  1. Supply Some(cls) when generating a class method's self argument.
  2. Alternatively mark the conversion as Trusted only if you can guarantee the pointer is the right type (unsafe; avoid).
  3. Report to pyo3 if triggered by stock macros with valid code.

Example fix

// before
self_arg(None) // for a #[classmethod] with default (checked) conversion
// after
self_arg(Some(class_ident))
Defensive patterns

Strategy: validation

Validate before calling

// Class methods with Checked conversion need cls
if policy == Checked { assert!(cls.is_some(), "class method requires cls"); }

Type guard

fn classmethod_ok(cls: &Option<syn::Ident>, trusted: bool) -> bool {
    trusted || cls.is_some()
}

Prevention

When it happens

Trigger: Invoking the internal FnType::self_arg with cls = None for a class method whose SelfConversionPolicy is Checked (the default); i.e. class methods must always receive their class name.

Common situations: Hand-rolled macros or codegen tools reusing pyo3 method internals and omitting the class context; not reachable through normal #[pyclass] / #[classmethod] usage.

Related errors


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